This is a ticket-reading lab, not a Golden Ticket note. Target is a lab domain user on a joined VM. Goal: klist a TGT and a service ticket, write down authtime / start / end / renew-till, and name the checksum field I can see without decrypting. I do not mint tickets, I do not touch krbtgt hash, I do not pass /ptt.
1Figure 1. authtime is when AS succeeded. endtime is when the ticket dies. They are not the same field.
2AS-REP TGT authtime/start/end/renew-till
3TGS-REP HTTP end capped by TGT end; no 'initial' flag
Lab layout
1labs/krb_lab/
2 klist-tgt.txt
3 klist-http.txt
4 4768.json # event log export, SIDs redacted
Account: labuser@LAB.INTERNAL. DC and workstation clocks NTP-synced. If they were not, the 401 at the bottom is the artifact.
Artifact: klist (Windows)
1C:\lab> klist
2Current LogonId is 0:0x[REDACTED]
3
4Cached Tickets: (2)
5
6#0> Client: labuser @ LAB.INTERNAL
7 Server: krbtgt/LAB.INTERNAL @ LAB.INTERNAL
8 KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
9 Ticket Flags 0x40e10000 -> forwardable renewable initial pre_authent
10 Start Time: 3/14/2023 13:55:02 (local)
11 End Time: 3/14/2023 23:55:02 (local)
12 Renew Time: 3/21/2023 13:55:02 (local)
13 Session Key Type: AES-256-CTS-HMAC-SHA1-96
14 Cache Flags: 0x1 -> PRIMARY
15 Kdc Called: dc01.lab.internal
16
17#1> Client: labuser @ LAB.INTERNAL
18 Server: HTTP/web.lab.internal @ LAB.INTERNAL
19 KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
20 Ticket Flags 0x40a10000 -> forwardable renewable pre_authent
21 Start Time: 3/14/2023 13:56:11 (local)
22 End Time: 3/14/2023 23:55:02 (local)
23 Renew Time: 3/21/2023 13:55:02 (local)
24 Session Key Type: AES-256-CTS-HMAC-SHA1-96
25 Cache Flags: 0
26 Kdc Called: dc01.lab.internal
Field decode I write next to the dump:
| Field | TGT (#0) | HTTP (#1) | Meaning |
|---|---|---|---|
| Client | labuser @ LAB.INTERNAL | same | the principal the KDC authenticated |
| Server | krbtgt/LAB.INTERNAL | HTTP/web.lab.internal | SPN |
| Enc type | AES-256-CTS-HMAC-SHA1-96 | same | etype; RC4 here would be a finding |
| Flags | initial, pre_authent, renewable, forwardable | no initial | initial ⇒ this came from AS, not TGS |
| Start | 13:55:02 | 13:56:11 | when the ticket becomes valid |
| End | 23:55:02 | 23:55:02 | 10h TGT lifetime; service ticket capped by TGT end |
| Renew | 3/21 13:55:02 | same | 7-day renew-till in this domain |
| Kdc Called | dc01.lab.internal | same | which DC; compare to 4768 |
End on #1 equals End on #0. That is normal: a TGS ticket cannot outlive the TGT that requested it. A service ticket with end far beyond the TGT end, or a TGT with end far beyond domain policy, is the forgery-class anomaly — I file it as “times do not match policy”, I do not “fix” it by forging a better one.
Linux klist (same user, SSSD lab):
1$ klist
2Ticket cache: FILE:/tmp/krb5cc_[REDACTED]
3Default principal: labuser@LAB.INTERNAL
4
5Valid starting Expires Service principal
603/14/23 13:55:02 03/14/23 23:55:02 krbtgt/LAB.INTERNAL@LAB.INTERNAL
7 renew until 03/21/23 13:55:02
803/14/23 13:56:11 03/14/23 23:55:02 HTTP/web.lab.internal@LAB.INTERNAL
Same numbers, fewer flags. I still want the Windows dump when the question is PAC / etype.
Checksums, without decrypting a ticket
A Kerberos ticket is an encrypted blob plus some cleartext (realm, sname). Inside the enc-part: session key, times, flags, transited, authz-data (PAC on AD). The PAC carries its own checksums (ServerChecksum, KDCChecksum in Microsoft’s structure). From userland I do not dump those bytes. What I can see:
1# Wireshark, lab capture, AS-REP
2kerberos.CNameString == labuser
3kerberos.realm == LAB.INTERNAL
4kerberos.ticket.sname == krbtgt
5kerberos.etype == 18 (aes256-cts-hmac-sha1-96)
6kerberos.checksum.type == 16 (hmac-sha1-96-aes256) # authenticator cksum
7kerberos.authtime == 2023-03-14 05:55:02 UTC
8kerberos.endtime == 2023-03-14 15:55:02 UTC
kerberos.checksum.type on the authenticator is not the PAC KDC checksum. Do not mix them in the timeline. Authenticator checksum proves the client knew the session key for this AP-REQ. PAC KDC checksum is a DC-side integrity field; userland klist will not print it.
Event 4768 (AS success), redacted:
1Account Name: labuser
2Supplied Realm Name: LAB.INTERNAL
3Service Name: krbtgt
4Service ID: S-1-5-21-[REDACTED]-502
5Ticket Options: 0x40810010
6Ticket Encryption Type: 0x12 # AES256
7Pre-Authentication Type: 2 # encrypted timestamp
8Client Address: 127.0.0.1
9Certificate Issuer Name: -
Match: klist Kdc Called dc01, etype AES256, client labuser, time 13:55:02 local = 05:55:02 UTC. That is a reconstructed AS. A 4768 with etype 0x17 (RC4) for this user is a separate ticket.
Analysis steps for a suspicious ticket
- Compare
klistEnd/Renew to domain policy (Maximum lifetime for user ticket= 10 hours here,Maximum lifetime for user ticket renewal= 7 days). initialflag on a ticket whose Server is notkrbtgt/...is odd.- Etype RC4 (
0x17) when the account has AES keys: hunt for constrained RC4 or an old client, not immediately “forged”, but file it. authtimein the future or far in the past vs DC clock: clock-skew or crafted times.Kdc Calledempty / not a DC you own: local cache import. I still do not run/pttto confirm.
Sanitized reproduction (clock skew / reject only)
I moved the workstation clock +10 hours once. AP-REQ to the lab HTTP SPN:
1C:\lab> curl -u : --negotiate http://web.lab.internal/who
2curl: (6) / HTTP/1.1 401 Unauthorized
3WWW-Authenticate: Negotiate
4
5# DC 4769 failure (excerpt)
6Result Code: 0x25 # KRB_AP_ERR_SKEW
7Account Name: labuser
8Service Name: HTTP/web.lab.internal
9Client Address: 10.[REDACTED]
0x25 skew is the crash analog. I set the clock back. I do not “fix” skew by minting a ticket with adjusted authtime.
Failed preauth (wrong password) is 4771, not a ticket:
1Event 4771 Kerberos pre-authentication failed
2Account Name: labuser
3Pre-Auth Type: 2
4Failure Code: 0x18 # KDC_ERR_PREAUTH_FAILED
5Client Address: 127.0.0.1
No TGT in klist after that. Do not confuse 4771 with a checksum anomaly.
PAC times vs logon times
Event 4624 for the same logon (type 3 or 2) carries Logon Time that should sit within a few seconds of klist Start on the TGT. A 4624 at 13:55:02 and a TGT Start at 10:00:00 the previous day is a cache that survived a sleep, or a ticket that did not come from this logon. I record both clocks (UTC) before I say “forged”.
1# 4624 excerpt, same session
2Logon Type: 2
3Authentication Package: Kerberos
4Logon Time: 2023-03-14T13:55:02.110
5Source Network Address: 127.0.0.1
Authentication Package: Kerberos plus a TGT in klist is consistent. NTLM plus a TGT from a different logon id (klist -li) is two stories; do not merge them.
Mitigation / policy I tick
- AES only:
msDS-SupportedEncryptionTypeson accounts, disable RC4 domain-wide when the audit is clean. - Ticket lifetimes: 10h / 7d is common; shorter for high-value. Log 4768 etype + options.
- Clock: NTP on DCs and members.
0x25should be rare. - Do not leave KRBTGT password unrotated; rotation is an ops note, not a forge recipe. I record last set time from a privileged query I do not paste.
- Detection: TGT end > policy; service ticket with
initial; etype 0x17; 4768 from a workstation that has no corresponding logon 4624.
What I file after this lab
klist: TGT AES256, flags initial|pre_authent|renewable|forwardable, start 13:55:02, end +10h, renew +7d- HTTP ticket: no
initial, end equal to TGT end - Wireshark:
kerberos.etype==18,checksum.type==16on authenticator, times UTC - 4768 etype 0x12, preauth 2, address 127.0.0.1
- Skew repro: 401 + 4769
0x25; bad password: 47710x18 - Out of scope: forging PAC checksums,
krbtgthash,/ptt
Commands appendix
1klist
2klist -li 0:0x[REDACTED]
3# Linux: klist; kvno HTTP/web.lab.internal
4wevtutil qe Security /q:"*[System[(EventID=4768)]]" /c:1 /f:text