Kerberos.NET icon indicating copy to clipboard operation
Kerberos.NET copied to clipboard

Provide Structured SerializedData in PacCredentialInfo

Open dev-2null opened this issue 2 years ago • 9 comments

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

dev-2null avatar Sep 19 '22 01:09 dev-2null

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.

dev-2null avatar Sep 19 '22 09:09 dev-2null

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.

SteveSyfuhs avatar Sep 19 '22 16:09 SteveSyfuhs

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.

dev-2null avatar Sep 19 '22 16:09 dev-2null

            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)

dev-2null avatar Sep 19 '22 16:09 dev-2null

I see now. How are you getting sessionKey?

SteveSyfuhs avatar Sep 19 '22 17:09 SteveSyfuhs

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: @.***>

dev-2null avatar Sep 19 '22 17:09 dev-2null

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.

SteveSyfuhs avatar Sep 19 '22 17:09 SteveSyfuhs

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!

dev-2null avatar Sep 19 '22 17:09 dev-2null

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;
        }
    }

dev-2null avatar Sep 20 '22 07:09 dev-2null