pac
import "github.com/TheManticoreProject/Manticore/network/kerberos/v5/pac"
Package pac parses, builds, and verifies the Microsoft Privilege Attribute Certificate (PAC, [MS-PAC]) carried in a Kerberos ticket’s authorization data.
A PAC is a PACTYPE container (little-endian, NOT NDR at the container level): a cBuffers/Version header followed by a table of PAC_INFO_BUFFER entries, each pointing at an 8-byte-aligned buffer. Individual buffers such as the logon info (KERB_VALIDATION_INFO) are themselves NDR-encoded; this package exposes those buffers as raw bytes and additionally NDR-decodes the logon info into a typed KERB_VALIDATION_INFO via PAC.LogonInfo (see logoninfo.go).
The security-relevant operations — the Server and KDC signatures ([MS-PAC] 2.8, “Generation of PAC Signatures”) — are implemented: both are keyed checksums with key usage KERB_NON_KERB_CKSUM_SALT (17); the server signature covers the entire PAC with all signature buffers’ Signature fields zeroed, and the KDC signature counter-signs the server signature.
Index
- Constants
- func MarshalClientInfo(clientID time.Time, name string) []byte
- func MarshalKerbValidationInfo(info *KERB_VALIDATION_INFO) ([]byte, error)
- func SignatureTypeForEType(etype int) (sigType uint32, size int, ok bool)
- type Buffer
- type CredentialInfo
- type FILETIME
- type GROUP_MEMBERSHIP
- type KERB_SID_AND_ATTRIBUTES
- type KERB_VALIDATION_INFO
- func UnmarshalKerbValidationInfo(data []byte) (*KERB_VALIDATION_INFO, error)
- func (k *KERB_VALIDATION_INFO) AccountControl() uint32
- func (k *KERB_VALIDATION_INFO) DomainSID() *msdtyp.RPC_SID
- func (k *KERB_VALIDATION_INFO) ExtraSIDs() []string
- func (k *KERB_VALIDATION_INFO) FullNameString() string
- func (k *KERB_VALIDATION_INFO) GroupRIDs() []uint32
- func (k *KERB_VALIDATION_INFO) GroupSIDs() []string
- func (k *KERB_VALIDATION_INFO) LogonDomainNameString() string
- func (k *KERB_VALIDATION_INFO) LogonServerName() string
- func (k *KERB_VALIDATION_INFO) PrimaryGroupRID() uint32
- func (k *KERB_VALIDATION_INFO) PrimaryGroupSID() string
- func (k *KERB_VALIDATION_INFO) UserName() string
- func (k *KERB_VALIDATION_INFO) UserRID() uint32
- func (k *KERB_VALIDATION_INFO) UserSID() string
- type NTLMCredential
- type PAC
- func Forge(info *KERB_VALIDATION_INFO, clientName string, clientAuthTime time.Time, signEType int) (*PAC, error)
- func Parse(data []byte) (*PAC, error)
- func (p *PAC) Buffer(ulType uint32) (Buffer, bool)
- func (p *PAC) LogonInfo() (*KERB_VALIDATION_INFO, error)
- func (p *PAC) Marshal() ([]byte, error)
- func (p *PAC) Sign(serverKey, kdcKey []byte) ([]byte, error)
- func (p *PAC) VerifyKDCSignature(krbtgtKey []byte) error
- func (p *PAC) VerifyServerSignature(serverKey []byte) error
- type RPC_UNICODE_STRING
- type USER_SESSION_KEY
Constants
PAC_INFO_BUFFER ulType values ([MS-PAC] 2.4).
const (
BufferLogonInfo = 0x00000001 // KERB_VALIDATION_INFO (NDR)
BufferCredentials = 0x00000002 // PAC_CREDENTIAL_INFO
BufferServerChecksum = 0x00000006 // server signature
BufferKDCChecksum = 0x00000007 // KDC (krbtgt) signature
BufferClientInfo = 0x0000000A // PAC_CLIENT_INFO
BufferConstrainedDeleg = 0x0000000B // S4U_DELEGATION_INFO
BufferUPNDNSInfo = 0x0000000C // UPN_DNS_INFO
BufferClientClaims = 0x0000000D // client claims
BufferDeviceInfo = 0x0000000E // PAC_DEVICE_INFO
BufferDeviceClaims = 0x0000000F // device claims
BufferTicketChecksum = 0x00000010 // ticket signature
BufferAttributesInfo = 0x00000011 // PAC_ATTRIBUTES_INFO
BufferRequestorSID = 0x00000012 // PAC_REQUESTOR
BufferExtendedKDCChecksum = 0x00000013 // extended KDC (full-PAC) signature
)
DefaultGroupAttributes marks a group as mandatory, enabled by default, and enabled (SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED), the attribute set Windows assigns to a user’s group memberships in the PAC.
const DefaultGroupAttributes uint32 = 0x00000007
func MarshalClientInfo
func MarshalClientInfo(clientID time.Time, name string) []byte
MarshalClientInfo lays out a PAC_CLIENT_INFO buffer ([MS-PAC] 2.7): a little-endian FILETIME ClientId (the TGT authentication time), a 16-bit NameLength in bytes, and the client account name as UTF-16LE. This buffer is not NDR-encoded.
func MarshalKerbValidationInfo
func MarshalKerbValidationInfo(info *KERB_VALIDATION_INFO) ([]byte, error)
MarshalKerbValidationInfo NDR-encodes the logon info under type serialization version 1 ([MS-RPCE] 2.2.6): the CommonTypeHeader and PrivateHeaderForConstruct edType frame the NDR body, which is padded to an 8-octet boundary.
func SignatureTypeForEType
func SignatureTypeForEType(etype int) (sigType uint32, size int, ok bool)
SignatureTypeForEType returns the PAC signature type ([MS-PAC] 2.8) and its byte length for a signing key of the given Kerberos encryption type: HMAC-MD5 for RC4, HMAC-SHA1-96 for the AES enctypes. It reports false for unsupported enctypes.
type Buffer
Buffer is one PAC_INFO_BUFFER and its contents.
type Buffer struct {
Type uint32
Offset uint64 // offset from the start of the PAC (informational after Parse)
Data []byte
}
type CredentialInfo
CredentialInfo is a parsed PAC_CREDENTIAL_INFO buffer ([MS-PAC] §2.6.1).
type CredentialInfo struct {
// Version is the structure version (0).
Version uint32
// EncryptionType is the etype of SerializedData (the AS reply key's etype).
EncryptionType uint32
// SerializedData is the encrypted PAC_CREDENTIAL_DATA.
SerializedData []byte
}
func ParseCredentialInfo
func ParseCredentialInfo(data []byte) (*CredentialInfo, error)
ParseCredentialInfo parses a PAC_CREDENTIAL_INFO buffer.
func (*CredentialInfo) DecryptCredentialData
func (ci *CredentialInfo) DecryptCredentialData(replyKey []byte) ([]byte, error)
DecryptCredentialData decrypts a PAC_CREDENTIAL_INFO’s SerializedData with the AS reply key (the PKINIT-derived key), yielding the NDR-serialized PAC_CREDENTIAL_DATA. The key usage is KERB_NON_KERB_SALT (16) per [MS-PAC].
type FILETIME
FILETIME is the [MS-DTYP] 2.3.3 64-bit timestamp as two 32-bit halves, NDR marshaled as a structure of two ULONGs (little-endian).
type FILETIME struct {
LowDateTime uint32
HighDateTime uint32
}
func FileTimeFromTime
func FileTimeFromTime(t time.Time) FILETIME
FileTimeFromTime converts a Go time to a Windows FILETIME. A zero time maps to a zero FILETIME.
func NeverExpireFileTime
func NeverExpireFileTime() FILETIME
NeverExpireFileTime is the FILETIME value (0x7FFFFFFFFFFFFFFF) used for “never” times such as LogoffTime, KickOffTime, and PasswordMustChange.
func (FILETIME) IsNever
func (f FILETIME) IsNever() bool
IsNever reports whether the FILETIME is the “never” sentinel (0x7FFFFFFFFFFFFFFF), used for LogoffTime/KickOffTime/PasswordMustChange when the corresponding limit does not apply.
func (FILETIME) Time
func (f FILETIME) Time() time.Time
Time converts the FILETIME to a Go time in UTC. A zero FILETIME (password never set) and the “never” sentinel both map to the zero time.Time, so callers can test the result with time.Time.IsZero. It is the inverse of FileTimeFromTime.
func (FILETIME) Uint64
func (f FILETIME) Uint64() uint64
Uint64 returns the FILETIME as a single 64-bit count of 100-nanosecond intervals since 1601-01-01 (the low half is the least significant).
type GROUP\_MEMBERSHIP
GROUP_MEMBERSHIP is the [MS-PAC] 2.2.2 (RID, attributes) pair naming a group the account belongs to in a domain.
type GROUP_MEMBERSHIP struct {
RelativeId uint32
Attributes uint32
}
type KERB\_SID\_AND\_ATTRIBUTES
KERB_SID_AND_ATTRIBUTES is the [MS-PAC] 2.2.1 (SID, attributes) pair used for ExtraSids (groups from domains other than the account domain).
type KERB_SID_AND_ATTRIBUTES struct {
Sid *msdtyp.RPC_SID `ndr:"unique"`
Attributes uint32
}
type KERB\_VALIDATION\_INFO
KERB_VALIDATION_INFO is the [MS-PAC] 2.5 logon-info structure (ulType 0x00000001). It is a subset of NETLOGON_VALIDATION_SAM_INFO4; the NTLM-specific members (UserSessionKey, the User* flag bits) are zero under Kerberos. The pointer members are unique; the size_is arrays are pointers to conformant arrays whose counts follow from the paired *Count field / slice length.
type KERB_VALIDATION_INFO struct {
LogonTime FILETIME
LogoffTime FILETIME
KickOffTime FILETIME
PasswordLastSet FILETIME
PasswordCanChange FILETIME
PasswordMustChange FILETIME
EffectiveName RPC_UNICODE_STRING
FullName RPC_UNICODE_STRING
LogonScript RPC_UNICODE_STRING
ProfilePath RPC_UNICODE_STRING
HomeDirectory RPC_UNICODE_STRING
HomeDirectoryDrive RPC_UNICODE_STRING
LogonCount uint16
BadPasswordCount uint16
UserId uint32
PrimaryGroupId uint32
GroupCount uint32
GroupIds []GROUP_MEMBERSHIP `ndr:"unique,size_is=GroupCount"`
UserFlags uint32
UserSessionKey USER_SESSION_KEY
LogonServer RPC_UNICODE_STRING
LogonDomainName RPC_UNICODE_STRING
LogonDomainId *msdtyp.RPC_SID `ndr:"unique"`
Reserved1 [2]uint32
UserAccountControl uint32
SubAuthStatus uint32
LastSuccessfulILogon FILETIME
LastFailedILogon FILETIME
FailedILogonCount uint32
Reserved3 uint32
SidCount uint32
ExtraSids []KERB_SID_AND_ATTRIBUTES `ndr:"unique,size_is=SidCount"`
ResourceGroupDomainSid *msdtyp.RPC_SID `ndr:"unique"`
ResourceGroupCount uint32
ResourceGroupIds []GROUP_MEMBERSHIP `ndr:"unique,size_is=ResourceGroupCount"`
}
func UnmarshalKerbValidationInfo
func UnmarshalKerbValidationInfo(data []byte) (*KERB_VALIDATION_INFO, error)
UnmarshalKerbValidationInfo decodes a type-serialized logon-info buffer (the output of MarshalKerbValidationInfo, or a Windows PAC’s ulType 0x01 buffer) back into a KERB_VALIDATION_INFO. It strips the 16-byte serialization headers and NDR-decodes the body.
func (*KERB_VALIDATION_INFO) AccountControl
func (k *KERB_VALIDATION_INFO) AccountControl() uint32
AccountControl returns the userAccountControl flags carried in the PAC.
func (*KERB_VALIDATION_INFO) DomainSID
func (k *KERB_VALIDATION_INFO) DomainSID() *msdtyp.RPC_SID
DomainSID returns the account domain’s SID (LogonDomainId), or nil if absent.
func (*KERB_VALIDATION_INFO) ExtraSIDs
func (k *KERB_VALIDATION_INFO) ExtraSIDs() []string
ExtraSIDs returns the ExtraSids member (SIDs from domains other than the account domain, e.g. universal groups and injected SIDs) as textual SID strings.
func (*KERB_VALIDATION_INFO) FullNameString
func (k *KERB_VALIDATION_INFO) FullNameString() string
FullNameString returns the account’s display name (FullName).
func (*KERB_VALIDATION_INFO) GroupRIDs
func (k *KERB_VALIDATION_INFO) GroupRIDs() []uint32
GroupRIDs returns the RIDs of the account’s groups in its own domain (GroupIds).
func (*KERB_VALIDATION_INFO) GroupSIDs
func (k *KERB_VALIDATION_INFO) GroupSIDs() []string
GroupSIDs returns the account’s account-domain group memberships (GroupIds) as full SID strings (domain SID + each group RID). It returns nil if the PAC has no LogonDomainId.
func (*KERB_VALIDATION_INFO) LogonDomainNameString
func (k *KERB_VALIDATION_INFO) LogonDomainNameString() string
LogonDomainNameString returns the NetBIOS name of the account’s domain.
func (*KERB_VALIDATION_INFO) LogonServerName
func (k *KERB_VALIDATION_INFO) LogonServerName() string
LogonServerName returns the NetBIOS name of the KDC that issued the ticket.
func (*KERB_VALIDATION_INFO) PrimaryGroupRID
func (k *KERB_VALIDATION_INFO) PrimaryGroupRID() uint32
PrimaryGroupRID returns the RID of the account’s primary group.
func (*KERB_VALIDATION_INFO) PrimaryGroupSID
func (k *KERB_VALIDATION_INFO) PrimaryGroupSID() string
PrimaryGroupSID returns the primary group’s SID (domain SID + PrimaryGroupId), or "" if the PAC has no LogonDomainId.
func (*KERB_VALIDATION_INFO) UserName
func (k *KERB_VALIDATION_INFO) UserName() string
UserName returns the account’s samAccountName (EffectiveName) as a Go string.
func (*KERB_VALIDATION_INFO) UserRID
func (k *KERB_VALIDATION_INFO) UserRID() uint32
UserRID returns the account’s relative identifier (UserId).
func (*KERB_VALIDATION_INFO) UserSID
func (k *KERB_VALIDATION_INFO) UserSID() string
UserSID returns the account’s SID as the domain SID with the UserId RID appended, or "" if the PAC has no LogonDomainId.
type NTLMCredential
NTLMCredential holds the LM and NT hashes recovered from a NTLM_SUPPLEMENTAL_CREDENTIAL ([MS-PAC] §2.6.4).
type NTLMCredential struct {
// Version is the structure version (0).
Version uint32
// Flags indicates which hashes are present (bit 0 = LM, bit 1 = NT).
Flags uint32
// LMHash is the 16-byte LM hash (valid when Flags&1 != 0).
LMHash []byte
// NTHash is the 16-byte NT hash (valid when Flags&2 != 0).
NTHash []byte
}
func ParseNTLMSupplementalCredential
func ParseNTLMSupplementalCredential(data []byte) (*NTLMCredential, error)
ParseNTLMSupplementalCredential extracts the NTLM_SUPPLEMENTAL_CREDENTIAL from a decrypted PAC_CREDENTIAL_DATA blob.
PAC_CREDENTIAL_DATA is NDR type-serialized ([MS-PAC] §2.6.2): a 16-byte type serialization header, then CredentialCount and an array of SECPKG_SUPPLEMENTAL_CRED. For the single “NTLM” package Windows emits, the deferred Credentials byte array is the 40-byte NTLM_SUPPLEMENTAL_CREDENTIAL (Version, Flags, LM[16], NT[16]); it is the final conformant array in the buffer, located here via its 4-byte MaximumCount prefix.
type PAC
PAC is a parsed PACTYPE container.
type PAC struct {
Version uint32
Buffers []Buffer
// contains filtered or unexported fields
}
func Forge
func Forge(info *KERB_VALIDATION_INFO, clientName string, clientAuthTime time.Time, signEType int) (*PAC, error)
Forge assembles an unsigned PAC from an NDR-encoded logon info and the client principal, with empty server (0x06) and KDC (0x07) signature buffers sized for signEType. Call Sign(serverKey, kdcKey) on the result to fill the signatures and obtain the final marshaled PAC. For a golden ticket both keys are the krbtgt key; for a silver ticket both are the service account key.
func Parse
func Parse(data []byte) (*PAC, error)
Parse decodes a PACTYPE container. Buffer contents are copied out.
func (*PAC) Buffer
func (p *PAC) Buffer(ulType uint32) (Buffer, bool)
Buffer returns the first buffer of the given ulType.
func (*PAC) LogonInfo
func (p *PAC) LogonInfo() (*KERB_VALIDATION_INFO, error)
LogonInfo locates the logon-info buffer (PAC_INFO_BUFFER ulType 0x00000001) and NDR-decodes it into a KERB_VALIDATION_INFO ([MS-PAC] 2.5). It returns an error if the PAC has no logon-info buffer or the buffer does not decode.
func (*PAC) Marshal
func (p *PAC) Marshal() ([]byte, error)
Marshal lays out the PAC in wire form: header, PAC_INFO_BUFFER table, then each buffer’s data 8-byte aligned. It uses the buffers’ current Data verbatim (so signature buffers should already be sized and, if unsigned, zero-filled).
func (*PAC) Sign
func (p *PAC) Sign(serverKey, kdcKey []byte) ([]byte, error)
Sign fills the server signature (buffer 0x06) and then the KDC signature (buffer 0x07) in place, using the given keys. Both use key usage KERB_NON_KERB_CKSUM_SALT. The buffers must already exist, sized to 4 + checksumSize(their SignatureType). Returns the fully signed marshaled PAC.
func (*PAC) VerifyKDCSignature
func (p *PAC) VerifyKDCSignature(krbtgtKey []byte) error
VerifyKDCSignature recomputes the KDC signature with the krbtgt key (over the stored server-signature bytes) and compares it to the stored value.
func (*PAC) VerifyServerSignature
func (p *PAC) VerifyServerSignature(serverKey []byte) error
VerifyServerSignature recomputes the server signature with serverKey and compares it, in constant time, to the stored value. The PAC must have been produced by Parse (so the exact issued bytes are available).
type RPC\_UNICODE\_STRING
RPC_UNICODE_STRING is the [MS-DTYP] 2.3.10 counted Unicode string. Length and MaximumLength are byte counts; Buffer is a unique pointer to a conformant and varying array of UTF-16LE code units (no NUL terminator). The NDR array counts are derived from Buffer’s length, so callers should keep Length == MaximumLength == 2*len(Buffer) (NewUnicodeString does this).
type RPC_UNICODE_STRING struct {
Length uint16
MaximumLength uint16
Buffer []uint16 `ndr:"unique,varying"`
}
func NewUnicodeString
func NewUnicodeString(s string) RPC_UNICODE_STRING
NewUnicodeString builds an RPC_UNICODE_STRING from a Go string, encoding it as UTF-16LE with Length and MaximumLength set to its byte length.
func (RPC_UNICODE_STRING) String
func (u RPC_UNICODE_STRING) String() string
String decodes the RPC_UNICODE_STRING to a Go string. Only the first Length/2 UTF-16 code units are significant ([MS-DTYP] 2.3.10 counts bytes, and the buffer carries no NUL terminator); any extra allocated units are dropped.
type USER\_SESSION\_KEY
USER_SESSION_KEY is the 16-byte session key ([MS-PAC] 2.2.3). It is zero for [MS-KILE] (Kerberos) authentication.
type USER_SESSION_KEY struct {
Data [16]byte
}