Skip to content

Commit

Permalink
Implemented managed password derivation
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelGrafnetter committed Sep 30, 2023
1 parent 77e7717 commit 05f344a
Show file tree
Hide file tree
Showing 37 changed files with 1,148 additions and 239 deletions.
1 change: 1 addition & 0 deletions Documentation/PowerShell/Get-ADDBAccount.md
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
## RELATED LINKS
[Get-BootKey](Get-BootKey.md)
[Get-ADDBServiceAccount](Get-ADDBServiceAccount.md)
[Get-ADReplAccount](Get-ADDBAccount.md)
[Get-ADSIAccount](Get-ADSIAccount.md)
[Test-PasswordQuality](Test-PasswordQuality.md)
Expand Down
2 changes: 2 additions & 0 deletions Documentation/PowerShell/Get-ADDBKdsRootKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,5 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
## NOTES
## RELATED LINKS
[Get-ADDBServiceAccount](Get-ADDBServiceAccount.md)
129 changes: 129 additions & 0 deletions Documentation/PowerShell/Get-ADDBServiceAccount.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
external help file: DSInternals.PowerShell.dll-Help.xml
Module Name: DSInternals
online version:
schema: 2.0.0
---

# Get-ADDBServiceAccount

## SYNOPSIS
Reads all Group Managed Service Accounts (gMSAs) from a ntds.dit file, while deriving their current passwords from KDS root keys.

## SYNTAX

```
Get-ADDBServiceAccount [-EffectiveTime <DateTime>] -DatabasePath <String> [-LogPath <String>]
[<CommonParameters>]
```

## DESCRIPTION
As none of the required information is encrypted, the BootKey is not required.
Does not work on database files from RODCs.

## EXAMPLES

### Example 1
```powershell
PS C:\> Get-ADDBServiceAccount -DatabasePath 'C:\ADBackup\ntds.dit'
<# Sample Output:
DistinguishedName: CN=svc_adfs,CN=Managed Service Accounts,DC=contoso,DC=com
Sid: S-1-5-21-2468531440-3719951020-3687476655-1109
Guid: 53c845f7-d9cd-471b-a364-e733641dcc86
SamAccountName: svc_adfs$
Description: ADFS Service Account
Enabled: True
Deleted: False
UserAccountControl: WorkstationAccount
SupportedEncryptionTypes: RC4_HMAC, AES128_CTS_HMAC_SHA1_96, AES256_CTS_HMAC_SHA1_96
ServicePrincipalName: {http/login.contoso.com, host/login.contoso.com}
WhenCreated: 9/9/2023 5:02:05 PM
PasswordLastSet: 9/9/2023 5:02:06 PM
ManagedPasswordInterval: 30
ManagedPasswordId: RootKey=7dc95c96-fa85-183a-dff5-f70696bf0b11, Cycle=9/9/2023 10:00:00 AM (L0=361, L1=26, L2=24)
ManagedPasswordPreviousId:
KDS Derived Secrets
NTHash: 0b5fbfb646dd7bce4f160ad69edb86ba
Kerberos Keys
AES256_CTS_HMAC_SHA1_96
Key: 5dcc418cd0a30453b267e6e5b158be4b4d80d23fd72a6ae4d5bd07f023517117
Iterations: 4096
AES128_CTS_HMAC_SHA1_96
Key: 8e1e66438a15d764ae2242eefd15e09a
Iterations: 4096
#>
```

Reads all Group Managed Service Accounts (gMSAs) from the specified ntds.dit file, while deriving their current passwords from KDS root keys.

### Example 2
```powershell
PS C:\> Get-ADDBServiceAccount -EffectiveTime (Get-Date).AddMonths(1) -DatabasePath 'C:\ADBackup\ntds.dit'
```

Reads all Group Managed Service Accounts (gMSAs) from the specified ntds.dit file, while deriving their future passwords from KDS root keys.

## PARAMETERS

### -DatabasePath
Specifies the path to a domain database, for instance, C:\Windows\NTDS\ntds.dit.

```yaml
Type: String
Parameter Sets: (All)
Aliases: Database, DBPath, DatabaseFilePath, DBFilePath

Required: True
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -EffectiveTime
Specifies the date and time at which the fenerated credentials should be valid. Defaults to the current time.
```yaml
Type: DateTime
Parameter Sets: (All)
Aliases: EffectiveDate, PasswordLastSet, PwdLastSet, Date, Time, d, t

Required: False
Position: Named
Default value: [datetime]::Now
Accept pipeline input: False
Accept wildcard characters: False
```
### -LogPath
Specifies the path to a directory where the transaction log files are located. For instance, C:\Windows\NTDS. The default log directory is the one that contains the database file itself.
```yaml
Type: String
Parameter Sets: (All)
Aliases: Log, TransactionLogPath

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
### None
## OUTPUTS
### DSInternals.Common.Data.GroupManagedServiceAccount
## NOTES
## RELATED LINKS
[Get-ADDBKdsRootKey](Get-ADDBKdsRootKey.md)
[Get-ADDBAccount](Get-ADDBAccount.md)
3 changes: 3 additions & 0 deletions Documentation/PowerShell/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ Reads the DPAPI backup keys from a ntds.dit file.
### [Get-ADDBKdsRootKey](Get-ADDBKdsRootKey.md#get-addbkdsrootkey)
Reads KDS Root Keys from a ntds.dit. file. Can be used to aid DPAPI-NG decryption, e.g. SID-protected PFX files.

### [Get-ADDBServiceAccount](Get-ADDBServiceAccount.md#get-addbserviceaccount)
Reads all Group Managed Service Accounts (gMSAs) from a ntds.dit file, while deriving their current passwords from KDS root keys.

### [Get-ADDBDomainController](Get-ADDBDomainController.md#get-addbdomaincontroller)
Reads information about the originating DC from a ntds.dit file, including domain name, domain SID, DC name and DC site.

Expand Down
6 changes: 5 additions & 1 deletion Src/Configuration/Common.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<CreateHardLinksForCopyLocalIfPossible>true</CreateHardLinksForCopyLocalIfPossible>
<CreateHardLinksForPublishFilesIfPossible>true</CreateHardLinksForPublishFilesIfPossible>
</PropertyGroup>
<PropertyGroup>
<!-- Some crypto API functions are not available in WoW64.-->
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<!-- Signing configuration -->
<PropertyGroup>
<DoSign Condition=" '$(Configuration)'=='Release' ">true</DoSign>
Expand All @@ -31,4 +35,4 @@
<SigningKeyPath Condition=" '$(PerformDelaySign)' == 'true' ">$(PublicKeyPath)</SigningKeyPath>
<SigningKeyPath Condition=" '$(PerformDelaySign)' == 'false' ">$(PrivateKeyPath)</SigningKeyPath>
</PropertyGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
using DSInternals.Common.Data;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel;
using System.Security;

[TestClass]
public class KerberosKeyDerivationTester
{
[TestMethod]
public void KerberosKeyDerivation_DES_CBC_MD5()
public void KerberosKeyDerivation_DES_CBC_MD5_User()
{
var password = "Pa$$w0rd".ToSecureString();
string salt = "ADATUM.COMApril";
Expand All @@ -20,7 +21,22 @@ public void KerberosKeyDerivation_DES_CBC_MD5()
}

[TestMethod]
public void KerberosKeyDerivation_AES256_CTS_HMAC_SHA1_96()
public void KerberosKeyDerivation_DES_CBC_MD5_Service()
{
SecureString password = "f81377aacff9cafe039d91a8f758de148200332b062dc1ac59d8cfcb4f14d9fe0def16e33e4b1a7d90645407860797097ac424570c0664f50d3f3433cea5c3e8594eada2797ef1e27cda6d92fe72d3425206e3ca173f01ac04325d0eaab2eac06b3ff7b4668f3a62a1696e27c1c32e7f06e09adb7784290a2704dc02416bb46c19e91bc4b5a842ce0879459439f685b20225134ed4562cb5bcd944d0acb07986308466385a455e65fd0ee325cae97709a33bc0f413b66ef40bbc59ce7a2a20f500cc1f80a13849b86efbfd59f037277c017ac4bfda3596a75cf06d84a5e118a948653f1aef02dec76501d26ea3cc3b63bb587824a727d02373ea8a5a9e7a71f5".HexToBinary().ReadSecureWString(0);
string salt = "CONTOSO.COMhostsvc_adfs.contoso.com";
int iterations = 4096;
string expected = "16bab507d3dad66d";

byte[] result = KerberosKeyDerivation.DeriveKey(KerberosKeyType.DES_CBC_MD5, password, salt, iterations);

// TODO: Properly implement DES key derivation for service accounts.
Assert.Inconclusive("DES key derivation does not yield proper results for service accounts yet.");
Assert.AreEqual(expected, result.ToHex(false));
}

[TestMethod]
public void KerberosKeyDerivation_AES256_CTS_HMAC_SHA1_96_User()
{
var password = "Pa$$w0rd".ToSecureString();
string salt = "ADATUM.COMApril";
Expand All @@ -31,6 +47,18 @@ public void KerberosKeyDerivation_AES256_CTS_HMAC_SHA1_96()
Assert.AreEqual(expected, result.ToHex(false));
}

[TestMethod]
public void KerberosKeyDerivation_AES256_CTS_HMAC_SHA1_96_Service()
{
SecureString password = "f81377aacff9cafe039d91a8f758de148200332b062dc1ac59d8cfcb4f14d9fe0def16e33e4b1a7d90645407860797097ac424570c0664f50d3f3433cea5c3e8594eada2797ef1e27cda6d92fe72d3425206e3ca173f01ac04325d0eaab2eac06b3ff7b4668f3a62a1696e27c1c32e7f06e09adb7784290a2704dc02416bb46c19e91bc4b5a842ce0879459439f685b20225134ed4562cb5bcd944d0acb07986308466385a455e65fd0ee325cae97709a33bc0f413b66ef40bbc59ce7a2a20f500cc1f80a13849b86efbfd59f037277c017ac4bfda3596a75cf06d84a5e118a948653f1aef02dec76501d26ea3cc3b63bb587824a727d02373ea8a5a9e7a71f5".HexToBinary().ReadSecureWString(0);
string salt = "CONTOSO.COMhostsvc_adfs.contoso.com";
int iterations = 4096;
string expected = "5dcc418cd0a30453b267e6e5b158be4b4d80d23fd72a6ae4d5bd07f023517117";

byte[] result = KerberosKeyDerivation.DeriveKey(KerberosKeyType.AES256_CTS_HMAC_SHA1_96, password, salt, iterations);
Assert.AreEqual(expected, result.ToHex(false));
}

[TestMethod]
public void KerberosKeyDerivation_AES128_CTS_HMAC_SHA1_96()
{
Expand All @@ -55,9 +83,15 @@ public void KerberosKeyDerivation_NULL()
}

[TestMethod]
public void KerberosKeyDerivation_Salt()
public void KerberosKeyDerivation_UserSalt()
{
Assert.AreEqual("ADATUM.COMApril", KerberosKeyDerivation.DeriveSalt("April", "Adatum.com"));
}

[TestMethod]
public void KerberosKeyDerivation_ComputerSalt()
{
Assert.AreEqual("CONTOSO.COMhostdc01.contoso.com", KerberosKeyDerivation.DeriveSalt("DC01$", "Contoso.com"));
}
}
}
4 changes: 2 additions & 2 deletions Src/DSInternals.Common.Test/DSInternals.Common.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess>
<PlatformTarget>x64</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="CBOR, Version=4.5.2.0, Culture=neutral, PublicKeyToken=9cd62db60ea5554c, processorArchitecture=MSIL">
Expand Down
63 changes: 54 additions & 9 deletions Src/DSInternals.Common.Test/KdsRootKeyTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,26 @@ public void ParseKdfParameters_EmptyInput()
public void ParseSecretAgreementParameters_Vector1()
{
byte[] blob = "0c0200004448504d0001000087a8e61db4b6663cffbbd19c651959998ceef608660dd0f25d2ceed4435e3b00e00df8f1d61957d4faf7df4561b2aa3016c3d91134096faa3bf4296d830e9a7c209e0c6497517abd5a8a9d306bcf67ed91f9e6725b4758c022e0b1ef4275bf7b6c5bfc11d45f9088b941f54eb1e59bb8bc39a0bf12307f5c4fdb70c581b23f76b63acae1caa6b7902d52526735488a0ef13c6d9a51bfa4ab3ad8347796524d8ef6a167b5a41825d967e144e5140564251ccacb83e6b486f6b3ca3f7971506026c0b857f689962856ded4010abd0be621c3a3960a54e710c375f26375d7014103a4b54330c198af126116d2276e11715f693877fad7ef09cadb094ae91e1a15973fb32c9b73134d0b2e77506660edbd484ca7b18f21ef205407f4793a1a0ba12510dbc15077be463fff4fed4aac0bb555be3a6c1b0c6b47b1bc3773bf7e8c6f62901228f8c28cbb18a55ae31341000a650196f931c77a57f2ddf463e5e9ec144b777de62aaab8a8628ac376d282d6ed3864e67982428ebc831d14348f6f2f9193b5045af2767164e1dfc967c1fb3f2e55a4bd1bffe83b9c80d052b985d182ea0adb2a3b7313d3fe14c8484b1e052588b9b7d2bbd2df016199ecd06e1557cd0915b3353bbb64e0ec377fd028370df92b52c7891428cdc67eb6184b523d1db246c32f63078490f00ef8d647d148d47954515e2327cfef98c582664b4c0f6cc41659".HexToBinary();
KdsRootKey.ParseSecretAgreementParameters(blob);
throw new AssertInconclusiveException();
(byte[] p, byte[] g) = KdsRootKey.ParseSecretAgreementParameters(blob);
Assert.IsNotNull(p);
Assert.IsNotNull(g);
Assert.AreEqual(p.Length, g.Length);
}

[TestMethod]
public void ParseSecretAgreementParameters_NullInput()
{
KdsRootKey.ParseSecretAgreementParameters(null);
throw new AssertInconclusiveException();
(byte[] p, byte[] g) = KdsRootKey.ParseSecretAgreementParameters(null);
Assert.IsNull(p);
Assert.IsNull(g);
}

[TestMethod]
public void ParseSecretAgreementParameters_EmptyInput()
{
KdsRootKey.ParseSecretAgreementParameters(new byte[0] { });
throw new AssertInconclusiveException();
(byte[] p, byte[] g) = KdsRootKey.ParseSecretAgreementParameters(new byte[0] { });
Assert.IsNull(p);
Assert.IsNull(g);
}

[TestMethod]
Expand All @@ -75,7 +79,6 @@ public void GetGmsaPassword_Vector1()
{
byte[] binaryPassword = KdsRootKey.GetPassword(
new SecurityIdentifier("S-1-5-21-2468531440-3719951020-3687476655-1109"),
null,
Guid.Parse("7dc95c96-fa85-183a-dff5-f70696bf0b11"),
"814ad2f3928ff96d3650487967392feab3924f3d0dff8629d46a723640101cff8ca2cbd6aba40805cf03b380803b27837d80663eb4d18fd4cec414ebb2271fe2".HexToBinary(),
"SP800_108_CTR_HMAC",
Expand All @@ -87,18 +90,60 @@ public void GetGmsaPassword_Vector1()

[TestMethod]
public void GetGmsaPassword_Vector2()
{
byte[] binaryPassword = KdsRootKey.GetPassword(
new SecurityIdentifier("S-1-5-21-2468531440-3719951020-3687476655-1109"),
Guid.Parse("7dc95c96-fa85-183a-dff5-f70696bf0b11"),
"814ad2f3928ff96d3650487967392feab3924f3d0dff8629d46a723640101cff8ca2cbd6aba40805cf03b380803b27837d80663eb4d18fd4cec414ebb2271fe2".HexToBinary(),
"SP800_108_CTR_HMAC",
"00000000010000000e000000000000005300480041003500310032000000".HexToBinary(),
DateTime.FromFileTimeUtc(133387453261266352),
DateTime.FromFileTimeUtc(133403352475182719),
30
);

Assert.AreEqual("0b5fbfb646dd7bce4f160ad69edb86ba", NTHash.ComputeHash(binaryPassword).ToHex());
}

[TestMethod]
public void GetGmsaPassword_Vector3()
{
var managedPasswordId = new ProtectionKeyIdentifier("010000004b44534b02000000690100001a00000018000000965cc97d85fa3a18dff5f70696bf0b1100000000180000001800000063006f006e0074006f0073006f002e0063006f006d00000063006f006e0074006f0073006f002e0063006f006d000000".HexToBinary());
byte[] binaryPassword = KdsRootKey.GetPassword(
new SecurityIdentifier("S-1-5-21-2468531440-3719951020-3687476655-1109"),
managedPasswordId,
Guid.Parse("7dc95c96-fa85-183a-dff5-f70696bf0b11"),
"814ad2f3928ff96d3650487967392feab3924f3d0dff8629d46a723640101cff8ca2cbd6aba40805cf03b380803b27837d80663eb4d18fd4cec414ebb2271fe2".HexToBinary(),
"SP800_108_CTR_HMAC",
"00000000010000000e000000000000005300480041003500310032000000".HexToBinary(),
DateTime.FromFileTimeUtc(133403352475182719));
managedPasswordId.L0KeyId,
managedPasswordId.L1KeyId,
managedPasswordId.L2KeyId
);

Assert.AreEqual("0b5fbfb646dd7bce4f160ad69edb86ba", NTHash.ComputeHash(binaryPassword).ToHex());
}
[TestMethod]
public void GetGmsaPassword_Vector4()
{
byte[] binaryPassword = KdsRootKey.GetPassword(
new SecurityIdentifier("S-1-5-21-1040335485-253814736-2627409954-1145"),
Guid.Parse("0670b5ed-f2aa-9a86-dd0e-49cfc2130533"),
"902bc244751f7cfb1bbafff7586585d467496953da553fd3decae08421b6c0ab5f60637541655b8be90fa319e24041875eccd465e253ceba238e1d475c80f64b".HexToBinary(),
"SP800_108_CTR_HMAC",
"00000000010000000e000000000000005300480041003500310032000000".HexToBinary(),
DateTime.FromFileTimeUtc(133211195280000000), // whenCreated ( 6 months diff)
DateTime.FromFileTimeUtc(133404554396754922)
);

Assert.AreEqual("e510057c721830f0b27482833cff4986", NTHash.ComputeHash(binaryPassword).ToHex());
}

[TestMethod]
public void IntervalCalculation_Reverse()
{
DateTime effectiveTime = KdsRootKey.GetRootIntervalStart(361, 28, 4);

Assert.AreEqual(new DateTime(2023, 9, 27).Date, effectiveTime.Date);
}
}
}
Loading

0 comments on commit 05f344a

Please sign in to comment.