Inside Kerberos – 5: Tokens
Alright then, we’ve covered SIDs, SIDHistory, and Tickets. Let’s move onto Access Tokens.
Privilege Access Certificate
The Privilege Access Certificate or PAC is a Microsoft extension to Kerberos utilizing the Authorization Data field in the tickets. This is sometimes referred to as the Access Token, however strictly speaking the Access Token is the structure generated by the LSA on the destination server. Whilst that uses data from the PAC, it is not the same.
The PAC is limited in size to 64KB maximum, although modern OSes default this via MaxTokenSize to 48KB, to allow for Base64 encoding in IIS.
The PAC is broken down into many structures, which contain a whole swathe of information about the user who’s trying to make the request. The main structures are:
- Logon Information: Contains information about the user
- Credentials Information: Contains information about the credentials whenever non-password methods are used for authentication (e.g. public/private key methods)
- Server Checksum: A checksum of the entire PAC (with both checksum fields zeroed out) encrypted using the server/service secret key
- KDC Checksum: A checksum of the entire PAC (with both checksum fields zeroed out) encrypted using the krbtgt secret key
- Client Name and Ticket Info: The user’s name
- Constrained Delegation Info: Information relevant to constrained delegation (if used)
- Client Claims Info: Claims information (if used)
- Device Info: Information about the device that the user is on
- Device Claims Info: Claims information for the device at the user is on (if used)
- UPN DNS Info: The User Principal Name and FQDN of the account that this ticket is for
Out of those the Logon Information has all of the group information and much more. Check out the fields within a Logon Information Structure:
- LogonTime
- LogoffTime
- KickOffTime
- PasswordLastSet
- PasswordCanChange
- PasswordMustChange
- EffectiveName
- FullName
- LogonScript
- ProfilePath
- HomeDirectory
- HomeDirectoryDrive
- LogonCount
- BadPasswordCount
- UserId
- PrimaryGroupId
- GroupCount
- GroupIds
- UserFlags
- UserSessionKey
- LogonServer
- LogonDomainName
- LogonDomainId
- UserAccountControl
- SubAuthStatus
- LastSuccessfulILogon
- LastFailedILogon
- FailedILogonCount
- SidCount
- ExtraSids
- ResourceGroupDomainSid
- ResourceGroupCount
- ResourceGroupIds
Some interesting things about the PAC:
- When generated as part of the TGT, it only includes the Global groups and Universal groups that the user belongs to.
- When generated as part of the ST, it also gets the Domain Local groups. However these Domain Local groups are from the domain that the requested server/service is in
- Global Groups as well as Universal groups belonging to the user’s domain are compressed by writing the domain sid into LogonDomainId, and then putting the RID instead of the full SID into GroupIds (Note that every entry in GroupIds has the RID plus an attributes field, making 8 bytes per entry)
- The UserId and PrimaryGroupID are also RIDs rather than SIDs, and are combined with LogonDomainId to create the full SID
- For KDCs running Windows Server 2012 R2 or later, Domain Local group compression is enabled by default (can be turned off per service account or domain wide using msDS-SupportedEncryptionTypes if bit 0x80000 is set) If enabled then it writes the server/service’s domain sid into ResourceGroupDomainSid, and places the RIDs and an attributes field into ResourceGroupIds, again for 8 bytes per entry)
- ExtraSids is used for any group SIDs that aren’t already saved, including sid-history SIDs. Each entry has an attributes field (4 bytes) plus a pointer (8 bytes) to the full SID (28 bytes) for a total of 40 bytes per entry
- There is nothing in the PAC that causes the 1024 group limit in an Access Token, in fact even the 64KB token size limit looks to be self inflicted as the structure has no inherent limitation around 64KB (It tops out at 4MB for the Logon Information section which is the one that holds the SIDs)
Access Token
As we said, the Access Token is generated by the LSA on the machine that hosts the service you’re accessing. The LSA uses the information from inside the PAC to help with this process.
I can’t actually find a definitive answer on exactly what the Access Token structure looks like, but we can see that GetTokenInformation is passed a value from the TOKEN_INFORMATION enumeration, which includes a lot of structures, of which the more interesting ones are:
TokenUser
The user’s SID and an attributes field
TokenGroups
The number of groups and the list of SIDs plus their respective attributes field
TokenPrivileges
The number of privileges and the privilege IDs plus their respective attributes field
TokenOwner
The SID of the owner for any newly created objects
TokenPrimaryGroup
The SID of the group for any newly created objects
TokenDefaultDacl
The Discretionary ACL added to any newly created objects
TokenSource
The name and identifier for where this access token came from (e.g. Session Manager, LAN Manager, and RPC Server)
TokenType
A flag as to whether this is the primary token or an impersonation token
TokenImpersonationLevel
A flag indicating the impersonation level (Anonymous, Identification, Impersonation or Delegation)
TokenStatistics
A structure with some general statistics about this access token. It is a combination of attributes that are available in other structures too, like Group Count, Privilege Count, etc.
TokenRestrictedSids
The number of groups and the list of SIDs plus their respective attributes field. This is only populated if an application has created a restricted token by effectively disabling some groups/privileges
TokenSessionId
The Terminal Services session id associated with this token (or zero if not a terminal services session related token)
TokenGroupsAndPrivileges
The combination of TokenGroups, TokenPrivileges, TokenRestructureSids and the AuthenticationId of the authenticator of this token
TokenVirtualizationAllowed
A flag to indicate whether per user file/registry virtualization is allowed for this token
TokenVirtualizationEnabled
A flag to indicate whether per user file/registry virtualization is enabled for this token
TokenIntegrityLevel
The Mandatory Integrity Level of this token (i.e. does it have Mandatory High, or Admin rights allows)
TokenUserClaimAttributes
The list of claims associated with this user
TokenDeviceClaimAttributes
The list of claims associated with this user’s device
TokenRestrictedUserClaimAttributes
The list of claims associated with this user that have been restricted by an application
TokenRestrictedDeviceClaimAttributes
The list of claims associated with this user’s device that have been restricted by an application
TokenDeviceGroups
The number of groups and the list of SIDs plus their respective attributes field for this user’s device
TokenRestrictedDeviceGroups
The number of groups and the list of SIDs plus their respective attributes field for this user’s device, if an application has created a restricted token by effectively disabling some groups/privileges
Of course we’re mostly interested in how the LSA takes the groups from the PAC and fills the TokenGroups structure. It’s interesting to note that while a Kerberos ticket supports Group SID Compression, the Access Token does not. Every SID in the Access Token uses up 40 bytes (the 8 byte pointer, the 4 byte Attributes, and the 28 byte SID)
The main thing that the LSA adds to the list of groups is any implicit memberships that need to be granted, including:
- Everyone (S-1-1-0)
- BUILTIN\Users (S-1-5-32-545)
- BUILTIN\Administrators (S-1-5-32-544)
- NT AUTHORITY\Authenticated Users (S-1-5-11)
- Logon Session Sid (S-1-5-5-X-Y)
- BUILTIN\Pre-Windows 2000 Compatible Access(S-1-5-32-554) if user is a member of this group (nested)
- Logon Dependent SIDs
- LOCAL (S-1-2-0)
- CONSOLE LOGON (S-1-2-1)
- NT AUTHORITY\NETWORK (S-1-5-2)
- NT AUTHORITY\SERVICE (S-1-5-6)
- NT AUTHORITY\INTERACTIVE (S-1-5-4)
- NT AUTHORITY\TERMINAL SERVER USER (S-1-5-13)
- NT AUTHORITY\BATCH (S-1-5-3)
- Integrity Level SID
- Medium Mandatory Level (S-1-16-8192)
- High Mandatory Level (S-1-16-12288)
As well as an local groups on the machine that the LSA runs.
Let’s take a look at the communication flows in more detail in part 6