Some time ago, Microsoft released a security patch that changed the way Kerberos tickets are created and validated. The update added new safeguards in the Kerberos Privileged Attribute Certificate (PAC) and improved the validations in the authentication process. Bye-bye golden tickets! Bye-bye golden tickets? š
Let’s see how to forge tickets in 2023 with Impacket.
š Merged PRs #1391, #1545, #1546.
Patching the world
Before we get started, let’s go over a few things to understand the introduced changes. I’ll be brief, I promise.
The vulnerabilities and the corresponding patch mentioned bellow are not new, so if you’re familiar with these things, you can go straight to what’s new.
In November 2021, Microsoft released the security patch KB5008380 – Authentication updates to address CVE-2021-42287 aka Kerberos Key Distribution Center (KDC) confusion. This is a security bypass vulnerability that affects the Kerberos PAC and together with CVE-2021ā42278 (sAMAccountName spoofing) allow potential attackers to impersonate domain controllers.Ā This attack is known as noPAC.
CVE-2021ā42278/sAMAccountName spoofing
A normal user is allowed to add up to 10 Active Directory (AD) computer accounts objects by default and renamed them. These accounts should have a trailing $ in their name, but there was no validation in place to enforce it. So, a regular user could create a computer account a rename it to a name that doesn’t end with $.
CVE-2021-42287/Kerberos KDC confusion
Whit a valid Ticket Granting Ticket (TGT), a user can request a service ticket to access an AD resource. If the user account of the ticket is not found by the KDC, it automatically searches again with a trailing $.
In a few words, the noPAC attack consists of chaining those two vulnerabilities. An attacker in possession of a normal user creates a computer account, then renames it to a Domain Controller’s name without the trailing $, and whit this account, requests a TGT. After that, the attacker removes the computer account and, using the previous TGT, requests a service ticket abusing S4U2self.
You can imagine how this continues. The KDC won’t find the user, so it’ll append a $ to the name (getting the Domain Controller’s name š§) and lookup again. As the result, a service ticket will be granted to the requesting user, making the regular user a domain admin š¤Æ.
No noPAC, no fun
Getting back to the security patch, Microsoft updated the PAC component adding two new data structures: PAC_ATTRIBUTES_INFO
and PAC_REQUESTOR
.
The Privilege Attribute Certificate (PAC) is a component used in the Kerberos authentication protocol. The PAC stores additional authorization information about a user and is attached to the Kerberos ticket, providing details about the user’s group memberships, privileges, and other security-related attributes.
TheĀ PAC_ATTRIBUTES_INFOĀ structure contains supplemental information about the PAC (Was it requested by the client or was it given implicitly?). On the other hand, theĀ PAC_REQUESTOR structure contains the SID of the client that requested the ticket.Ā Both new structures are required for a ticket to be accepted.
However, the most interesting part of the patch is the new validation introduced with the PAC_REQUESTOR structure. The KDC validates that the username (client name) of the ticket resolves to the same SID that is included in that structure.
The security update was released in phases. In October 2022, the enforcement phase was put in place. That means that any ticket without the new PAC structure (or for a non-existent user) will be denied.
So what happened with Impacket? When we used ticketer.py to forge a golden ticket and tried to use it with any of our examples, we got:
[-] Kerberos SessionError: KDC_ERR_TGT_REVOKED(TGT has been revoked)
What’s new
Following the Windows reinforcement policy, @Dramelac opened PR #1391 where implemented the new PAC structure in Impacket and ticketer.py to generate new golden tickets that will be accepted by fully patched servers!
The changes included the following additions:
- Implementation of
UPN_DNS_INFO_FULL
(when the S flag is set, the SamName and Sid is also populated). - Added a method to generate a compliant
UPN_DNS_INFO_FULL
in ticketer.py. - Implementation of
PAC_ATTRIBUTES_INFO
andPAC_REQUESTOR
. - Add a method to generate a compliant
PAC_ATTRIBUTES_INFO
andPAC_REQUESTOR
in ticketer.py. - Modification of the hardcoded
PrimaryGroupId
. Now the first group of the list will be used asPrimaryGroupId
(with a default in 513) - Dynamic number of PAC in
PACTYPE
. - Generalization of padding calculation methods (less mistake).
Additional changes were included with PRs #1545 and #1546:
- New
SID
structure in Impacket. - TheĀ defaultĀ ticket structure now contains the new PACs:
PAC_ATTRIBUTES_INFO
andPAC_REQUESTOR
. - A new option,Ā
old-pac
, was addedĀ to forge tickets with the old structure. It will exclude thePAC_ATTRIBUTES_INFO
andPAC_REQUESTOR
structures. - If you need to include the
UPN_DNS_INFO
structure in your ticket, you can use theĀextra-pac
option.
So, How do we forge a ticket?
It’s simple. After getting the AES key (or NTHash) of the krbtgt
account using secretsdump.py, and the domain SID (+ target user SID) using lookupsid.py, we can run ticketer.py with the following command:
ticketer.py -aesKey 7873... -domain-sid S-1-5-21-228... -domain contoso.com -user-id 1111 username
It’ll forge a ticket with the new PAC structures forĀ usernameĀ in the contoso domain. As I mentioned before, since KB5008380, the username must exist in the domain and have a matching RID. You have to set theĀ user-id
Ā option.
If you need to forge tickets with the old PAC structure, you can use the old-pac
option as follow:
ticketer.py -nthash f450... -domain-sid S-1-5-21-295... -domain contoso.com -old-pac username
Final notes
Not much more. Thanks @Dramelac for all your work!