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.
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.
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:
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.
[-] Kerberos SessionError: KDC_ERR_TGT_REVOKED(TGT has been revoked)
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
- Implementation of
- Add a method to generate a compliant
- Modification of the hardcoded
PrimaryGroupId. Now the first group of the list will be used as
PrimaryGroupId(with a default in 513)
- Dynamic number of PAC in
- Generalization of padding calculation methods (less mistake).
SIDstructure in Impacket.
- The default ticket structure now contains the new PACs:
- A new option,
old-pac, was added to forge tickets with the old structure. It will exclude the
- If you need to include the
UPN_DNS_INFOstructure in your ticket, you can use the
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
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
Not much more. Thanks @Dramelac for all your work!