Prancer Blog / GOAD Challenge: Swarmhack AD pentesting

Anonymous Bind to Domain Admin — One Command Against GOAD

One swarmhack spawn against GOAD — anonymous LDAP bind, kerberoasting, ADCS ESC1 certificate enrollment, Domain Admin in 9–10 minutes. No credentials supplied.

SwarmHack Team · 2026-05-12 · 12 min

TL;DR

  • One swarmhack spawn --target 192.168.56.10 runs the entire AD kill chain — no operator credentials supplied, no --agents filter
  • 22 native AD agents orchestrated by a GOAP planner detect AD services automatically and route to LDAP / Kerberos / SMB / ADCS
  • Validated numbers across four GOAD variants — GOAD-Mini 15/11 in 10m 10s, GOAD-Light 15/11 in 9m 9s, including autonomous ESC1 certificate enrollment as [email protected]

This is Part 2 of 3. In Part 1 we built a real Active Directory lab using GOAD. Now we attack it.

Series:
- Part 1 — Build the GOAD lab
- Part 2 — Run the engagement (you are here)
- Part 3 — Under the hood

1. The command

swarmhack spawn \
  --target 192.168.56.10 \
  --token $PRANCER_TOKEN \
  --customer $PRANCER_CUSTOMER \
  --exec-mode kill-chain

That's it. Note what's not in that command:

  • No --agents filter. The orchestrator detects LDAP (389), Kerberos (88), SMB (445), Global Catalog (3268), and ADCS Web Enrollment (80/443) on first port sweep and routes to the AD agent suite automatically.
  • No --username / --password. The engagement starts with zero credentials. vagrant:vagrant gets discovered by the SMB enumerator and bridged into the AD context by the cross-agent intelligence layer.
  • No --allow-write-operations. Default is read-only. Destructive operations (DACL grants, owner changes, forced password resets, Shadow Credentials, RBCD) require explicit opt-in and back up the original DACL before writing.

2. The validated numbers

Real GOAD labs, 2026-05-09 hands-on re-validation, single command per row:

| Lab | Domain | Findings | Crown Jewels | Time | Validator |

| ----- | -------- | ---------- | -------------- | ------ | ----------- |

| Synthetic LDAP fixture | corp.local | 36 | 30 | 5m 48s | — |

| GOAD-Mini | sevenkingdoms.local | 15 | 11 | 10m 10s | PASS — variant=goad-mini matched=2/2 |

| MINILAB | mini.lab | 14 | 10 | — | PASS — variant=minilab matched=2/2 |

| GOAD-Light | sevenkingdoms.local + essos.local | 15 | 11 | 9m 9s | PASS — variant=goad-light matched=3/3 |

| DRACARYS | dracarys.lab | 8 | 3 | 7m 44s | PASS — variant=dracarys matched=2/2 |

Every lab captured vagrant:vagrant over SMB. Every lab surfaced anonymous LDAP bind. Every lab walked the seven Domain-Admins / Enterprise-Admins WriteDACL paths to the Domain Controllers container. Mini and Light autonomously enrolled an ESC1 certificate for [email protected] against SEVENKINGDOMS-CA and saved it as administrator.pfx.

3. The kill chain — phase by phase

The GOAP planner sequences agents by world-state preconditions. Here's what fires when:

<diagram title="AD kill chain — output of phase N becomes input of phase N+1">

PHASE 1 (T+0..T+45s)        PHASE 2 (T+45..T+120s)      PHASE 3 (T+120..T+180s)
Discovery                   LDAP enumeration            SMB sweep
─────────                   ──────────────              ─────────
nmap top-1k + AD ports      Anonymous bind → rootDSE    smb_enum default-cred
LDAP/Kerberos/SMB/ADCS      149 user accounts           vagrant:vagrant ✓
detected, GOAP routes       Kerberoastable SPNs         6 shares enumerated
to AD agent suite           AS-REP-roastable users      → bridge to AD ctx
                            adminCount=1 principals
                            nTSecurityDescriptor blobs

       │                            │                          │
       ▼                            ▼                          ▼
PHASE 4 (T+180..T+240s)     PHASE 5 (T+240..T+300s)     PHASE 6 (T+300..T+340s)
Kerberos roasting           ACL graph walk              ADCS ESC1
─────────────────           ──────────────              ─────────
TGS hashes (-m 13100)       Native nTSecurityDescriptor adcs_exploit detects
AS-REP hashes (-m 18200)    parser walks 7 ACE edges    EnrolleeSuppliesSubject
queued to hash_crack         finance → erin (DA)         + low enrollment ACL
                            shadow_creds primed for     → autonomous enrollment
                            DA-tier compromise          → administrator.pfx ✓
                                                         │
                                                         ▼
                                                  PHASE 7 (T+340..T+360s)
                                                  LAPS / gMSA / delegation
                                                  ─────────────────────────
                                                  ms-Mcs-AdmPwd cleartext
                                                  msDS-ManagedPassword blob
                                                  TRUSTED_FOR_DELEGATION UAC

</diagram>

Each phase produces intelligence the next phase consumes — automatically, through a shared-memory bus, with no human gluing things together.

4. Phase 1 — Service discovery (T+0 → T+45s)

A focused port sweep against the target. AD has a tell-tale fingerprint: 389/636 (LDAP), 88 (Kerberos), 445 (SMB), 3268 (GC), 80/443 (ADCS Web Enrollment). When that pattern matches, the GOAP planner routes to the 22-agent AD suite instead of (or alongside) the web suite.

No human says "this is an AD target." The agents agree among themselves based on what they see.

5. Phase 2 — LDAP enumeration via anonymous bind

ldap_enum opens a TCP socket to 389, sends an LDAP bindRequest with empty DN and empty password, and gets a bindResponse: success. That alone is a medium-severity finding in any modern AD audit.

Then it pivots into discovery, using extensible-match LDAP filters that hard-code the canonical Microsoft attribute OIDs:

| Query | Filter | Returns |

| ------- | -------- | --------- |

| Kerberoastable SPNs | (servicePrincipalName=*) (excluding krbtgt) | svc_sql, svc_web, svc_iis, svc_backup, … |

| AS-REP-roastable users | (userAccountControl:1.2.840.113556.1.4.803:=4194304) | every principal with DONT_REQ_PREAUTH |

| DA-tier principals | (adminCount=1) | erin, administrator, … |

| Computers | (objectClass=computer) | WS01$, WS02$, FILE01$, DC01$ |

| Trusts | (objectClass=trustedDomain) | parent/child / cross-forest relationships |

| LAPS-enabled hosts | (ms-Mcs-AdmPwdExpirationTime=*) | every host with LAPS deployed |

| Description-field secrets | (description=*) | the classic password-in-description leak |

Critically, every principal's nTSecurityDescriptor binary blob is pulled and parked in shared memory. That's the raw material the ACL graph walker consumes in Phase 5.

6. Phase 3 — SMB credential sweep

smb_enum runs a default-credential sweep using data/wordlists/smb.yaml. Every GOAD VM ships with the Vagrant baseline vagrant:vagrant account. The sweep lands on the first try.

That match is enriched, not just logged:

  • The credential gets cross-published into the AD namespace (creds_ad_*) so cred-consuming agents (DCSync, WinRM, MSSQL, ADCS RPC) can use it
  • All accessible SMB shares are enumerated (typically ADMIN$, C$, IPC$, NETLOGON, SYSVOL, plus any GOAD-specific shares)
  • SYSVOL gets a sweep for GPP cpassword XML files — the classic legacy AD vulnerability that still shows up in real audits
For DRACARYS (the post-install vagrant-disabled variant), the --no-vagrant-default flag suppresses the baseline credential from the sweep so the run reflects a hardened baseline.

7. Phase 4 — Kerberoast + AS-REP-roast

kerberos_attack consumes the SPN list from Phase 2 and the AD credential from Phase 3.

Kerberoast issues a TGS-REQ for each kerberoastable SPN, captures the encrypted portion of the response (encrypted under the service-account's long-term key), and formats it as hashcat mode -m 13100:

$krb5tgs$23$*alice$CORP.LOCAL$HTTP/dc01.corp.local*
lt;checksum_hex>
lt;encrypted_hex>

AS-REP roast issues an AS-REQ *without* PA-ENC-TIMESTAMP padata for every DONT_REQ_PREAUTH principal. The KDC's response exposes the encrypted timestamp section under the user's long-term key — formatted as hashcat mode -m 18200:

[email protected]:<checksum_hex>
lt;encrypted_hex>

Both hash sets are queued straight into the hash_crack agent's shared-memory queue. No copy-paste, no manual --format=krb5tgs flag. Cracked plaintexts come back into the AD credential namespace and can rotate into the next agent's authentication.

There's a deeper primitive here too: targeted Kerberoast. When the chain has LDAP write rights against a principal, the agent writes a temporary SPN onto that principal, issues a TGS-REQ against the temp SPN, captures the hash, removes the SPN, and feeds the cracked plaintext back into the credential pool. It's the native equivalent of targetedKerberoast.py.

8. Phase 5 — The ACL graph walk

This is the part wrapper-based stacks usually outsource to BloodHound + a human staring at a graph. SwarmHack does it natively.

The ACL agent walks every nTSecurityDescriptor blob captured in Phase 2, parsing it per MS-DTYP §2.4.6. Every ACE that grants a non-trivial access mask becomes an edge in an in-memory directed graph of principals.

A shortest-path search with weighted edges (ForceChangePassword and GenericAll cheapest, WriteOwner most expensive) finds the most efficient route to a DA-tier principal.

The synthetic fixture's seven-step chain looks like this:

alice  ─[ForceChangePassword]→  bob
bob    ─[GenericWrite]→         carol
carol  ─[WriteDACL]→            dave
dave   ─[Self/WriteSelf]→       engineering OU
engineering ─[WriteSelf]→       operations OU
operations  ─[WriteOwner]→      finance OU
finance ─[GenericAll]→          erin (adminCount=1, DA-tier) ✓

On the real GOAD variants, the corresponding chains terminate at the Domain Controllers container via Domain Admins / Enterprise Admins WriteDACL ACEs — the canonical seven paths every red-team report ends with.

Every edge in the chain becomes a structured crown jewel with source SID, target DN, access mask bitmap, and exploitation downstream pointer. Exactly what a SOC ticket needs.

9. Phase 6 — ADCS ESC1: autonomous certificate enrollment

adcs_exploit walks the certificate templates published in CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=… and inspects each for the ESC1 fingerprint:

  • pKIExtendedKeyUsage includes Client Authentication
  • msPKI-Certificate-Name-Flag has the ENROLLEE_SUPPLIES_SUBJECT bit
  • The discretionary ACL grants enrollment rights to a low-privilege principal

When all three line up, the agent calls the ADCS web-enrollment endpoint (/certsrv/certfnsh.asp) — or, on hardened deployments, the MS-WCCE RPC opnum 0 via the native DCERPC stack — with a CSR whose subject alternative name says [email protected].

The CA happily issues the certificate. SwarmHack saves it as administrator.pfx and emits a critical finding with the certificate fingerprint as the crown jewel.

That's a paper-clean DA-tier impersonation primitive, autonomously, without an operator at the keyboard.

10. Phase 7 — LAPS, gMSA, delegation, coercion

These run in parallel because they don't depend on each other:

  • laps_reader queries each computer object for ms-Mcs-AdmPwd and emits cleartext local admin passwords — three of them on the synthetic fixture (LAPS!Workstation01@2026, LAPS!Workstation02#2026, LAPS!FileServer01$2026).
  • gmsa_reader reads msDS-ManagedPassword from gMSA accounts and decodes the MSDS-MANAGEDPASSWORD_BLOB structure (Version, Reserved, Length, CurrentPassword, PreviousPassword, intervals).
  • delegation_exploit scans userAccountControl for the TRUSTED_FOR_DELEGATION bit (0x80000) — DC01$ lights up.
  • auth_coercion runs MS-RPRN PrinterBug (opnum 65), MS-EFSR PetitPotam (opnum 0), MS-DFSNM DFSCoerce (opnum 13) when DCERPC is reachable.
  • gpo_abuse enumerates groupPolicyContainer objects.
  • trust_exploit reads trustedDomain records and emits trust-relationship findings (parent/child for Light, partner forest for the synthetic fixture).

11. The OCSF report

When the orchestrator finishes, you get a single OCSF 1.1.0 JSON report at reports/mission-<timestamp>-<uuid>.json. Each finding includes:

  • cwe.uid — e.g. CWE-284 for the ACL findings, CWE-522 for cleartext LAPS
  • attacks — MITRE ATT&CK technique IDs (T1098, T1208, T1558.003, T1003.006, …)
  • severity_id, risk_score, confidence
  • crown_jewels[] — actual extracted impact, with structured context blobs

Sample (the capstone ACL finding):

{
  "title": "AD ACL abuse: finance → erin via GenericAll",
  "severity_id": 6,
  "cwe": { "uid": "CWE-284" },
  "attacks": ["T1098"],
  "evidence": "Native nTSecurityDescriptor walk: finance --[GenericAll]--> erin (DA-tier; adminCount=1).",
  "crown_jewels": [{
    "category": "acl_attack_path",
    "value": "finance --[GenericAll]--> erin",
    "context": {
      "agent": "acl_abuse",
      "ace_kind": "ACCESS_ALLOWED_ACE",
      "access_mask": "0x10000000",
      "trustee_sid": "S-1-5-21-1234567890-2345678901-3456789012-1107",
      "target_dn": "cn=erin,dc=corp,dc=local",
      "da_tier": true,
      "exploitation": "Path identified; downstream shadow_creds primitive applied"
    }
  }]
}

That's a SOC-ready crown jewel: source SID, target DN, access-mask bitmap, downstream chain pointer. No human transcription. No CSV wrangling.

12. Run it yourself

If you finished Part 1, you're one command away:

# Make sure GOAD is up
cd ~/GOAD && ./goad.sh
goad> set_lab GOAD-Mini && set_provider virtualbox && status

# Fire SwarmHack — read-only by default
swarmhack spawn \
  --target 192.168.56.10 \
  --token $PRANCER_TOKEN \
  --customer $PRANCER_CUSTOMER \
  --exec-mode kill-chain

# Output
ls -la reports/mission-*.json

Expected on GOAD-Mini:

  • ~10 minutes wall-clock
  • 15 findings, 11 crown jewels
  • vagrant:vagrant captured over SMB
  • Anonymous LDAP bind flagged
  • Kerberoastable + AS-REP-roastable hashes queued
  • The seven Domain-Admins / Enterprise-Admins WriteDACL paths walked
  • administrator.pfx sitting in your output directory, ready for a PKINIT TGT

If you turn on --allow-write-operations, the destructive primitives become available — with automatic backup of every original nTSecurityDescriptor blob before modification, so post-engagement cleanup can restore the original DACL exactly.

What's next

In Part 3 — Under the Hood we'll open up the engine: the 22 native AD agents, the GOAP planner that sequences them, and the native Rust protocol stack — LDAP, Kerberos, DCERPC (PrinterBug, PetitPotam, DFSCoerce, DCSync, ADCS-RPC, LSARPC), NTLMv2, and BloodHound — that replaces impacket, certipy, BloodHound.py, pywhisker, and Responder with one compact binary and a sub-second cold start.

See you in Part 3.