Improved DPAPI credential parsing
This commit is contained in:
parent
df6e0806ef
commit
4efd132ff5
|
@ -209,3 +209,6 @@ FakesAssemblies/
|
|||
# MIDL generated code
|
||||
/Src/DSInternals.Replication.Interop/drsr.h
|
||||
/Src/DSInternals.Replication.Interop/drsr.cpp
|
||||
|
||||
# Test Data
|
||||
[Tt]est[Dd]ata/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2021 Michael Grafnetter
|
||||
Copyright (c) 2015-2024 Michael Grafnetter
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Downloads the sample data from Azure storage.
|
||||
|
||||
.NOTES
|
||||
Uses AzCopy, which can be installed by running winget.exe install AzCopy.
|
||||
|
||||
#>
|
||||
|
||||
#Requires -Version 3
|
||||
|
||||
# Test that AzCopy is available
|
||||
Get-Command -Name azcopy.exe -CommandType Application -ErrorAction Stop | Out-Null
|
||||
|
||||
# Download the test data
|
||||
[string] $parentDirectory = Split-Path -Path $PSScriptRoot -Parent -ErrorAction Stop
|
||||
[string] $destinationDirectory = Join-Path -Path $parentDirectory -ChildPath TestData -ErrorAction Stop
|
||||
azcopy.exe copy https://dsinternals.blob.core.windows.net/databases/* $destinationDirectory --recursive --skip-version-check --output-level essential
|
|
@ -57,6 +57,23 @@ namespace DSInternals.Common.Test
|
|||
Assert.IsNull(key.KiwiCommand);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DPAPIBackupKey_PreferredRSAKeyPointerConflict()
|
||||
{
|
||||
// Test vector
|
||||
byte[] blob = "ec56e7ef7cf83a49902f259030203445".HexToBinary();
|
||||
string distinguishedName = "CN=BCKUPKEY_PREFERRED Secret\\0ACNF:26c8edbb-6b48-4f11-9e13-9ddbccedab5a,CN=System,DC=contoso,DC=com";
|
||||
|
||||
// Parse the input
|
||||
var key = new DPAPIBackupKey(distinguishedName, blob);
|
||||
|
||||
// Validate the results
|
||||
Assert.AreEqual(DPAPIBackupKeyType.PreferredRSAKeyPointer, key.Type);
|
||||
Assert.AreEqual(Guid.Parse("efe756ec-f87c-493a-902f-259030203445"), key.KeyId);
|
||||
Assert.IsNull(key.FilePath);
|
||||
Assert.IsNull(key.KiwiCommand);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DPAPIBackupKey_PreferredLegacyKeyPointer()
|
||||
{
|
||||
|
@ -73,5 +90,22 @@ namespace DSInternals.Common.Test
|
|||
Assert.IsNull(key.FilePath);
|
||||
Assert.IsNull(key.KiwiCommand);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DPAPIBackupKey_PreferredLegacyKeyPointerConflict()
|
||||
{
|
||||
// Test vector
|
||||
byte[] blob = "d5525c587817434d9d7bc22b4f7e5fa4".HexToBinary();
|
||||
string distinguishedName = "CN=BCKUPKEY_P Secret\\0ACNF:202d1e62-cf69-4446-9578-fce798843cde,CN=System,DC=contoso,DC=com";
|
||||
|
||||
// Parse the input
|
||||
var key = new DPAPIBackupKey(distinguishedName, blob);
|
||||
|
||||
// Validate the results
|
||||
Assert.AreEqual(DPAPIBackupKeyType.PreferredLegacyKeyPointer, key.Type);
|
||||
Assert.AreEqual(Guid.Parse("585c52d5-1778-4d43-9d7b-c22b4f7e5fa4"), key.KeyId);
|
||||
Assert.IsNull(key.FilePath);
|
||||
Assert.IsNull(key.KiwiCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,12 @@
|
|||
private const int RSAPrivateKeyOffset = RSACertificateSizeOffset + sizeof(int);
|
||||
private const string BackupKeyNameFormat = "G$BCKUPKEY_{0}";
|
||||
private const string BackupKeyDNFormat = "CN=BCKUPKEY_{0} Secret,CN=System,{1}";
|
||||
private const string BackupKeyDNRegex = "CN=BCKUPKEY_(.*) Secret,CN=System,.*";
|
||||
// Examples:
|
||||
// CN=BCKUPKEY_P Secret,CN=System,DC=contoso,DC=com
|
||||
// CN=BCKUPKEY_PREFERRED Secret,CN=System,DC=contoso,DC=com
|
||||
// CN=BCKUPKEY_PREFERRED Secret\0ACNF:26c8edbb-6b48-4f11-9e13-9ddbccedab5a,CN=System,DC=contoso,DC=com
|
||||
// CN=BCKUPKEY_ac9e427c-fa85-4b78-8db1-771d94c03bad Secret,CN=System,DC=contoso,DC=com
|
||||
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,.+";
|
||||
private const string PreferredLegacyKeyPointerName = "P";
|
||||
private const string PreferredRSAKeyPointerName = "PREFERRED";
|
||||
private const string TemporaryKeyContainerName = "DSInternals";
|
||||
|
@ -237,7 +242,7 @@
|
|||
private static string GetSecretNameFromDN(string distinguishedName)
|
||||
{
|
||||
var match = Regex.Match(distinguishedName, BackupKeyDNRegex);
|
||||
bool success = match.Success && (match.Groups.Count == 2);
|
||||
bool success = match.Success && (match.Groups.Count >= 2);
|
||||
return success ? match.Groups[1].Value : null;
|
||||
}
|
||||
|
||||
|
@ -292,4 +297,4 @@
|
|||
return (DPAPIBackupKeyType)BitConverter.ToInt32(blob, KeyVersionOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,8 @@
|
|||
// The actual roamed data
|
||||
this.Data = reader.ReadBytes(dataSize);
|
||||
|
||||
if(this.Type == RoamedCredentialType.CNGPrivateKey && dataSize > 0)
|
||||
// Note: The data structure might be corrupted and Data.Length can be less than dataSize.
|
||||
if (this.Type == RoamedCredentialType.CNGPrivateKey && this.Data.Length > 0)
|
||||
{
|
||||
// Remove Software KSP NCRYPT_OPAQUETRANSPORT_BLOB header
|
||||
this.Data = new CngSoftwareProviderTransportBlob(this.Data).KeyData;
|
||||
|
|
|
@ -8,7 +8,7 @@ DSInternals PowerShell Module and Framework
|
|||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2023 Michael Grafnetter
|
||||
Copyright (c) 2015-2024 Michael Grafnetter
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -47,6 +47,7 @@ EndProject
|
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{A6D948F4-1847-47B5-8997-83EEBD971892}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\Scripts\Build-Solution.ps1 = ..\Scripts\Build-Solution.ps1
|
||||
..\Scripts\Get-TestData.ps1 = ..\Scripts\Get-TestData.ps1
|
||||
..\Scripts\Invoke-SmokeTests.ps1 = ..\Scripts\Invoke-SmokeTests.ps1
|
||||
..\Scripts\Make.ps1 = ..\Scripts\Make.ps1
|
||||
..\Scripts\Pack-Chocolatey.ps1 = ..\Scripts\Pack-Chocolatey.ps1
|
||||
|
@ -93,11 +94,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PowerShell", "PowerShell",
|
|||
..\Documentation\PowerShell\Disable-ADDBAccount.md = ..\Documentation\PowerShell\Disable-ADDBAccount.md
|
||||
..\Documentation\PowerShell\Enable-ADDBAccount.md = ..\Documentation\PowerShell\Enable-ADDBAccount.md
|
||||
..\Documentation\PowerShell\Get-ADDBAccount.md = ..\Documentation\PowerShell\Get-ADDBAccount.md
|
||||
..\Documentation\PowerShell\Get-ADDBServiceAccount.md = ..\Documentation\PowerShell\Get-ADDBServiceAccount.md
|
||||
..\Documentation\PowerShell\Get-ADDBBackupKey.md = ..\Documentation\PowerShell\Get-ADDBBackupKey.md
|
||||
..\Documentation\PowerShell\Get-ADDBDomainController.md = ..\Documentation\PowerShell\Get-ADDBDomainController.md
|
||||
..\Documentation\PowerShell\Get-ADDBKdsRootKey.md = ..\Documentation\PowerShell\Get-ADDBKdsRootKey.md
|
||||
..\Documentation\PowerShell\Get-ADDBSchemaAttribute.md = ..\Documentation\PowerShell\Get-ADDBSchemaAttribute.md
|
||||
..\Documentation\PowerShell\Get-ADDBServiceAccount.md = ..\Documentation\PowerShell\Get-ADDBServiceAccount.md
|
||||
..\Documentation\PowerShell\Get-ADKeyCredential.md = ..\Documentation\PowerShell\Get-ADKeyCredential.md
|
||||
..\Documentation\PowerShell\Get-ADReplAccount.md = ..\Documentation\PowerShell\Get-ADReplAccount.md
|
||||
..\Documentation\PowerShell\Get-ADReplBackupKey.md = ..\Documentation\PowerShell\Get-ADReplBackupKey.md
|
||||
|
|
Loading…
Reference in New Issue