Kerberos.NET
Kerberos.NET copied to clipboard
Provide Structured SerializedData in PacCredentialInfo
Hi, it would be nice to provide a solution to deal with the SerializedData
in PAC's CredentialType
(PacCredentialInfo
structure).
This could be quite useful for WHfB/smartcard users to obtain their NTLM hash in that encrypted NTLM_SUPPLEMENTAL_CREDENTIAL
blob after the User-to-User authentication, in order to perform NTLM authentication to access some dedicated resources.
At least the SerializedData
should be further structured as described in https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/2f9cae55-350a-423e-a692-1d16659e544a
I tried to decrypt this blob but I'm not sure how I can handle the decrypted data:
[-] Data: 000000001200000023946A4242359A4EF5D07C6B14C425F402FE7ADD8A32BF9E08B1E00C1C999EBE61F52CFF442118EDAC511240ACCB2942D9C753DA8471F1C9B7C4449D6D80B5C15030A1C49ABEA6CE9DBE5B78A58D5FD7A19C38286300A8511CBAE63EEAB0B7670B652D7299122C2859F879111E1913857F90CE08B87714D8E4FB324DFC36E34806D313C0AFFC3B8EA6A8D0E300000000
[-] Data Length: 152
[-] Cred Data Length: 144
[-] Encrypted: 23946A4242359A4EF5D07C6B14C425F402FE7ADD8A32BF9E08B1E00C1C999EBE61F52CFF442118EDAC511240ACCB2942D9C753DA8471F1C9B7C4449D6D80B5C15030A1C49ABEA6CE9DBE5B78A58D5FD7A19C38286300A8511CBAE63EEAB0B7670B652D7299122C2859F879111E1913857F90CE08B87714D8E4FB324DFC36E34806D313C0AFFC3B8EA6A8D0E3
[B64Encrypted] I5RqQkI1mk710HxrFMQl9AL+et2KMr+eCLHgDByZnr5h9Sz/RCEY7axREkCsyylC2cdT2oRx8cm3xESdbYC1wVAwocSavqbOnb5beKWNX9ehnDgoYwCoURy65j7qsLdnC2UtcpkSLChZ+HkRHhkThX+Qzgi4dxTY5PsyTfw240gG0xPAr/w7jqao0OM=
[-] Key: D089C983207D5548B3CB0D909E2E3AB77475F2BA08F3E271733AEB2683C7858F
[-] KeyEncType: AES256_CTS_HMAC_SHA1_96
[-] Decrypted: 33FE4CD5C2AA03F6A2F357FAC7B3FD59240D666BF29D143977DF01B62E101154CE2C1F6329E15707303C4611419CAA5D266738C8542E1F6719BF615BB89CC3DDB684A59C8BD2719F8A7DE0526C92692175F7DC980D2F5A4D8B75980829DF22874B28BB9B68962E5CE8FA50D1E31F44B6
The decrypted data is not sth I can further process, it's not Type Serialization Version 1 or 2 according to [MS-RPCE]. Not sure what type of data it is. The following data shows the correct format of decrypted data:
[-] Data: 0000000012000000E6AC2C5ACD657CA025F6C4A3A462133FE78310F19BCFE77A9F8B8B5F6F0895D3FD1299951A42802D5E8D0136C098490FE30D13D34389BDE015C1F3497CA41AD7AA9E259663B60645F4948F310E306DC0E55D61B462277ACB7B91427A280CE4533CB3C3F5DC9F49F74229ED49C9A7E306E854F2F6E1B4E07599C04F4A4607202516D06977DA37A70099018713
[-] Encrypted: E6AC2C5ACD657CA025F6C4A3A462133FE78310F19BCFE77A9F8B8B5F6F0895D3FD1299951A42802D5E8D0136C098490FE30D13D34389BDE015C1F3497CA41AD7AA9E259663B60645F4948F310E306DC0E55D61B462277ACB7B91427A280CE4533CB3C3F5DC9F49F74229ED49C9A7E306E854F2F6E1B4E07599C04F4A4607202516D06977DA37A70099018713
[-] Key: A85086A22E4D8C4985B359E0B449F4A41A00A4C06281E8ED24BD75B929A97564
[B64Encrypted] 5qwsWs1lfKAl9sSjpGITP+eDEPGbz+d6n4uLX28IldP9EpmVGkKALV6NATbAmEkP4w0T00OJveAVwfNJfKQa16qeJZZjtgZF9JSPMQ4wbcDlXWG0Yid6y3uRQnooDORTPLPD9dyfSfdCKe1JyafjBuhU8vbhtOB1mcBPSkYHICUW0Gl32jenAJkBhxM=
[-] Decrypted: 01100800CCCCCCCC6000000000000000000002000100000001000000080008000400020028000000080002000400000000000000040000004E0054004C004D002800000000000000020000000000000000000000000000000000000040C4B290222052161E9628FC680ACE6B00000000
This decrypted data is version 1 serialization that can be used.
What is this value?
01100800CCCCCCCC6000000000000000000002000100000001000000080008000400020028000000080002000400000000000000040000004E0054004C004D002800000000000000020000000000000000000000000000000000000040C4B290222052161E9628FC680ACE6B00000000
Is this the decrypted form of PAC_CREDENTIAL_INFO.SerializedData
? Just eyeballing the bytes it looks like a fully formed RPC message.
01
= Version 1
10
= Little Endian
08
= Common Header
00
= Padding
CCCCCCCC
= Filler
60000000
= Length...?
00000000
= Padding
And then everything else afterword is the structure. You can use NdrBuffer
with the INdrStruct
and friend interfaces to parse this.
Yes, but what I meant is that 01100800CCCCCCCC600...
is something I should get after decrypting SerializedData
, see encCredData
in Rubeus's implementaion of PacCredentialInfo. The encCredData
is equivalent to the SerializedData.ToArray()
) in Kerberos.Net, I tried to decrypt it with the ASREP key, and got 33FE4CD5C2AA03F6A2F35...
I mentioned above. So I don't know if the SerializedData
is properly serialized or I did something wrong.
KrbEncTicketPart ticketDecrypted = tgsRep.Ticket.EncryptedPart.Decrypt(
KerberosRun.U2USessionKey.AsKey(),
KeyUsage.Ticket,
(ReadOnlyMemory<byte> t) => KrbEncTicketPart.DecodeApplication(t));
var pac = Helper.GetPAC(ticketDecrypted);
Console.WriteLine("[-] PacCredentialInfo Data:");
Console.WriteLine(" Data: {0}", (BitConverter.ToString(pac.CredentialType.Marshal().ToArray())).Replace("-", ""));
Console.WriteLine(" Length: {0}", pac.CredentialType.Marshal().ToArray().Length);
var credSerialData = pac.CredentialType.SerializedData.ToArray();
Console.WriteLine("[-] SerializedData Data:");
Console.WriteLine(" Data: {0}", (BitConverter.ToString(credSerialData)).Replace("-", ""));
Console.WriteLine(" Length: {0}", credSerialData.Length);
var key = Utils.ByteArrayToStringCrypto(sessionKey.KeyValue.ToArray());
Console.WriteLine("[-] KeyEncType: {0}", sessionKey.EType);
Console.WriteLine("[-] Key: {0}", key);
var plainCredData = Helper.KerberosDecrypt(sessionKey.EType, 16, Utils.StringToByteArrayCrypto(key), credSerialData);
Console.WriteLine("[-] Decrypted: {0}", (BitConverter.ToString(plainCredData)).Replace("-", ""));
BinaryReader reader = new BinaryReader(new MemoryStream(plainCredData));
Console.WriteLine("[-] SerialType Version: {0}",reader.ReadByte());
[-] PacCredentialInfo Data:
Data: 0000000012000000616071D1B57DC951FAEC9F1E6D1E6CA3A2D8738AC6C0A6AD98B2D8AA874762257EB47F6ED5D8DFE5F5CCA83477925B0501B5923EB29DCA85C15E2FCCEDB1E9345599D3CEC054E63A7286D4F353D43D4F0046623104A79D35BAF492428E5A84E25883B7949808FE9221CA74FCC7ABC9EF10B33FADE6A1B6CA98F6BF97A6DB0F5F8037EFAFA86656D97EC95D1D00000000
Length: 152
[-] SerializedData Data:
Data: 616071D1B57DC951FAEC9F1E6D1E6CA3A2D8738AC6C0A6AD98B2D8AA874762257EB47F6ED5D8DFE5F5CCA83477925B0501B5923EB29DCA85C15E2FCCEDB1E9345599D3CEC054E63A7286D4F353D43D4F0046623104A79D35BAF492428E5A84E25883B7949808FE9221CA74FCC7ABC9EF10B33FADE6A1B6CA98F6BF97A6DB0F5F8037EFAFA86656D97EC95D1D
Length: 140
[-] KeyEncType: AES256_CTS_HMAC_SHA1_96
[-] Key: 296005BD5441B347C9ECC726F89B3035D73CDB17CD49A9F73D982D24446E0BE8
[-] Decrypted: 09C8E8994F6D29888958C7312F5574785C1EA998D680C43A3647D270A52BF4DF0A4A7623C65EB9F20DE71E2228E5B1000CE633021E5CE38F1C2B03F548869D6C1762BB24FD710E77AF517AD8D25C82F486866DD873DC269BC85A0996A0E60873884C4F2E42DCF16CFD80874CB6D442BE
[-] SerialType Version: 9
I should get SerialType Version 1 (or 2)
I see now. How are you getting sessionKey
?
From AS reply. Decrypt the ticket enc part and get the key
Get Outlook for iOShttps://aka.ms/o0ukef
From: Steve Syfuhs @.> Sent: Tuesday, September 20, 2022 1:11:54 AM To: dotnet/Kerberos.NET @.> Cc: dev2null @.>; Author @.> Subject: Re: [dotnet/Kerberos.NET] Provide Structured SerializedData in PacCredentialInfo (Issue #317)
I see now. How are you getting sessionKey?
— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/Kerberos.NET/issues/317#issuecomment-1251307153, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AHD3R4XT7HYAYPDENNHLJSLV7CNFVANCNFSM6AAAAAAQPWC4TA. You are receiving this because you authored the thread.Message ID: @.***>
That's not the correct key. It's the DH session key. The point is to bind the NTLM key in such a way that it shows possession of the certificate's private key. You can only do that with the DH session key.
Yeah, I always make mistakes... I thought ASREP key is asRep.Key... Now it works perfectly Thank you very much for your help! Really appreicate it!
Maybe as a reference for this feature request (decrypted SerializedData
structure), at least it works for me ;)
public class PacCredentialData : INdrStruct
{
public void Marshal(NdrBuffer buffer)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
buffer.WriteInt32LittleEndian(this.CredentialCount);
//Not sure about this
buffer.WriteDeferredStructArray(this.SuppCredential);
}
public void Unmarshal(NdrBuffer buffer)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
this.CredentialCount = buffer.ReadInt32LittleEndian();
this.SuppCredential = buffer.ReadConformantArray<SupplementalCredential>(this.CredentialCount, new Func<SupplementalCredential>(buffer.ReadStruct<SupplementalCredential>));
}
public PacCredentialData(ReadOnlyMemory<byte> bytes)
{
using (var buffer = new NdrBuffer(bytes))
{
buffer.UnmarshalObject(this);
}
}
[KerberosIgnore]
public int CredentialCount { get; set; }
public IEnumerable<SupplementalCredential> SuppCredential { get; set; }
public PacCredentialData(int CredentialCount, SupplementalCredential[] Credentials)
{
this.CredentialCount = CredentialCount;
this.SuppCredential = Credentials;
}
}
public class SupplementalCredential : INdrStruct
{
public SupplementalCredential() { }
public void Marshal(NdrBuffer buffer)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
buffer.WriteStruct<RpcString>(this.PackageName);
buffer.WriteInt32LittleEndian(this.CredentialSize);
buffer.WriteDeferredConformantArray<sbyte>(this.Credentials.ToArray());
}
public void Unmarshal(NdrBuffer buffer)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
this.PackageName = buffer.ReadStruct<RpcString>();
this.CredentialSize = buffer.ReadInt32LittleEndian();
buffer.ReadDeferredConformantArray<sbyte>(this.CredentialSize, v => this.Credentials = v);
}
[KerberosIgnore]
public RpcString PackageName { get; set; }
public int CredentialSize { get; set; }
public ReadOnlyMemory<sbyte> Credentials;
public SupplementalCredential(RpcString PackageName, int CredentialSize, sbyte[] Credentials)
{
this.PackageName = PackageName;
this.CredentialSize = CredentialSize;
this.Credentials = Credentials;
}
}