Skip to content

Commit 4efd132

Browse files
Improved DPAPI credential parsing
1 parent df6e080 commit 4efd132

File tree

8 files changed

+70
-8
lines changed

8 files changed

+70
-8
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,6 @@ FakesAssemblies/
209209
# MIDL generated code
210210
/Src/DSInternals.Replication.Interop/drsr.h
211211
/Src/DSInternals.Replication.Interop/drsr.cpp
212+
213+
# Test Data
214+
[Tt]est[Dd]ata/

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2015-2021 Michael Grafnetter
3+
Copyright (c) 2015-2024 Michael Grafnetter
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Scripts/Get-TestData.ps1

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<#
2+
.SYNOPSIS
3+
Downloads the sample data from Azure storage.
4+
5+
.NOTES
6+
Uses AzCopy, which can be installed by running winget.exe install AzCopy.
7+
8+
#>
9+
10+
#Requires -Version 3
11+
12+
# Test that AzCopy is available
13+
Get-Command -Name azcopy.exe -CommandType Application -ErrorAction Stop | Out-Null
14+
15+
# Download the test data
16+
[string] $parentDirectory = Split-Path -Path $PSScriptRoot -Parent -ErrorAction Stop
17+
[string] $destinationDirectory = Join-Path -Path $parentDirectory -ChildPath TestData -ErrorAction Stop
18+
azcopy.exe copy https://dsinternals.blob.core.windows.net/databases/* $destinationDirectory --recursive --skip-version-check --output-level essential

Src/DSInternals.Common.Test/DPAPIBackupKeyTester.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,23 @@ public void DPAPIBackupKey_PreferredRSAKeyPointer()
5757
Assert.IsNull(key.KiwiCommand);
5858
}
5959

60+
[TestMethod]
61+
public void DPAPIBackupKey_PreferredRSAKeyPointerConflict()
62+
{
63+
// Test vector
64+
byte[] blob = "ec56e7ef7cf83a49902f259030203445".HexToBinary();
65+
string distinguishedName = "CN=BCKUPKEY_PREFERRED Secret\\0ACNF:26c8edbb-6b48-4f11-9e13-9ddbccedab5a,CN=System,DC=contoso,DC=com";
66+
67+
// Parse the input
68+
var key = new DPAPIBackupKey(distinguishedName, blob);
69+
70+
// Validate the results
71+
Assert.AreEqual(DPAPIBackupKeyType.PreferredRSAKeyPointer, key.Type);
72+
Assert.AreEqual(Guid.Parse("efe756ec-f87c-493a-902f-259030203445"), key.KeyId);
73+
Assert.IsNull(key.FilePath);
74+
Assert.IsNull(key.KiwiCommand);
75+
}
76+
6077
[TestMethod]
6178
public void DPAPIBackupKey_PreferredLegacyKeyPointer()
6279
{
@@ -73,5 +90,22 @@ public void DPAPIBackupKey_PreferredLegacyKeyPointer()
7390
Assert.IsNull(key.FilePath);
7491
Assert.IsNull(key.KiwiCommand);
7592
}
93+
94+
[TestMethod]
95+
public void DPAPIBackupKey_PreferredLegacyKeyPointerConflict()
96+
{
97+
// Test vector
98+
byte[] blob = "d5525c587817434d9d7bc22b4f7e5fa4".HexToBinary();
99+
string distinguishedName = "CN=BCKUPKEY_P Secret\\0ACNF:202d1e62-cf69-4446-9578-fce798843cde,CN=System,DC=contoso,DC=com";
100+
101+
// Parse the input
102+
var key = new DPAPIBackupKey(distinguishedName, blob);
103+
104+
// Validate the results
105+
Assert.AreEqual(DPAPIBackupKeyType.PreferredLegacyKeyPointer, key.Type);
106+
Assert.AreEqual(Guid.Parse("585c52d5-1778-4d43-9d7b-c22b4f7e5fa4"), key.KeyId);
107+
Assert.IsNull(key.FilePath);
108+
Assert.IsNull(key.KiwiCommand);
109+
}
76110
}
77-
}
111+
}

Src/DSInternals.Common/Data/DPAPI/DPAPIBackupKey.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ public class DPAPIBackupKey : DPAPIObject
1616
private const int RSAPrivateKeyOffset = RSACertificateSizeOffset + sizeof(int);
1717
private const string BackupKeyNameFormat = "G$BCKUPKEY_{0}";
1818
private const string BackupKeyDNFormat = "CN=BCKUPKEY_{0} Secret,CN=System,{1}";
19-
private const string BackupKeyDNRegex = "CN=BCKUPKEY_(.*) Secret,CN=System,.*";
19+
// Examples:
20+
// CN=BCKUPKEY_P Secret,CN=System,DC=contoso,DC=com
21+
// CN=BCKUPKEY_PREFERRED Secret,CN=System,DC=contoso,DC=com
22+
// CN=BCKUPKEY_PREFERRED Secret\0ACNF:26c8edbb-6b48-4f11-9e13-9ddbccedab5a,CN=System,DC=contoso,DC=com
23+
// CN=BCKUPKEY_ac9e427c-fa85-4b78-8db1-771d94c03bad Secret,CN=System,DC=contoso,DC=com
24+
private const string BackupKeyDNRegex = "CN=BCKUPKEY_(.+) Secret(\\\\0ACNF:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?,CN=System,.+";
2025
private const string PreferredLegacyKeyPointerName = "P";
2126
private const string PreferredRSAKeyPointerName = "PREFERRED";
2227
private const string TemporaryKeyContainerName = "DSInternals";
@@ -237,7 +242,7 @@ public static string GetPreferredLegacyKeyPointerDN(string domainDN)
237242
private static string GetSecretNameFromDN(string distinguishedName)
238243
{
239244
var match = Regex.Match(distinguishedName, BackupKeyDNRegex);
240-
bool success = match.Success && (match.Groups.Count == 2);
245+
bool success = match.Success && (match.Groups.Count >= 2);
241246
return success ? match.Groups[1].Value : null;
242247
}
243248

@@ -292,4 +297,4 @@ private static DPAPIBackupKeyType GetKeyType(byte[] blob)
292297
return (DPAPIBackupKeyType)BitConverter.ToInt32(blob, KeyVersionOffset);
293298
}
294299
}
295-
}
300+
}

Src/DSInternals.Common/Data/DPAPI/RoamedCredential.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ public RoamedCredential(byte[] blob, string accountName, SecurityIdentifier acco
8686
// The actual roamed data
8787
this.Data = reader.ReadBytes(dataSize);
8888

89-
if(this.Type == RoamedCredentialType.CNGPrivateKey && dataSize > 0)
89+
// Note: The data structure might be corrupted and Data.Length can be less than dataSize.
90+
if (this.Type == RoamedCredentialType.CNGPrivateKey && this.Data.Length > 0)
9091
{
9192
// Remove Software KSP NCRYPT_OPAQUETRANSPORT_BLOB header
9293
this.Data = new CngSoftwareProviderTransportBlob(this.Data).KeyData;

Src/DSInternals.PowerShell/License.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ DSInternals PowerShell Module and Framework
88

99
The MIT License (MIT)
1010

11-
Copyright (c) 2015-2023 Michael Grafnetter
11+
Copyright (c) 2015-2024 Michael Grafnetter
1212

1313
Permission is hereby granted, free of charge, to any person obtaining a copy
1414
of this software and associated documentation files (the "Software"), to deal

Src/DSInternals.sln

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ EndProject
4747
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{A6D948F4-1847-47B5-8997-83EEBD971892}"
4848
ProjectSection(SolutionItems) = preProject
4949
..\Scripts\Build-Solution.ps1 = ..\Scripts\Build-Solution.ps1
50+
..\Scripts\Get-TestData.ps1 = ..\Scripts\Get-TestData.ps1
5051
..\Scripts\Invoke-SmokeTests.ps1 = ..\Scripts\Invoke-SmokeTests.ps1
5152
..\Scripts\Make.ps1 = ..\Scripts\Make.ps1
5253
..\Scripts\Pack-Chocolatey.ps1 = ..\Scripts\Pack-Chocolatey.ps1
@@ -93,11 +94,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PowerShell", "PowerShell",
9394
..\Documentation\PowerShell\Disable-ADDBAccount.md = ..\Documentation\PowerShell\Disable-ADDBAccount.md
9495
..\Documentation\PowerShell\Enable-ADDBAccount.md = ..\Documentation\PowerShell\Enable-ADDBAccount.md
9596
..\Documentation\PowerShell\Get-ADDBAccount.md = ..\Documentation\PowerShell\Get-ADDBAccount.md
96-
..\Documentation\PowerShell\Get-ADDBServiceAccount.md = ..\Documentation\PowerShell\Get-ADDBServiceAccount.md
9797
..\Documentation\PowerShell\Get-ADDBBackupKey.md = ..\Documentation\PowerShell\Get-ADDBBackupKey.md
9898
..\Documentation\PowerShell\Get-ADDBDomainController.md = ..\Documentation\PowerShell\Get-ADDBDomainController.md
9999
..\Documentation\PowerShell\Get-ADDBKdsRootKey.md = ..\Documentation\PowerShell\Get-ADDBKdsRootKey.md
100100
..\Documentation\PowerShell\Get-ADDBSchemaAttribute.md = ..\Documentation\PowerShell\Get-ADDBSchemaAttribute.md
101+
..\Documentation\PowerShell\Get-ADDBServiceAccount.md = ..\Documentation\PowerShell\Get-ADDBServiceAccount.md
101102
..\Documentation\PowerShell\Get-ADKeyCredential.md = ..\Documentation\PowerShell\Get-ADKeyCredential.md
102103
..\Documentation\PowerShell\Get-ADReplAccount.md = ..\Documentation\PowerShell\Get-ADReplAccount.md
103104
..\Documentation\PowerShell\Get-ADReplBackupKey.md = ..\Documentation\PowerShell\Get-ADReplBackupKey.md

0 commit comments

Comments
 (0)