Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide Structured SerializedData in PacCredentialInfo #317

Open
dev-2null opened this issue Sep 19, 2022 · 9 comments
Open

Provide Structured SerializedData in PacCredentialInfo #317

dev-2null opened this issue Sep 19, 2022 · 9 comments

Comments

@dev-2null
Copy link

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
Copy link
Author

dev-2null commented Sep 19, 2022

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.

@SteveSyfuhs
Copy link
Collaborator

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.

@dev-2null
Copy link
Author

dev-2null commented Sep 19, 2022

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
Copy link
Author

dev-2null commented Sep 19, 2022

            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)

@SteveSyfuhs
Copy link
Collaborator

I see now. How are you getting sessionKey?

@dev-2null
Copy link
Author

dev-2null commented Sep 19, 2022 via email

@SteveSyfuhs
Copy link
Collaborator

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.

@dev-2null
Copy link
Author

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
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants