Get-AuthenticodeSignature needs to use embedded signatures
Summary of the new feature / enhancement
TL;DR we need Get-AuthenticodeSignature to consider embedded (Authenticode) signatures even if the file is catalog signed. This is continuation of #8401 but with some new context.
Rationale
Windows ships with a boot manager signed by “Microsoft Windows Production PCA 2011” certificate. This certificate is associated with known security issues, and will eventually be revoked from UEFI, rendering the old boot manager unusable. Therefore, Windows updates distribute a new boot manager signed by “Windows UEFI CA 2023” certificate. This boot manager is meant to eventually replace the old boot manager on all machines.
Because there are a lot of moving parts in the process (OS updates, UEFI trust list, OEMs, 3rd party boot loaders and UEFI modules, etc.) the process of replacing the boot manager is convoluted and spans across multiple phases. Not everyone gets the new boot manager at once, and it's not always safe to have it replaced on your machine right away. Extreme caution is advised.
With that, users have a legitimate need to know how this or that boot file is signed.
Details
The issue is easy to illustrate since both old and new boot managers are present on a sufficiently up to date Windows machine.
- copy of the original boot manager is located at
C:\Windows\Boot\EFI\bootmgfw.efi - the updated boot manager is placed to
C:\Windows\Boot\EFI_EX\bootmgfw_EX.efi
(However, worth noting that the problem is not limited to just these two particular files, since other boot loaders and UEFI modules may be present on the machine which need to be assessed.)
The easiest way to check the signature is GUI. File properties in Windows Explorer do reflect the differences in signatures correctly, and it is currently documented as the only way a user can validate those signatures. (Scroll down the page and expand “Step 2: Install the PCA2023-signed boot manager” section to see that guidance.)
There are a lot of obvious reasons why GUI is not a great choice for this task. For instance, one cannot use Windows Explorer to peek into EFI system partition (ESP). Therefore, official documentation includes a multi-step process which includes assigning a temporary drive letter to that partition and using command line tools to copy the file over, and only after that using Explorer to observe the properties of the copy. This is highly inefficient, not scalable and error prone. Ideally, it should be one or two lines in PowerShell to validate the actual boot manager in use.
Unfortunately, the Windows boot files are dual-signed, and the catalog signature is not updated in the new boot manager. (Perhaps because UEFI only cares for the embedded signature.) Therefore, Get-AuthenticodeSignature is not very helpful here.
PS C:\Users\artemp> $old = Get-Item -Path 'C:\Windows\Boot\EFI\bootmgr.efi'
PS C:\Users\artemp> Get-AuthenticodeSignature -FilePath $old.FullName | Select-Object -ExpandProperty 'SignerCertificate' | Select-Object -ExpandProperty 'issuer'
CN=Microsoft Windows Production PCA 2011, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
PS C:\Users\artemp> $new = Get-Item -Path 'C:\Windows\Boot\EFI_EX\bootmgfw_EX.efi'
PS C:\Users\artemp> Get-AuthenticodeSignature -FilePath $new.FullName | Select-Object -ExpandProperty 'SignerCertificate' | Select-Object -ExpandProperty 'issuer'
CN=Microsoft Windows Production PCA 2011, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
PS C:\Users\artemp>
This behavior is understood, thanks to documentation and the discussion in #8401. However, it is suboptimal since it does not help us distinguish between the old and new boot managers. In other words, as a user, I cannot tell whether the effective boot manager on my machine was updated to use the new certificate, and it's safe to proceed revoking the old certificate.
Ultimately, we need a way for Get-AuthenticodeSignature to use embedded Authenticode signature even if catalog signature is also present.
Alternative solutions
One option is to use sigcheck.exe from Sysinternals. It does show the difference in Authenticode signatures as expected. But it's not very suitable for automation.
PS C:\Users\artemp> sigcheck.exe -noBanner -i $old.FullName | Select-Object -First 10
c:\windows\boot\efi\bootmgr.efi:
Verified: Signed
Link date: 11:21 PM 10/17/1964
Signing date: 12:50 AM 3/22/2024
Catalog: c:\windows\boot\efi\bootmgr.efi
Signers:
Microsoft Windows
Cert Status: Valid
Valid Usage: NT5 Crypto, Code Signing
Cert Issuer: Microsoft Windows Production PCA 2011
PS C:\Users\artemp> sigcheck.exe -noBanner -i $new.FullName | Select-Object -First 10
c:\windows\boot\efi_ex\bootmgfw_EX.efi:
Verified: Signed
Link date: 11:14 PM 12/10/1983
Signing date: 9:55 AM 3/29/2024
Catalog: c:\windows\boot\efi_ex\bootmgfw_EX.efi
Signers:
Microsoft Windows
Cert Status: Valid
Valid Usage: Code Signing, NT5 Crypto
Cert Issuer: Windows UEFI CA 2023
PS C:\Users\artemp>
We can also rely on workaround suggested in #8401. Currently it looks like a best option, but it's not very obvious.
PS C:\Users\artemp> $certificate = [System.Security.Cryptography.X509Certificates.X509Certificate]::CreateFromSignedFile( $old.FullName )
PS C:\Users\artemp> [System.Security.Cryptography.X509Certificates.X509Certificate2]::new( $certificate ) | Select-Object -ExpandProperty 'issuer'
CN=Microsoft Windows Production PCA 2011, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
PS C:\Users\artemp> $certificate = [System.Security.Cryptography.X509Certificates.X509Certificate]::CreateFromSignedFile( $new.FullName )
PS C:\Users\artemp> [System.Security.Cryptography.X509Certificates.X509Certificate2]::new( $certificate ) | Select-Object -ExpandProperty 'issuer'
CN=Windows UEFI CA 2023, O=Microsoft Corporation, C=US
PS C:\Users\artemp>
Finally, it looks like the community has realized the shortcoming of Get-AuthenticodeSignature because alternative solutions emerged, such as Get-DigitalSignature script. Unfortunately, it has a large dependency which is difficult to audit and therefore it's not very suitable for use in security-cautious production environments.
Just an FYI be careful with using CreateFromSignedFile as it does a very naive scan the raw bytes for a PKCS7 byte sequence rather than actually extracting the signature based on the file extension.
While Get-AuthenticodeSignature does have a -Content and -SourcePathOrExtension parameter to rovide the raw bytes of the file manually it is currently hardcoded to use the PowerShell SIP provider so this won't work for exe/efi files :(
Adding in a switch parameter to bypass the API that checks the catalog files should be a pretty easy thing to implement. https://github.com/PowerShell/PowerShell/blob/da1ca4a7266e2c9e43f96a38aeea8092ace71cca/src/System.Management.Automation/security/Authenticode.cs#L282-L286
I need to write some tests for CI but I've tested https://github.com/PowerShell/PowerShell/pull/23821 with the example file in https://github.com/PowerShell/PowerShell/issues/8401 and it returns both the catalogue cert by default and the embedded cert with -EmbeddedSignature set
My efi file in your example still seems to be the same signature so I cannot test that example myself.
We could add new test C# project, sign the exe and use in the test.
Or reuse test\tools\TestExe
It should be possible to just use a new ps1 file. Even generating an exe at runtime isn’t that hard to do. The hard bit is figuring out how to setup the catalog file so it is used in a normal check. I’ve found some APIs I just need to try them out.
@pronichkin Thanks for reporting this, saved me a considerable amount of frustration.
Thanks. +1