mirror of
https://github.com/itm4n/PrivescCheck
synced 2026-06-08 14:54:28 +00:00
Add private key file information to personal certificate enumeration
This commit is contained in:
Vendored
+3
@@ -117,6 +117,7 @@
|
||||
"IPHTTPS",
|
||||
"ISATAP",
|
||||
"jaredcatkinson",
|
||||
"KEYEXCHANGE",
|
||||
"Lanman",
|
||||
"Ldarg",
|
||||
"Ldtoken",
|
||||
@@ -149,6 +150,7 @@
|
||||
"msidb",
|
||||
"msiexec",
|
||||
"msrcincident",
|
||||
"ncrypt",
|
||||
"NDIS",
|
||||
"netapi",
|
||||
"NETLOGON",
|
||||
@@ -203,6 +205,7 @@
|
||||
"Sacl",
|
||||
"SAERXCIT",
|
||||
"Sandboxed",
|
||||
"SCARD",
|
||||
"SCCM",
|
||||
"SCHTASKS",
|
||||
"SDDL",
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
373422768
|
||||
380036237
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-12-25
|
||||
|
||||
### Added
|
||||
|
||||
- Attempt to determine private key file path when enumerating machine and user personal certificates (issue #77).
|
||||
|
||||
## 2025-12-24
|
||||
|
||||
### Added
|
||||
|
||||
@@ -2501,7 +2501,8 @@ function Get-CertificateContextProperty {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[IntPtr] $ContextPtr,
|
||||
[UInt32] $PropId
|
||||
[UInt32] $PropId,
|
||||
[String] $PropName
|
||||
)
|
||||
|
||||
process {
|
||||
@@ -2515,8 +2516,16 @@ function Get-CertificateContextProperty {
|
||||
# CERT_KEY_PROV_INFO_PROP_ID
|
||||
2 {
|
||||
$ProviderInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PropertyPtr, [type] $script:CRYPT_KEY_PROV_INFO)
|
||||
Write-Verbose $ProviderInfo.ContainerName
|
||||
return $ProviderInfo.ProvName
|
||||
if ([String]::IsNullOrEmpty($PropName)) {
|
||||
return $ProviderInfo.ProvName
|
||||
}
|
||||
else {
|
||||
switch ($PropName) {
|
||||
"ProviderName" { return $ProviderInfo.ProvName }
|
||||
"ContainerName" { return $ProviderInfo.ContainerName }
|
||||
default { throw "Unhandled property name: $($PropName)"}
|
||||
}
|
||||
}
|
||||
}
|
||||
# CERT_HASH_PROP_ID
|
||||
3 {
|
||||
@@ -2559,6 +2568,117 @@ function Get-CertificateContextProperty {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-CertificateNCryptProperty {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Wrapper for NCryptGetProperty
|
||||
|
||||
Author: @itm4n
|
||||
License: BSD 3-Clause
|
||||
|
||||
.DESCRIPTION
|
||||
This cmdlet invokes the NCryptGetProperty CNG API to retrieve a property of a certificate.
|
||||
|
||||
.PARAMETER ContextPtr
|
||||
A pointer to a certificate context.
|
||||
|
||||
.PARAMETER Property
|
||||
The name of a certificate property to retrieve.
|
||||
#>
|
||||
|
||||
[OutputType([String])]
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[IntPtr] $ContextPtr,
|
||||
[String] $Property
|
||||
)
|
||||
|
||||
begin {
|
||||
$FlagValues = @(
|
||||
0x00000000,
|
||||
# NCRYPT_MACHINE_KEY_FLAG
|
||||
0x00000020
|
||||
)
|
||||
$LegacyKeySpecValues = @(
|
||||
0x00000000,
|
||||
# AT_KEYEXCHANGE
|
||||
0x00000001,
|
||||
# AT_SIGNATURE
|
||||
0x00000002
|
||||
)
|
||||
}
|
||||
|
||||
process {
|
||||
$ProviderName = Get-CertificateContextProperty -ContextPtr $ContextPtr -PropId 2 -PropName "ProviderName"
|
||||
$ContainerName = Get-CertificateContextProperty -ContextPtr $ContextPtr -PropId 2 -PropName "ContainerName"
|
||||
|
||||
$ProviderHandle = [UIntPtr]::Zero
|
||||
$Status = $script:Ncrypt::NCryptOpenStorageProvider([ref] $ProviderHandle, $ProviderName, 0)
|
||||
if ($Status -eq 0) {
|
||||
|
||||
$KeyHandle = [UIntPtr]::Zero
|
||||
foreach ($FlagValue in $FlagValues) {
|
||||
foreach ($LegacyKeySpecValue in $LegacyKeySpecValues) {
|
||||
# NCRYPT_SILENT_FLAG = 0x00000040
|
||||
# Provide the "silent" flag to avoid interactive prompts that would block the execution.
|
||||
# This is especially relevant for Smart Card readers.
|
||||
$Flags = 0x00000040 + $FlagValue
|
||||
$Status = $script:Ncrypt::NCryptOpenKey($ProviderHandle, [ref] $KeyHandle, $ContainerName, $LegacyKeySpecValue, $Flags)
|
||||
# SCARD_E_NO_READERS_AVAILABLE = 0x8010002e
|
||||
# Access to a Smart Card reader is required, so this case should be ignored.
|
||||
if ($Status -eq 0x8010002e) { break }
|
||||
if ($KeyHandle -ne [UIntPtr]::Zero) { break }
|
||||
}
|
||||
if ($KeyHandle -ne [UIntPtr]::Zero) { break }
|
||||
}
|
||||
|
||||
if (($Status -ne 0) -and ($Status -ne 0x8010002e)) {
|
||||
# NTE_BAD_KEYSET = 0x80090016
|
||||
if ($Status -eq 0x80090016) {
|
||||
Write-Warning "Key '$($ProviderName)' -> '$($ContainerName)' not found. Administrator privileges might be required to open it."
|
||||
}
|
||||
else {
|
||||
Write-Warning "NCryptOpenKey('$($ContainerName)') - 0x$("{0:x8}" -f $Status)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($KeyHandle -ne [UIntPtr]::Zero) {
|
||||
$BufferSize = 0
|
||||
$Status = $script:Ncrypt::NCryptGetProperty($KeyHandle, $Property, [IntPtr]::Zero, 0, [ref] $BufferSize, 0x00000040)
|
||||
if ($Status -eq 0) {
|
||||
$PropertyValuePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BufferSize)
|
||||
$Status = $script:Ncrypt::NCryptGetProperty($KeyHandle, $Property, $PropertyValuePtr, $BufferSize, [ref] $BufferSize, 0x00000040)
|
||||
if ($Status -eq 0) {
|
||||
switch ($Property) {
|
||||
"Unique Name" {
|
||||
return [System.Runtime.InteropServices.Marshal]::PtrToStringUni($PropertyValuePtr)
|
||||
}
|
||||
default {
|
||||
throw "Unhandled property name: $($Property)"
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Warning "NCryptGetProperty('$($Property)') - 0x$("{0:x8}" -f $Status)"
|
||||
}
|
||||
|
||||
$null = [System.Runtime.InteropServices.Marshal]::FreeHGlobal($PropertyValuePtr)
|
||||
}
|
||||
else {
|
||||
Write-Warning "NCryptGetProperty('$($Property)') - 0x$("{0:x8}" -f $Status)"
|
||||
}
|
||||
|
||||
$null = $script:Ncrypt::NCryptFreeObject($KeyHandle)
|
||||
}
|
||||
|
||||
$null = $script:Ncrypt::NCryptFreeObject($ProviderHandle)
|
||||
}
|
||||
else {
|
||||
Write-Warning "NCryptOpenStorageProvider('$($ProviderName)') - 0x$("{0:x8}" -f $Status)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-CertificateExtendedKeyUsage {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -2731,21 +2851,22 @@ function Get-SerializedCertificateInformation {
|
||||
$CertInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($CertContext.CertInfo, [type] $script:CERT_INFO)
|
||||
|
||||
$ProviderName = Get-CertificateContextProperty -ContextPtr $CertContextPtr -PropId 2
|
||||
$ContainerName = Get-CertificateContextProperty -ContextPtr $CertContextPtr -PropId 2 -PropName "ContainerName"
|
||||
$CertificateHash = Get-CertificateContextProperty -ContextPtr $CertContextPtr -PropId 3
|
||||
$FriendlyName = Get-CertificateContextProperty -ContextPtr $CertContextPtr -PropId 11
|
||||
$Description = Get-CertificateContextProperty -ContextPtr $CertContextPtr -PropId 13
|
||||
|
||||
$ExtendedKeyUsage = Get-CertificateExtendedKeyUsage -ContextPtr $CertContextPtr
|
||||
$UniqueName = Get-CertificateNCryptProperty -ContextPtr $CertContextPtr -Property "Unique Name"
|
||||
|
||||
$Result = New-Object -TypeName PSObject
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "FriendlyName" -Value $FriendlyName
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Description" -Value $Description
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Subject" -Value $(Convert-CertNameBlob -EncodingType $CertContext.CertEncodingType -Blob $CertInfo.Subject)
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Issuer" -Value $(Convert-CertNameBlob -EncodingType $CertContext.CertEncodingType -Blob $CertInfo.Issuer)
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "FriendlyName" -Value $FriendlyName
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "NotBefore" -Value $(Convert-FiletimeToDatetime -FileTime $CertInfo.NotBefore)
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "NotAfter" -Value $(Convert-FiletimeToDatetime -FileTime $CertInfo.NotAfter)
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Version" -Value $($CertInfo.Version + 1)
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Provider" -Value $ProviderName
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Container" -Value $ContainerName
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "UniqueName" -Value $UniqueName
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "ExtendedKeyUsage" -Value $ExtendedKeyUsage
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "PublicKeyAlgorithm" -Value $(Convert-CertificateOid -Oid $CertInfo.SubjectPublicKeyInfo.Algorithm.ObjId | Select-Object -ExpandProperty Name)
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "SignatureAlgorithm" -Value $(Convert-CertificateOid -Oid $CertInfo.SignatureAlgorithm.ObjId | Select-Object -ExpandProperty Name)
|
||||
|
||||
@@ -55,6 +55,11 @@ $FunctionDefinitions = @(
|
||||
(New-Function kernel32 SearchPathW ([UInt32]) @([IntPtr], [String], [String], [UInt32], [System.Text.StringBuilder], [IntPtr]) ([Runtime.InteropServices.CallingConvention]::Winapi) ([Runtime.InteropServices.CharSet]::Unicode) -SetLastError -EntryPoint SearchPathW),
|
||||
(New-Function kernel32 GetProcAddress ([IntPtr]) @([IntPtr], [String]) ([Runtime.InteropServices.CallingConvention]::Winapi) ([Runtime.InteropServices.CharSet]::Ansi) -SetLastError -EntryPoint GetProcAddress),
|
||||
|
||||
(New-Function ncrypt NCryptOpenStorageProvider ([Int32]) @([UIntPtr].MakeByRefType(), [String], [UInt32]) -Charset Unicode -EntryPoint NCryptOpenStorageProvider),
|
||||
(New-Function ncrypt NCryptOpenKey ([Int32]) @([UIntPtr], [UIntPtr].MakeByRefType(), [String], [UInt32], [UInt32]) -Charset Unicode -EntryPoint NCryptOpenKey),
|
||||
(New-Function ncrypt NCryptGetProperty ([Int32]) @([UIntPtr], [String], [IntPtr], [UInt32], [UInt32].MakeByRefType(), [UInt32]) -Charset Unicode -EntryPoint NCryptGetProperty),
|
||||
(New-Function ncrypt NCryptFreeObject ([Int32]) @([UIntPtr]) -EntryPoint NCryptFreeObject),
|
||||
|
||||
(New-Function netapi32 NetGetJoinInformation ([UInt32]) @([IntPtr], [IntPtr].MakeByRefType(), $script:NETSETUP_JOIN_STATUS.MakeByRefType()) -Charset Unicode -EntryPoint NetGetJoinInformation),
|
||||
(New-Function netapi32 NetGetAadJoinInformation ([Int32]) @([String], [IntPtr].MakeByRefType()) -Charset Unicode -EntryPoint NetGetAadJoinInformation),
|
||||
(New-Function netapi32 NetUserEnum ([UInt32]) @([IntPtr], [UInt32], [UInt32], [IntPtr].MakeByRefType(), [UInt32], [UInt32].MakeByRefType(), [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) -EntryPoint NetUserEnum),
|
||||
@@ -106,6 +111,7 @@ $script:Crypt32 = $Types['crypt32']
|
||||
$script:Iphlpapi = $Types['iphlpapi']
|
||||
$script:FirewallApi = $Types['firewallapi']
|
||||
$script:Kernel32 = $Types['kernel32']
|
||||
$script:Ncrypt = $Types['ncrypt']
|
||||
$script:Netapi32 = $Types['netapi32']
|
||||
$script:Ntdll = $Types['ntdll']
|
||||
$script:Shell32 = $Types['shell32']
|
||||
|
||||
@@ -1437,6 +1437,64 @@ function Get-NetworkEndpoint {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-CertificatePrivateKeyFilePath {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get the path of a certificate's private key.
|
||||
|
||||
Author: @itm4n
|
||||
License: BSD 3-Clause
|
||||
|
||||
.DESCRIPTION
|
||||
This cmdlet attempts to retrieve the path of a certificate's private given its "Unique Name" property.
|
||||
|
||||
.PARAMETER UniqueName
|
||||
The "Unique Name" property of a certificate.
|
||||
#>
|
||||
|
||||
[OutputType([String])]
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[String] $UniqueName
|
||||
)
|
||||
|
||||
begin {
|
||||
$CandidatePaths = @(
|
||||
# Legacy CryptoAPI private key locations
|
||||
"$($env:APPDATA)\Microsoft\Crypto\RSA",
|
||||
"$($env:APPDATA)\Microsoft\Crypto\DSS",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\RSA\S-1-5-18",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\DSS\S-1-5-18",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\RSA\S-1-5-19",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\DSS\S-1-5-19",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\RSA\S-1-5-20",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\DSS\S-1-5-20",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\RSA\MachineKeys",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\DSS\MachineKeys",
|
||||
# Cryptography API next generation private key locations
|
||||
"$($env:APPDATA)\Microsoft\Crypto\Keys",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\SystemKeys",
|
||||
"$($env:windir)\ServiceProfiles\LocalService",
|
||||
"$($env:windir)\ServiceProfiles\NetworkService",
|
||||
"$($env:ALLUSERSPROFILE)\Application Data\Microsoft\Crypto\Keys"
|
||||
)
|
||||
}
|
||||
|
||||
process {
|
||||
$PrivateKeyFilePath = ""
|
||||
if (-not [String]::IsNullOrEmpty($UniqueName)) {
|
||||
foreach ($CandidatePath in $CandidatePaths) {
|
||||
$FileItem = Get-ChildItem -Path $CandidatePath -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $UniqueName }
|
||||
if ($FileItem) {
|
||||
$PrivateKeyFilePath = $FileItem.FullName
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return $PrivateKeyFilePath
|
||||
}
|
||||
}
|
||||
|
||||
function Get-PersonalCertificateInformation {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -1482,18 +1540,21 @@ function Get-PersonalCertificateInformation {
|
||||
Path : C:\Users\Admin\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates\B74755D3600AFFC32344F18BD854875F888981A8
|
||||
#>
|
||||
|
||||
[OutputType([Object[]])]
|
||||
[CmdletBinding()]
|
||||
param ()
|
||||
|
||||
process {
|
||||
|
||||
$Certificates = @()
|
||||
|
||||
Get-ChildItem -Path "registry::HKLM\SOFTWARE\Microsoft\SystemCertificates\MY\Certificates" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
|
||||
$Blob = $_ | Get-ItemProperty -Name "Blob" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Blob
|
||||
if ($null -ne $Blob) {
|
||||
$Result = Get-SerializedCertificateInformation -Blob $Blob
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $_.Name
|
||||
$Result
|
||||
$Certificates += $Result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1507,7 +1568,7 @@ function Get-PersonalCertificateInformation {
|
||||
if ($null -ne $Blob) {
|
||||
$Result = Get-SerializedCertificateInformation -Blob $Blob
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $_.Name
|
||||
$Result
|
||||
$Certificates += $Result
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1521,8 +1582,14 @@ function Get-PersonalCertificateInformation {
|
||||
$Blob = [System.IO.File]::ReadAllBytes($_.FullName)
|
||||
$Result = Get-SerializedCertificateInformation -Blob $Blob
|
||||
$Result | Add-Member -MemberType "NoteProperty" -Name "Path" -Value $_.FullName
|
||||
$Result
|
||||
$Certificates += $Result
|
||||
}
|
||||
}
|
||||
|
||||
$Certificates | ForEach-Object {
|
||||
$PrivateKeyFilePath = Get-CertificatePrivateKeyFilePath -UniqueName $_.UniqueName
|
||||
$_ | Add-Member -MemberType "NoteProperty" -Name "PrivateKeyFilePath" -Value $PrivateKeyFilePath
|
||||
$_
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user