Skip to content

Add PAC client/device claims (CLAIMS_SET) parsing to describeTicket - #2258

Open
cbev0x wants to merge 4 commits into
fortra:masterfrom
cbev0x:claims-pac
Open

Add PAC client/device claims (CLAIMS_SET) parsing to describeTicket#2258
cbev0x wants to merge 4 commits into
fortra:masterfrom
cbev0x:claims-pac

Conversation

@cbev0x

@cbev0x cbev0x commented Aug 12, 2026

Copy link
Copy Markdown

describeTicket currently drops PAC buffers 13 (PAC_CLIENT_CLAIMS_INFO) and 15 (PAC_DEVICE_CLAIMS_INFO) — the existing classes are raw ('Claims', ':') stubs with no CLAIMS_SET decoder, so claims are invisible. The last PAC work (#1391) added UPN_DNS/Attributes/Requestor but not claims.

This adds the [MS-ADTS] 2.2.18 CLAIMS_SET NDR chain (CLAIMS_SET_METADATA → CLAIMS_SET → CLAIMS_ARRAY → CLAIM_ENTRY) covering all four value types (int64/uint64/string/boolean), a parse_claims_set() helper, and wires both client (13) and device (15) claims into describeTicket, printing source/id/type/values.

Scope: uncompressed CLAIMS_SET (the common case). Compressed blobs raise a clear NotImplementedError; XPRESS-Huffman decompression is a planned follow-up.

Validated against a real Windows Server 2025 PAC (AD-sourced UserCity/UserDept string claims decode correctly).

@cbev0x
cbev0x marked this pull request as ready for review August 12, 2026 17:25
@anadrianmanrique anadrianmanrique added the in review This issue or pull request is being analyzed label Aug 21, 2026

@gabrielg5 gabrielg5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @cbev0x,
Thanks for the PR, been checking and seems fine (just commented a couple documentation nits)
Tried against a Windows Server 2019 and worked fine, retrieving those buffers (one note here, had to patch our scripts to ask the KDC for claims, as they were not being retrieved)

Wondering, which workflows you were running that needed that information? Also, in that sense, have you considered including buffer 14 (PAC_DEVICE_INFO - which device those claims belong to) too?

Comment thread impacket/krb5/pac.py Outdated
Comment thread impacket/krb5/pac.py Outdated
@gabrielg5 gabrielg5 added waiting for response Further information is needed from people who opened the issue or pull request low Low priority item and removed in review This issue or pull request is being analyzed labels Aug 25, 2026
cbev0x and others added 2 commits August 25, 2026 15:53
Co-authored-by: Gabriel Gonzalez <gabriel.gonzalez@fortra.com>
Co-authored-by: Gabriel Gonzalez <gabriel.gonzalez@fortra.com>
@cbev0x

cbev0x commented Aug 25, 2026

Copy link
Copy Markdown
Author

Hey, thanks for the review and testing. Those Doc nits are now fixed and pushed. CLAIM_TYPE is now 2.2.18.2 and CLAIMS_SOURCE_TYPE is 2.2.18.3. CLAIMS_COMPRESSION_FORMAT was already right at 2.2.18.4, so I left it.

On having to ask the KDC for claims, yeah that tracks. Pre-2025 the DC only stamps claims into the PAC when "KDC support for claims" is enabled, or when the request opts in, so on a default 2019 they're just not there. Nothing describeTicket can really do about that side since it only decodes what's actually in the ticket.

As for the workflow, this came out of some offensive research I've been doing on claims-based and Dynamic Access Control. When a ticket carries AD-sourced claims, those feed into conditional ACEs and central access rules, so being able to see the claim id/type/values in a captured or forged TGT or ST really matters for reasoning about what it can actually reach. It's also useful for looking at cross-forest claims transformation. describeTicket was silently dropping 13 and 15, so short of hand-parsing the NDR there was no good way to inspect them.

Buffer 14 (PAC_DEVICE_INFO) is a good call and I'm happy to add it so the device side is complete. impacket already has the PAC_DEVICE_INFO NDRSTRUCT, so it's mostly a matter of wiring it into the same dispatch and printing the device SID and groups along with the device's own claims. One thing I'd like to be upfront about is that I validated the client-claims path against a real 2025 ticket, but haven't had a device-claims or device-info PAC in hand yet since those need FAST or compound armoring to show up. So I'd rather validate 14 against a real armored ticket before I call it solid instead of feeding it in blind. I'll get that captured and fold 14 in once it checks out.

@cbev0x

cbev0x commented Aug 25, 2026

Copy link
Copy Markdown
Author

Added buffer 14 (PAC_DEVICE_INFO) enumeration via impacket's existing NDRSTRUCT, so describeTicket now surfaces all of 13/14/15 instead of silently dropping them.

Validation: client claims (13) confirmed end-to-end against real WS2025 tickets; device info (14) verified by round-tripping a marshaled PAC_DEVICE_INFO through the decode path (device SID, group RIDs, and extra SIDs all decode correctly); device claims (15) share the client-claims decoder. Compressed CLAIMS_SET blobs enumerate with a clear "not yet supported" note - XPRESS-Huffman decompression is the planned follow-up if needed.

@gabrielg5 gabrielg5 removed the waiting for response Further information is needed from people who opened the issue or pull request label Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

low Low priority item

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants