Kerberos in Active Directory
For this first “real” article, the topic is how Kerberos works: one of the authentication protocols at the heart of Active Directory. Understanding it means understanding a good share of the attacks against it (AS-REP roasting, Kerberoasting, Golden and Silver Tickets, etc.). The goal of this article is mostly to explain:
- how a user proves their identity without sending their password;
- why copying a ticket is not enough to use it (and so where an attacker will look for it);
- who encrypts what, with which key;
- what an attacker looks at during each step.
This article owes a lot to the one written by Pixis on hackndo (in French), which taught me many Active Directory basics when I started out in pentesting. I can only recommend reading it and making the most of it, as I did a few years ago.
The animations are meant to stand on their own. Play them one after another and skip the text: it is there to go into detail if you want to understand everything from scratch.
Now, let’s get into it!
The problem: opening a share without handing over your password
Alice, an accountant at the company, wants to open the \\FS01\accounting share from her workstation. The server needs to know who it is dealing with, under two constraints:
- Alice’s password must never cross the network, not even encrypted;
- FS01 must not have to know the password of every user in the domain.
Kerberos solves this with a trusted third party, the KDC (Key Distribution Center). In an Active Directory domain, the domain controller plays this role. The journey has three stages:
- Alice gets recognised by the KDC, only once, and receives a credential;
- with this credential, she asks for a ticket for the service she is interested in;
- she shows this ticket to the service.
These three exchanges have names you will find everywhere (in Wireshark, in tools, in logs): AS (Authentication Service), TGS (Ticket-Granting Service) and AP (Application). The credential obtained in the first exchange is called the TGT (Ticket-Granting Ticket).
Each animation plays step by step, with the ◀ ▶ buttons or the arrow keys. A box with a coloured border is encrypted with the key of the same colour: as long as it is hatched, its content is unreadable, and it only opens for an actor who holds that key.
A picture to make it stick: a large hotel, with a swimming pool, a gym, a spa and a restaurant.
- Reception: proving your identity. On arrival, Alice shows her ID card. The receptionist checks that she is indeed a guest of the hotel and gives her a resident card, proving she has been identified. That is the AS exchange, and the resident card is the TGT.
- The concierge: asking for access. Alice wants to go to the spa. She shows her resident card to the concierge, who checks it and gives her a pass valid for the spa, and for the spa only. That is the TGS exchange, and the pass is the service ticket.
- The spa: using the access. At the spa entrance, the attendant checks that the pass is valid and that it is indeed for the spa, then lets Alice in, without calling the concierge. That is the AP exchange.
- And the pool? No need to go back to reception with her ID card: Alice returns to the concierge with her resident card and gets a new pass, for the pool this time.
Reception and the concierge are two desks of the same actor, the KDC: it hands out both TGTs and service tickets.
The picture has a limit: a card or a pass can be stolen. In Kerberos, showing a ticket is not enough, you also have to prove you are its legitimate owner. That is the whole point of the section “Why copying a ticket is not enough”.
Who knows which key
Kerberos relies entirely on shared secret keys: it is symmetric encryption, the same key encrypts and decrypts. Every account in the domain, user or machine, has a key derived from its password, and only knows its own key. The KDC knows them all, plus one that belongs to it alone: the key of the special krbtgt account.
Everything else follows from this: the KDC can write anyone a message that only that recipient will be able to read, and a recipient who manages to open a message encrypted with their key knows it was written by the KDC (or by themselves).
Only one key per account, really?
To keep things simple, the animations show one key per account. In reality, an account has several, one per encryption algorithm: an RC4 key, which is nothing other than the NT hash of the password, and AES keys, derived from the password with a salt (made of the domain and account names). The client and the KDC agree on the algorithm at each exchange.
The krbtgt key is the most valuable secret in the domain: with it, you can forge a TGT yourself, in anyone’s name, with any groups you like. That is the Golden Ticket. Since this key never leaves the domain controllers, you first have to compromise them to get it: the Golden Ticket is a persistence technique, not an intrusion technique.
Step 1 (AS): getting a TGT
Let’s follow Alice: she has just typed her password at logon. Her workstation has derived her key from it, and will not use the password itself any more.
KRB_AS_REQ: proving you know your secret
Alice asks the KDC for a TGT. Her request contains her name (in clear text) and the current time (encrypted with her own key). This encrypted time is the pre-authentication: only someone who knows Alice’s key could have produced it.
On receiving this message, the KDC fetches Alice’s key from its database and decrypts the timestamp. If it gets a time close to its own (5 minutes of skew allowed by default), the request really comes from someone who knows Alice’s secret. Otherwise, it is rejected (one of the reasons why clocks in a domain must stay synchronised).
KRB_AS_REP: two parts for two readers
Next, the KDC randomly generates a session key, valid for the lifetime of the TGT, then replies with two parts:
- a part for Alice, encrypted with Alice’s key, containing the session key;
- the TGT, encrypted with the
krbtgtkey. It contains Alice’s name, the TGT’s validity period, the same session key and the PAC, which describes Alice’s groups (we will open it in step 3).
At this point, you might wonder: why two parts? Because there are two readers. The first one is for Alice, right now. The TGT is for the KDC itself, later: it is a note the KDC writes to itself and hands to the client. Alice cannot read it, she keeps it as is to show it with her next request. Thanks to it, the KDC does not need to remember Alice between two exchanges.
Alice’s part is encrypted with a key derived from her password. If an account has pre-authentication disabled (the Do not require Kerberos preauthentication option), anyone can request an AS-REP in its name, without proving anything, then try passwords offline against this encrypted part: that is AS-REP roasting. The same principle applies to someone who captures an AS-REQ on the network: the encrypted timestamp lends itself to the same guessing.
Why copying a ticket is not enough
The TGT travels over the network. An attacker sniffing the traffic can therefore grab a copy. What can they do with it?
Nothing, thanks to the session key. Every time she shows a ticket, Alice attaches an authenticator: her name and the time, encrypted with the session key. The ticket says “this is Alice”; the authenticator adds “and I am the one showing it, here is the proof: I know the key that goes with it”.
Yet the session key has only ever travelled inside closed boxes: under Alice’s key in the AS-REP, and under the krbtgt key in the TGT. The attacker who copied the traffic can therefore neither forge a fresh authenticator, nor read the KDC’s replies, which are also encrypted with that key.
Remember this pair: a ticket is useless without its session key. The next two exchanges apply exactly this principle.
On the network, the session key is protected. On the workstation, however, the Kerberos cache keeps each ticket with its session key, so that the system can use them. An attacker with administrator rights on the workstation can extract both from memory (from the LSASS process) and inject them on their own machine: that is pass-the-ticket.
Step 2 (TGS): asking for a ticket for a service
KRB_TGS_REQ: showing the TGT
Alice now wants to use the file sharing service of FS01, called CIFS. She sends the KDC:
- her TGT, as is;
- the name of the target service, its SPN (Service Principal Name):
CIFS/FS01; - an authenticator, encrypted with the session key.
The KDC opens the TGT with the krbtgt key, reads Alice’s identity and the session key, then decrypts the authenticator with that key. If both match and the time is recent, the request really comes from the legitimate holder of the TGT. Alice’s key is never involved: the TGT takes its place for its whole validity period (10 hours by default).
KRB_TGS_REP: a ticket for FS01
The KDC then looks up which account holds the SPN CIFS/FS01: here, the machine account FS01$. Following the same pattern as the AS-REP, it prepares:
- a service ticket meant for FS01: service name, Alice’s identity, PAC and a new session key, all encrypted with the key of
FS01$; - a part for Alice, encrypted with the first session key, containing the new one.
The AS and the TGS have the same shape: the KDC returns a ticket the client cannot read, plus a session key only the client can read. The only thing that changes is the key protecting the client’s part: their own key for the AS, the session key for the TGS.
The service ticket is encrypted with the key of the account holding the SPN, and any user in the domain can request a ticket for any SPN. For a machine account, there is nothing to gain: its password is random. But if the SPN belongs to a service account whose password was chosen by a human (svc_sql, for example), you can request the ticket and then try passwords offline against it: that is Kerberoasting.
Step 3 (AP): being recognised, then authorised
KRB_AP_REQ: showing the ticket to the service
Alice sends FS01 the service ticket and a new authenticator, encrypted with the second session key. FS01 opens the ticket with its own key, without calling the KDC: besides FS01, only the KDC knows this key, so only the KDC could have made this ticket. FS01 finds the session key inside, decrypts the authenticator and checks: same name, recent time, and an authenticator it has not already received (it keeps those from the last few minutes in memory, so that a copied request cannot be replayed). Alice is authenticated.
The PAC: who Alice is, for Windows
Kerberos says who Alice is, not what she is allowed to do. For that, Windows adds the PAC (Privilege Attribute Certificate) to the ticket: Alice’s SID, the SIDs of her groups, and signatures that prevent them from being modified. The KDC filled it in when the TGT was created, then copied it into the service ticket.
FS01 compares these SIDs with the permissions of the share and of the files (the ACLs), and decides. The PAC grants no rights by itself: a perfectly valid ticket only opens what the ACLs allow. And since groups are frozen when the TGT is created, a user added to a group must get a new TGT (in practice, log on again) for it to count.
Finally, if the client asks for mutual authentication, the server proves its own identity in turn, by sending back a KRB_AP_REP encrypted with the session key: only the real FS01 could have opened the ticket and taken this key out of it.
FS01 trusts any ticket that opens with its key. An attacker who has stolen the key of FS01$ can therefore forge a ticket for this service themselves, in anyone’s name and with any PAC they like, without ever talking to the KDC: that is the Silver Ticket. The KDC signature in the PAC cannot be forged without the krbtgt key, but the server does not always have it checked.
Seeing it on a real workstation
The whole journey, in one go: Alice logs on, then opens the share on FS01. The TGT obtained at the start stays in the cache: as long as it is valid, it is enough, together with its session key, to request other tickets without going back to the password.
The ticket cache with klist
On a domain workstation, klist shows the Kerberos cache of the session. The experiment is simple: empty the cache, access the share, then look at what came back.
klist purgedir \\FS01\accountingklistTwo tickets should show up. The TGT, whose server is krbtgt/LAB.LOCAL, and the service ticket, for cifs/FS01.lab.local. Each one shows its start and end times, and its encryption type: AES-256-CTS-HMAC-SHA1-96 on a recent domain, RSADSI RC4-HMAC if RC4 is still in use. Windows requested a new TGT on its own, without asking for the password again: it keeps Alice’s key in memory for the duration of the session.
dir \\192.168.1.20\accounting would not use Kerberos: a ticket is requested for a service name (the SPN), and by default there is none for an IP address. Windows then falls back to NTLM.
Key takeaways
| Item | Encrypted with | Readable by |
|---|---|---|
| Pre-authentication timestamp (AS-REQ) | Alice’s key | Alice, KDC |
| Client part of the AS-REP (session key 1) | Alice’s key | Alice, KDC |
| TGT | krbtgt key | KDC only |
| TGS-REQ authenticator | session key 1 | Alice, KDC |
| Client part of the TGS-REP (session key 2) | session key 1 | Alice, KDC |
| Service ticket | FS01$ key | FS01, KDC |
| AP-REQ authenticator, AP-REP | session key 2 | Alice, FS01 |
The password never travels: it is used once, on the workstation, to derive a key. After that:
- each message can only be read by those who are meant to read it;
- each ticket proves it was made by a holder of the right key;
- no ticket is any use without its session key.
That’s all for this article, which aimed to present the basics of Kerberos (and mention some of the related attacks). I hope the content was not too heavy going and that the animations made it easier to understand.