Skip to content

Instantly share code, notes, and snippets.

@jacky9813
Last active May 15, 2026 06:38
Show Gist options
  • Select an option

  • Save jacky9813/c342653ce09c7f1c9791e352c0381a48 to your computer and use it in GitHub Desktop.

Select an option

Save jacky9813/c342653ce09c7f1c9791e352c0381a48 to your computer and use it in GitHub Desktop.
Reading EFI_VARIABLE_AUTHENTICATION_2 for updating Secure Boot variables
<#
PowerShell - Read EFI_VARIABLE_AUTHENTICATION_2 binary
EFI_VARIABLE_AUTHENTICATION_2 are typically used for updating
Secure Boot variables like db, dbx and KEK. Examples can be
found in GitHub microsoft/secureboot_objects/PostSignedObjects
Download this script and import it:
Invoke-WebRequest -Uri "SCRIPT_URI" -OutFile "read_auth.ps1"
Import-Module .\read_auth.ps1
To enable debugging message output:
$DebugPreference = "Continue"
Add Microsoft 2023 KEK certificate: (Tested in Windows Server 2022 on KVM)
Invoke-WebRequest -OutFile "KEKUpdate_RedHat_PK1.bin" -Uri "https://github.com/microsoft/secureboot_objects/raw/refs/heads/main/PostSignedObjects/KEK/RedHat/KEKUpdate_RedHat_PK1.bin"
$auth_data = [EfiVariableAuthentication2]::new("KEKUpdate_RedHat_PK1.bin")
# Use write helper function
$auth_data.WriteEfiVar("KEK")
# Or manually
$auth_data.ExportPayload("KEKUpdate_RedHat_PK1.esl")
$auth_data.AuthInfo.ExportCertData("KEKUpdate_RedHat_PK1.p7s")
Set-SecureBootUEFI -Name KEK -AppendWrite -Time $auth_data.FormatTime() -ContentFilePath "KEKUpdate_RedHat_PK1.esl" -SignedFilePath "KEKUpdate_RedHat_PK1.p7s"
Update to latest DBX:
Invoke-WebRequest -Uri "https://github.com/microsoft/secureboot_objects/raw/refs/heads/main/PostSignedObjects/DBX/amd64/DBXUpdate.bin" -OutFile "DBXUpdate.bin"
$auth_data = [EfiVariableAuthentication2]::new("DBXUpdate.bin")
# Use write helper function
$auth_data.WriteEfiVar("dbx")
# Or manually
$auth_data.ExportPayload("DBXUpdate.esl")
$auth_data.AuthInfo.ExportCertData("DBXUpdate.p7s")
Set-SecureBootUEFI -Name DBX -AppendWrite -Time $auth_data.FormatTime() -ContentFilePath "DBXUpdate.esl" -SignedFilePath "DBXUpdate.p7s"
Add Microsoft 2023 CA certificates to DB:
Invoke-WebRequest -OutFile "DBUpdate2024.bin" -Uri "https://github.com/microsoft/secureboot_objects/raw/refs/heads/main/PostSignedObjects/Optional/DB/amd64/DBUpdate2024.bin"
Invoke-WebRequest -OutFile "DBUpdate3P2023.bin" -Uri "https://github.com/microsoft/secureboot_objects/raw/refs/heads/main/PostSignedObjects/Optional/DB/amd64/DBUpdate3P2023.bin"
Invoke-WebRequest -OutFile "DBUpdateOROM2023.bin" -Uri "https://github.com/microsoft/secureboot_objects/raw/refs/heads/main/PostSignedObjects/Optional/DB/amd64/DBUpdateOROM2023.bin"
$auth_db_win2023 = [EfiVariableAuthentication2]::new("DBUpdate2024.bin")
$auth_db_win2023.WriteEfiVar("db")
$auth_db_ms2023 = [EfiVariableAuthentication2]::new("DBUpdate3P2023.bin")
$auth_db_ms2023.WriteEfiVar("db")
$auth_db_orom2023 = [EfiVariableAuthentication2]::new("DBUpdateOROM2023.bin")
$auth_db_orom2023.WriteEfiVar("db")
To list signatures in auth file payload:
$auth_data = [EfiVariableAuthentication2]::new("KEKUpdate_RedHat_PK1.bin")
$signatures = [EfiSignatureListFile]::new($auth_data.Payload).Entries
To list signatures from an ESL file:
$signatures = [EfiSignatureListFile]::new("filename.esl").Entries
To list db signatures from UEFI:
$signatures = [EfiSignatureListFile]::new((Get-SecureBootUEFI -Name db)).Entries
To export all certificates from db:
$signatures = [EfiSignatureListFile]::new((Get-SecureBootUEFI -Name db))
$signatures.ExportAllSignatures("db-")
#>
# Mapped to WIN_CERT_TYPE_*
enum WinCertType {
PkcsSignedData = 0x0002
EfiPkcs1_15 = 0xEF0
EfiGuid = 0xEF1
}
# Mapped to struct WIN_CERTIFICATE
class WinCertificate {
[UInt32]$dwLength
[UInt16]$wRevision
[WinCertType]$wCertificateType
WinCertificate([Byte[]]$InputBytes) {
$this.Decode($InputBytes, 0)
}
WinCertificate([Byte[]]$InputBytes, [Int]$Offset) {
$this.Decode($InputBytes, $Offset)
}
[void] Decode([Byte[]]$InputBytes, [Int]$Offset) {
$this.dwLength = [System.BitConverter]::ToUInt32($InputBytes, $Offset)
$this.wRevision = [System.BitConverter]::ToUInt16($InputBytes, $Offset+4)
if ($this.wRevision -ne 0x0200) {
Write-Warning "Expecting wRevision = 512, got $($this.wRevision.ToString())"
}
$certType = [System.BitConverter]::ToUInt16($InputBytes, $Offset+6)
$this.wCertificateType = [WinCertType]($certType)
}
}
# Mapped to struct WIN_CERTIFICATE_UEFI_GUID
class WinCertificateUefiGuid {
[WinCertificate]$Hdr
[System.Guid]$CertType
[Byte[]]$CertData
WinCertificateUefiGuid([Byte[]]$InputBytes) {
$this.Decode($InputBytes, 0)
}
WinCertificateUefiGuid([Byte[]]$InputBytes, [Int]$Offset) {
$this.Decode($InputBytes, $Offset)
}
[void] Decode([Byte[]]$InputBytes, [Int]$Offset) {
$this.Hdr = [WinCertificate]::new($InputBytes, $Offset)
$rawGuid = [Byte[]]$InputBytes[($Offset+8)..($Offset+23)]
$this.CertType = [System.Guid]::new($rawGuid)
if ($this.CertType -eq [System.Guid]::new("4aafd29d-68df-49ee-8aa9-347d375665a7")) {
Write-Debug "[$($this.GetType().Name)] CertType is EFI_CERT_TYPE_PKCS7_GUID"
if (($InputBytes[$Offset+24] -ne 0x30) -or ($InputBytes[$Offset+25] -ne 0x82)) {
Write-Warning "The CertData does not seem like a valid PKCS#7 SignedData"
}
# Can't use System.BitConverter here as ASN.1 uses different byte order
# Reading ASN.1 SEQUENCE LENGTH + SEQUENCE HEADER
$dataLength = ($InputBytes[($Offset+26)] * 256) + $InputBytes[($Offset+27)] + 4
Write-Debug "[$($this.GetType().Name)] PKCS#7 CertData has length of $($dataLength.ToString()) bytes"
$this.CertData = $InputBytes[($Offset+24)..($Offset+24+$dataLength-1)]
} else {
Write-Warning "Unknown CertType GUID: $($this.CertType.ToString())"
$this.CertData = $InputBytes[($Offset+24)..($Offset+$this.Hdr.dwLength-1)]
}
}
[void] ExportCertData([String]$Path) {
if ([System.IO.Path]::IsPathRooted($Path)) {
$truePath = $Path
} else {
$truePath = [System.IO.Path]::GetFullPath("$($PWD.Path)\$($Path)")
}
Write-Debug "[$($this.GetType().Name)] Writing CertData to file $($truePath)"
[System.IO.File]::WriteAllBytes($truePath, $this.CertData)
}
}
# Mapped to struct EFI_SIGNATURE_DATA
class EfiSignatureData {
[Guid]$SignatureOwner
[System.Object]$SignatureData
EfiSignatureData([Guid]$SignatureType, [Byte[]]$InputBytes){
$this.Decode($SignatureType, $InputBytes)
}
EfiSignatureData([Guid]$SignatureType, [Byte[]]$InputBytes, [Int]$Offset, [Int]$Length){
$this.Decode($SignatureType, $InputBytes[$Offset..($Offset + $Length - 1)])
}
[void] Decode([Guid]$SignatureType, [Byte[]]$InputBytes){
$this.SignatureOwner = [System.Guid]::new([Byte[]]$InputBytes[0..15])
$rawSignatureData = [byte[]]$InputBytes[16..($InputBytes.length-1)]
if ($SignatureType -eq [Guid]::new("a5c059a1-94e4-4aa7-87b5-ab155c2bf072")) {
Write-Debug "[$($this.GetType().Name)] Signature is X.509 Certificate"
$this.SignatureData = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(
$rawSignatureData
)
} elseif ($SignatureType -eq [Guid]::new("c1c41626-504c-4092-aca9-41f936934328")) {
Write-Debug "[$($this.GetType().Name)] Signature is SHA256 Digest"
$this.SignatureData = $rawSignatureData
} else {
Write-Warning "[$($this.GetType().Name)] Unknown SignatureType GUID $($SignatureType.ToString())"
$this.SignatureData = $rawSignatureData
}
}
[void] ExportSignatureData([String]$Path) {
if ([System.IO.Path]::IsPathRooted($Path)) {
$truePath = $Path
} else {
$truePath = [System.IO.Path]::GetFullPath("$($PWD.Path)\$($Path)")
}
Write-Debug "[$($this.GetType().Name)] Writing signature data to $($truePath)"
if ($this.SignatureData -is [System.Security.Cryptography.X509Certificates.X509Certificate2]){
$rawData = $this.SignatureData.RawData
} elseif ($this.SignatureData -is [byte[]]) {
$rawData = $this.SignatureData
} else {
throw "Unsupported SignatureData type: $($this.SignatureData.GetType().FullName)"
}
[System.IO.File]::WriteAllBytes($truePath, $rawData)
}
}
# Mapped to struct EFI_SIGNATURE_LIST
class EfiSignatureList {
[Guid]$SignatureType
[UInt32]$SignatureListSize
[UInt32]$SignatureHeaderSize
[UInt32]$SignatureSize
[Byte[]]$SignatureHeader
[EfiSignatureData[]]$Signatures
EfiSignatureList() {}
EfiSignatureList([String]$Path) {
$fullPath = (Get-Item $Path).FullName
Write-Debug "[$($this.GetType().Name)] Reading file $($fullPath)"
$rawContent = [System.IO.File]::ReadAllBytes($fullPath)
$this.Decode($rawContent)
}
EfiSignatureList([Byte[]]$InputBytes){
$this.Decode($InputBytes)
}
EfiSignatureList([Byte[]]$InputBytes, [Int]$Offset){
$this.Decode($InputBytes[$Offset..($Offset+$InputBytes.length-1)])
}
EfiSignatureList([Byte[]]$InputBytes, [Int]$Offset, [Int]$Length){
$this.Decode($InputBytes[$Offset..($Offset+$Length-1)])
}
[Boolean] IsX509SignatureType(){
return $this.SignatureType -eq [Guid]::new("a5c059a1-94e4-4aa7-87b5-ab155c2bf072")
}
[UInt32] Decode([Byte[]]$InputBytes){
$this.SignatureType = [System.Guid]::new([Byte[]]$InputBytes[0..15])
$this.SignatureListSize = [System.BitConverter]::ToUInt32($InputBytes, 16)
$this.SignatureHeaderSize = [System.BitConverter]::ToUInt32($InputBytes, 20)
$this.SignatureSize = [System.BitConverter]::ToUInt32($InputBytes, 24)
if ($this.SignatureHeaderSize -gt 0){
$this.SignatureHeader = [Byte[]]$InputBytes[28..(28+$this.SignatureHeaderSize-1)]
} else {
$this.SignatureHeader = [Byte[]]::new(0)
}
$offset = 28 + $this.SignatureHeaderSize
$this.Signatures = [EfiSignatureData[]]::new(0)
while ($offset -lt $this.SignatureListSize) {
Write-Debug "[$($this.GetType().Name)] Loading Signature (offset=$($offset.ToString()), length=$($this.SignatureSize.ToString()))"
$sig = [EfiSignatureData]::new($this.SignatureType, $InputBytes, $offset, $this.SignatureSize)
$this.Signatures += [EfiSignatureData[]]($sig)
$offset += $this.SignatureSize
}
Write-Debug "[$($this.GetType().Name)] Loaded $($this.Signatures.Length.ToString()) signatures"
return [UInt32]$offset
}
[UInt32] Decode([Byte[]]$InputBytes, [Int]$Offset){
return $this.Decode($InputBytes[$Offset..($InputBytes.length-1)])
}
}
# Each ESL can contain multiple EFI Signature Lists
class EfiSignatureListFile{
[EfiSignatureList[]]$Entries
EfiSignatureListFile([String]$Path){
$fullPath = (Get-Item $Path).FullName
Write-Debug "[$($this.GetType().Name)] Reading file $($fullPath)"
$rawContent = [System.IO.File]::ReadAllBytes($fullPath)
$this.Decode($rawContent)
}
EfiSignatureListFile([Byte[]]$InputBytes){
$this.Decode($InputBytes)
}
EfiSignatureListFile([object]$ObjInput){
if ($ObjInput.Bytes -eq $null) {
throw "$($ObjInput.GetType().FullName) is not a supported type"
}
Write-Debug "[$($this.GetType().Name)] Reading from $($ObjInput.GetType().FullName) instance"
$this.Decode($ObjInput.Bytes)
}
[void] Decode([Byte[]]$InputBytes){
$this.Entries = [EfiSignatureList[]]::new(0)
[UInt32]$offset = 0
while ($offset -lt $InputBytes.length) {
Write-Debug "[$($this.GetType().Name)] Reading List #$($this.Entries.Length) (offset=$($offset))"
$newEntry = [EfiSignatureList]::new()
$offset += $newEntry.Decode($InputBytes, $offset)
$this.Entries += [EfiSignatureList[]]($newEntry)
}
}
[Int] ExportAllSignatures([String]$PathPrefix, [String]$FileExt) {
$idx = [Int]0
foreach ($entry in $this.Entries) {
foreach ($signature_data in $entry.Signatures) {
$fileName = "$($PathPrefix)$($idx.ToString())$($FileExt)"
$signature_data.ExportSignatureData($fileName)
$idx += 1
}
}
return $idx
}
[Int] ExportAllSignatures([String]$PathPrefix) {
return $this.ExportAllSignatures($PathPrefix, ".der")
}
}
# Mapped to EFI_VARIABLE_AUTHENTICATION_2
class EfiVariableAuthentication2 {
[System.DateTimeOffset]$Timestamp
[WinCertificateUefiGuid]$AuthInfo
[Byte[]]$Payload
EfiVariableAuthentication2([Byte[]]$InputBytes) {
$this.Decode($InputBytes)
}
EfiVariableAuthentication2([String]$Path) {
$fullPath = (Get-Item $Path).FullName
Write-Debug "[$($this.GetType().Name)] Reading file $($fullPath)"
$rawContent = [System.IO.File]::ReadAllBytes($fullPath)
$this.Decode($rawContent)
}
[void] Decode([Byte[]]$InputBytes) {
# Write-Host "loaded content length: $($InputBytes.length.ToString())"
Function Read-EfiTime {
param (
[Parameter(Mandatory)][Byte[]]$InputBytes,
[Int]$Offset = 0
)
# Write-Host "Input Bytes count: $($InputBytes.length.ToString())"
# Write-Host "Reading EFI time at offset $($Offset.ToString())"
# $InputBytes[$Offset+7] => padding
$timeOffset = [System.TimeSpan]::new(
0, [System.BitConverter]::ToInt16($InputBytes, $Offset + 12), 0)
# $timeDaylight = $InputBytes[14] # not used here
# $InputBytes[15] => padding
$dateTime = [System.DateTimeOffset]::new(
[System.BitConverter]::ToUInt16($InputBytes, $Offset), # year
$InputBytes[$Offset+2], # month
$InputBytes[$Offset+3], # date
$InputBytes[$Offset+4], # hour
$InputBytes[$Offset+5], # minute
$InputBytes[$Offset+6], # seconds
[Math]::Round(
# nanos, but System.DateTime only accept millies
[System.BitConverter]::ToUInt32($InputBytes, $Offset + 8) / 1000000,
[MidpointRounding]::AwayFromZero
),
$timeOffset
)
return $dateTime
}
$this.Timestamp = Read-EfiTime -InputBytes $InputBytes -Offset 0
$this.AuthInfo = [WinCertificateUefiGuid]::new($InputBytes, 16)
Write-Debug "[$($this.GetType().Name)] AuthInfo has length of $($this.AuthInfo.Hdr.dwLength.ToString()) bytes"
$payloadStart = 40 + $this.AuthInfo.CertData.Length
$this.Payload = $InputBytes[$payloadStart..($InputBytes.length-1)]
Write-Debug "[$($this.GetType().Name)] Payload has length of $($this.Payload.Length.ToString()) bytes"
}
[void] ExportPayload([String]$Path) {
if ([System.IO.Path]::IsPathRooted($Path)) {
$truePath = $Path
} else {
$truePath = [System.IO.Path]::GetFullPath("$($PWD.Path)\$($Path)")
}
Write-Debug "[$($this.GetType().Name)] Writing $($this.Payload.Length) bytes payload to $($truePath)"
[System.IO.File]::WriteAllBytes($truePath, $this.Payload)
}
[String] FormatTime() {
return $this.Timestamp.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ")
}
[void] WriteEfiVar([String]$Name, [Boolean]$AppendWrite) {
$signedFilePath = ".\.signedFile-$($Name).p7s"
Write-Debug "[$($this.GetType().Name)] Set-SecureBootUEFI requires CertData being a standalone file. Exporting to $($signedFilePath)"
$this.AuthInfo.ExportCertData($signedFilePath)
if ($AppendWrite) {
Write-Debug "[$($this.GetType().Name)] Appending to $($Name)"
$result = Set-SecureBootUEFI -Name $Name -AppendWrite -Time $this.FormatTime() -Content $this.Payload -SignedFilePath $signedFilePath
} else {
Write-Debug "[$($this.GetType().Name)] Replacing $($Name)"
$result = Set-SecureBootUEFI -Name $Name -Time $this.FormatTime() -Content $this.Payload -SignedFilePath $signedFilePath
}
Write-Debug "[$($this.GetType().Name)] Removing $($signedFilePath)"
Remove-Item $signedFilePath
}
WriteEfiVar([String]$Name){
$this.WriteEfiVar($Name, $true)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment