Exemple #1
0
        /// <summary>
        /// The format of supplementalCredentials is a USER_PROPERTIES structure (https://msdn.microsoft.com/en-us/library/cc245674.aspx).
        /// USER_PROPERTIES structure is as follows (https://msdn.microsoft.com/en-us/library/cc245500.aspx):
        ///
        /// Reserved1 (4 bytes): This value MUST be set to zero and MUST be ignored by the recipient.
        /// Length(4 bytes): This value MUST be set to the length, in bytes, of the entire structure, starting from the Reserved4 field.
        /// Reserved2(2 bytes): This value MUST be set to zero and MUST be ignored by the recipient.
        /// Reserved3(2 bytes): This value MUST be set to zero and MUST be ignored by the recipient.
        /// Reserved4(96 bytes): This value MUST be ignored by the recipient and MAY contain arbitrary values.
        /// PropertySignature(2 bytes): This field MUST be the value 0x50, in little-endian byte order.This is an arbitrary value used to indicate whether the structure is corrupt.That is, if this value is not 0x50 on read, the structure is considered corrupt, processing MUST be aborted, and an error code MUST be returned.
        /// PropertyCount(2 bytes): The number of USER_PROPERTY elements in the UserProperties field.When there are zero USER_PROPERTY elements in the UserProperties field, this field MUST be omitted; the resultant USER_PROPERTIES structure has a constant size of 0x6F bytes.
        /// UserProperties(variable): An array of PropertyCount USER_PROPERTY elements.
        /// Reserved5(1 byte): This value SHOULD be set to zero and MUST be ignored by the recipient.
        ///
        /// USER_PROPERTY structure is as follows (https://msdn.microsoft.com/en-us/library/cc245501.aspx):
        ///
        /// NameLength (2 bytes): The number of bytes, in little-endian byte order, of PropertyName. The property name is located at an offset of zero bytes just following the Reserved field. For more information, see the message processing section for supplementalCredentials (section 3.1.1.8.11).
        /// ValueLength(2 bytes): The number of bytes contained in PropertyValue.
        /// Reserved(2 bytes): This value MUST be ignored by the recipient and MAY be set to arbitrary values on update.
        /// PropertyName(variable): The name of this property as a UTF-16 encoded string.
        /// PropertyValue(variable): The value of this property.The value MUST be hexadecimal-encoded using an 8-bit character size, and the values '0' through '9' inclusive and 'a' through 'f' inclusive(the specification of 'a' through 'f' is case-sensitive).
        /// </summary>
        /// <param name="pekList">The PEK list.</param>
        /// <param name="encryptedSupplementalCredentialsBlob">The encrypted blob.</param>
        /// <returns>Clear text passwords.</returns>
        public static Dictionary <string, byte[]> DecryptSupplementalCredentials(Dictionary <uint, byte[]> pekList, byte[] encryptedSupplementalCredentialsBlob)
        {
            var decryptedBlob = NTCrypto.DecryptSecret(pekList, encryptedSupplementalCredentialsBlob);

            var properties = new Dictionary <string, byte[]>();

            // Check the property signature is equal to 0x50, and if not assume the structure is corrupt.
            if (decryptedBlob.Length < 110 || BitConverter.ToUInt16(decryptedBlob, 108) != 0x50)
            {
                return(properties);
            }

            // If there are zero USER_PROPERTY elements, the length will be 0x6F
            if (decryptedBlob.Length == 0x6F)
            {
                return(properties);
            }

            var propertiesCount = BitConverter.ToUInt16(decryptedBlob, 110);
            var propertiesBlob  = decryptedBlob.Skip(112).Take(decryptedBlob.Length - 113).ToArray();

            using (var reader = new BinaryReader(new MemoryStream(propertiesBlob)))
            {
                for (var i = 0; i < propertiesCount; i++)
                {
                    var nameLength  = reader.ReadUInt16();
                    var valueLength = reader.ReadUInt16();
                    reader.ReadUInt16();
                    var propertyNameBlob  = reader.ReadBytes(nameLength);
                    var propertyValueBlob = reader.ReadBytes(valueLength);

                    var propertyName            = Encoding.Unicode.GetString(propertyNameBlob);
                    var hexEncodedPropertyValue = Encoding.ASCII.GetString(propertyValueBlob);
                    var propertyValue           = Enumerable.Range(0, hexEncodedPropertyValue.Length)
                                                  .Where(x => x % 2 == 0)
                                                  .Select(x => Convert.ToByte(hexEncodedPropertyValue.Substring(x, 2), 16))
                                                  .ToArray();

                    properties[propertyName] = propertyValue;
                }
            }

            return(properties);
        }
Exemple #2
0
        private void DecryptSecretData(string systemKeyPath, bool includeHistoryHashes)
        {
            Stopwatch stopwatch = null;

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"Called: {nameof(NtdsAudit)}::{nameof(DecryptSecretData)}");
                stopwatch = new Stopwatch();
                stopwatch.Start();
            }

            var systemKey = SystemHive.LoadSystemKeyFromHive(systemKeyPath);

            var encryptedPek     = _datatable.Single(x => x.PekList != null).PekList;
            var decryptedPekList = NTCrypto.DecryptPekList(systemKey, encryptedPek);

            foreach (var row in _datatable)
            {
                if ((row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT) == (int)ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT)
                {
                    if (row.EncryptedLmHash != null)
                    {
                        try
                        {
                            row.LmHash = ByteArrayToHexString(NTCrypto.DecryptHashes(decryptedPekList, row.EncryptedLmHash, row.Rid));
                        }
                        catch (Exception ex)
                        {
                            if (ShowDebugOutput)
                            {
                                ConsoleEx.WriteDebug($"Failed to decrypt LM hash for '{row.SamAccountName}' with error: {ex.Message}");
                            }

                            row.LmHash = EMPTY_LM_HASH;
                        }
                    }
                    else
                    {
                        row.LmHash = EMPTY_LM_HASH;
                    }

                    if (row.EncryptedNtHash != null)
                    {
                        try
                        {
                            row.NtHash = ByteArrayToHexString(NTCrypto.DecryptHashes(decryptedPekList, row.EncryptedNtHash, row.Rid));
                        }
                        catch (Exception ex)
                        {
                            if (ShowDebugOutput)
                            {
                                ConsoleEx.WriteDebug($"Failed to decrypt NT hash for '{row.SamAccountName}' with error: {ex.Message}");
                            }

                            row.NtHash = EMPTY_LM_HASH;
                        }
                    }
                    else
                    {
                        row.NtHash = EMPTY_NT_HASH;
                    }

                    if (includeHistoryHashes)
                    {
                        if (row.EncryptedLmHistory != null)
                        {
                            var hashStrings = new List <string>();

                            var decryptedHashes = new byte[0];
                            try
                            {
                                decryptedHashes = NTCrypto.DecryptHashes(decryptedPekList, row.EncryptedLmHistory, row.Rid);
                            }
                            catch (Exception ex)
                            {
                                if (ShowDebugOutput)
                                {
                                    ConsoleEx.WriteDebug($"Failed to decrypt LM history hashes for '{row.SamAccountName}' with error: {ex.Message}");
                                }
                            }

                            // The first hash is the same as the current hash, so skip it
                            for (var i = 16; i < decryptedHashes.Length; i += 16)
                            {
                                // If lm hash is disabled for the account, the lm history will contain junk data, ignore it
                                if (row.LmHash == EMPTY_LM_HASH)
                                {
                                    hashStrings.Add(EMPTY_LM_HASH);
                                }
                                else
                                {
                                    hashStrings.Add(ByteArrayToHexString(decryptedHashes.Skip(i).Take(16).ToArray()));
                                }
                            }

                            row.LmHistory = hashStrings.ToArray();
                        }

                        if (row.EncryptedNtHistory != null)
                        {
                            var hashStrings = new List <string>();

                            var decryptedHashes = new byte[0];
                            try
                            {
                                decryptedHashes = NTCrypto.DecryptHashes(decryptedPekList, row.EncryptedNtHistory, row.Rid);
                            }
                            catch (Exception ex)
                            {
                                if (ShowDebugOutput)
                                {
                                    ConsoleEx.WriteDebug($"Failed to decrypt LM history hashes for '{row.SamAccountName}' with error: {ex.Message}");
                                }
                            }

                            // The first hash is the same as the current hash, so skip it
                            for (var i = 16; i < decryptedHashes.Length; i += 16)
                            {
                                hashStrings.Add(ByteArrayToHexString(decryptedHashes.Skip(i).Take(16).ToArray()));
                            }

                            row.NtHistory = hashStrings.ToArray();
                        }
                    }

                    if (row.SupplementalCredentialsBlob != null)
                    {
                        try
                        {
                            row.SupplementalCredentials = NTCrypto.DecryptSupplementalCredentials(decryptedPekList, row.SupplementalCredentialsBlob);
                        }
                        catch (Exception ex)
                        {
                            if (ShowDebugOutput)
                            {
                                ConsoleEx.WriteDebug($"Failed to decrypt supplemental credentials for '{row.SamAccountName}' with error: {ex.Message}");
                            }
                        }
                    }
                }
            }

            if (ShowDebugOutput)
            {
                stopwatch.Stop();
                ConsoleEx.WriteDebug($"  Completed in {stopwatch.Elapsed}");
            }
        }