![如何验证签名的 UEFI 二进制文件?](https://linux22.com/image/188616/%E5%A6%82%E4%BD%95%E9%AA%8C%E8%AF%81%E7%AD%BE%E5%90%8D%E7%9A%84%20UEFI%20%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%96%87%E4%BB%B6%EF%BC%9F.png)
我试图理解为什么安全启动可以在我的机器上运行。它在 UEFI 配置中启用,一切正常启动,并mokutil --sb-state
显示SecureBoot enabled
.但我认为不应该。据我所知,它应该是这样工作的:
- 我打开机器,它开始运行其内置固件。
- 固件验证垫片(由 Microsoft 签名)并跳转到其中。
- 填充程序验证 GRUB(由发行版签名)并跳转到其中。
- GRUB 验证其配置、initrd(由我签名?)、内核(由发行版签名)并引导内核。
- 内核在加载模块(由发行版签名)之前验证它们。
当我尝试手动验证这一点时,唯一具有有效签名的是垫片。 GRUB 和内核映像包含签名,但验证失败。 initrd 和 grub 配置未签名。然而系统启动并不能激发信心。
我用过mokutil
,,sbverify
和osslsigncode
。也许这些不是正确的工具?也许安全启动实际上被禁用了?我会尽力提供尽可能多的信息。
这是我所做的
首先,我使用导出了所有可信公钥mokutil
并显示了发行者和主题以供参考。
# mokutil --export --pk
# mokutil --export --kek
# mokutil --export --db
# mokutil --export
# for derfile in ./*.der; do
> openssl x509 -inform der -outform pem -in "$derfile" -out "${derfile}.pem"
> done
# rename 's/.der.pem$/.pem/' ./*.der.pem
# for pemfile in ./*.pem; do
> echo "$pemfile"
> openssl x509 -inform pem -in "$pemfile" -text | egrep 'CN ?='
> done
./DB-0001.pem
Issuer: C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = Microsoft Root Certificate Authority 2010
Subject: C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = Microsoft Windows Production PCA 2011
./DB-0002.pem
Issuer: C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = Microsoft Corporation Third Party Marketplace Root
Subject: C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = Microsoft Corporation UEFI CA 2011
./DB-0003.pem
Issuer: C = US, O = HP Inc., CN = HP Inc. DB Key 2016 CA
Subject: CN = HP UEFI Secure Boot DB 2017, OU = CODE-SIGN, C = US, O = HP Inc.
./KEK-0001.pem
Issuer: C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = Microsoft Corporation Third Party Marketplace Root
Subject: C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = Microsoft Corporation KEK CA 2011
./KEK-0002.pem
Issuer: C = US, O = HP Inc., CN = HP Inc. KEK 2016 CA
Subject: CN = HP UEFI Secure Boot KEK 2017, OU = CODE-SIGN, C = US, O = HP Inc.
./MOK-0001.pem
Issuer: CN = strib.tech
Subject: CN = strib.tech
./MOK-0002.pem
Issuer: CN = Debian Secure Boot CA
Subject: CN = Debian Secure Boot CA
./PK-0001.pem
Issuer: C = US, O = HP Inc., CN = HP Inc. PK 2016 CA
Subject: CN = HP UEFI Secure Boot PK 2017, OU = CODE-SIGN, C = US, O = HP Inc.
然后,我尝试使用所有这些公钥验证所有相关文件sbverify
。
# for imgfile in /boot/vmlinuz-* /boot/initrd.img-* /boot/efi/EFI/devuan/*; do
> for pemfile in ./*.pem; do
> sbverify --cert "$pemfile" "$imgfile" &> /dev/null && echo "$imgfile is signed with $pemfile"
> done
> done
/boot/efi/EFI/devuan/shimx64.efi is signed with ./DB-0002.pem
哎呀。让我们检查一下原因。我将在这里显示 GRUB 映像,但 shim 帮助程序模块和内核映像的情况是相同的。
# sbverify --list /boot/efi/EFI/devuan/grubx64.efi
signature 1
image signature issuers:
- /CN=Debian Secure Boot CA
image signature certificates:
- subject: /CN=Debian Secure Boot Signer 2020
issuer: /CN=Debian Secure Boot CA
它声称由 签名的密钥签名MOK-0002.pem
。也许我没有那把钥匙?事实证明我知道,它只是与签名一起隐藏在同一个文件中。所以我把它拿出来了。
# osslsigncode extract-signature -pem -in /boot/efi/EFI/devuan/grubx64.efi -out signature.pem
Succeeded
# openssl pkcs7 -in signature.pem -print_certs -out intermediate.pem
# openssl x509 -in intermediate.pem -text | grep CN
Issuer: CN = Debian Secure Boot CA
Subject: CN = Debian Secure Boot Signer 2020
不幸的是,验证仍然失败。如果我以任何顺序将intermediate.pem
和放在MOK-0002.pem
一起,或者如果我之前删除了垃圾,也会发生同样的情况BEGIN CERTIFICATE
。
# sbverify --cert intermediate.pem /boot/efi/EFI/devuan/grubx64.efi
Signature verification failed
osslsigncode 还有一个验证选项,并提供比 sbverify 更多的信息。
# osslsigncode verify -in /boot/efi/EFI/devuan/grubx64.efi -CAfile both.pem -verbose
Current PE checksum : 00183046
Calculated PE checksum: 00183046
Signature Index: 0 (Primary Signature)
Message digest algorithm : SHA256
Current message digest : A9BDE7F125657CB9E30974274B8B2762B2AA7CB86D8DE9386A645A49077AAF8E
Calculated message digest : A9BDE7F125657CB9E30974274B8B2762B2AA7CB86D8DE9386A645A49077AAF8E
Signer's certificate:
Signer #0:
Subject: /CN=Debian Secure Boot Signer 2020
Issuer : /CN=Debian Secure Boot CA
Serial : B55EB3B9
Certificate expiration date:
notBefore : Jul 21 15:52:54 2020 GMT
notAfter : Jul 21 15:52:54 2030 GMT
Number of certificates: 1
Signer #0:
Subject: /CN=Debian Secure Boot Signer 2020
Issuer : /CN=Debian Secure Boot CA
Serial : B55EB3B9
Certificate expiration date:
notBefore : Jul 21 15:52:54 2020 GMT
notAfter : Jul 21 15:52:54 2030 GMT
CAfile: both.pem
TSA's certificates file: /usr/lib/ssl/certs/ca-bundle.crt
Timestamp is not available
Unsupported Signer's certificate purpose XKU_CODE_SIGN
Signature verification: failed
Number of verified signatures: 1
Failed
也许垫片会忽略所有其他多余的字段,例如证书用途。我对此没问题。但要相信这一点,我需要在修改后的 GRUB 映像中看到不同的错误。问题是当我到处覆盖几个字节时,osslsigncode 只是段错误。
部分解决方案
我知道可以将 initrd 嵌入到内核映像中,并将 GRUB 配置嵌入到 GRUB 映像中,然后我可以使用自己的密钥 进行签名MOK-0001.pem
。所以如果我能验证这些签名,我基本上就完成了。验证内核模块固然很棒,但由于它们是从经过验证的 initrd 或 LUKS 分区加载的,所以这并不是什么大问题。
内核模块怎么样
内核模块根据modinfo
(sbverify
并且osslsigncode
不识别文件格式)进行签名。
# modinfo iwlwifi | egrep -A12 '^sig'
sig_id: PKCS#7
signer: Debian Secure Boot CA
sig_key: B5:5E:B3:B9
sig_hashalgo: sha256
signature: A1:B8:D5:51:C0:C2:80:AD:01:7A:6E:E9:E9:96:E8:BB:4F:53:7F:09:
1A:62:04:8F:5A:62:97:0C:37:0D:98:17:C4:30:E3:39:9D:4B:FB:7E:
64:03:69:CA:A6:37:59:8C:F9:05:66:FB:A5:F1:66:88:8B:11:75:05:
0C:52:8B:A4:44:D7:70:BD:02:9F:29:1C:87:F4:37:15:6F:83:C8:D7:
2B:BC:CE:F9:9E:D4:D2:23:5A:26:48:A1:C7:43:A7:74:0C:6A:9C:18:
12:A7:D5:93:F2:D8:0D:9D:28:6F:34:CD:88:79:A1:26:32:D3:9F:BF:
8F:B7:91:CA:AF:1E:36:96:B0:F8:FA:B9:05:80:A5:E3:5B:5C:BB:A8:
5B:EC:5D:B6:97:B8:8F:00:99:62:69:19:C5:58:F4:13:D9:3C:5A:C0:
9F:08:49:43:CF:30:DB:CD:8E:9B:6F:65:21:5E:64:68:5B:33:26:93:
38:F5:DA:40:B1:F4:5F:E9:A0:E3:C3:10:C6:0C:EA:A1:42:BF:A8:DD:
59:88:32:E9:7B:4A:2B:0D:89:9D:6F:E7:CE:0D:A6:E9:28:D8:3F:C8:
B1:4F:FC:DD:35:22:D6:23:C4:86:C3:78:1F:5D:E8:2E:FD:3E:D4:D8:
CC:30:1D:BF:5A:2C:C2:2E:83:E8:63:CB:1F:D3:7D:FD
modinfo 是否确实验证了这一点,或者只是打印它发现的我不知道的任何签名。我尝试更改 .ko 文件中的随机字节以获取失败,但从错误消息中不清楚是否是由于签名,或者只是一些 ELF 校验和。
# modinfo iwlwifi.ko
filename: /root/iwlwifi.ko
modinfo: ERROR: could not get modinfo from 'iwlwifi': Invalid argument
更新
我修改了osslsigncode
源代码,现在可以验证签名了。问题是它明确禁止 XKU_CODE_SIGN 作为证书用途。
--- osslsigncode-2.1.orig/osslsigncode.c
+++ osslsigncode-2.1/osslsigncode.c
@@ -2521,12 +2521,6 @@ static int verify_authenticode(SIGNATURE
goto out;
}
- /* check extended key usage flag XKU_CODE_SIGN */
- if (!(X509_get_extended_key_usage(signer) & XKU_CODE_SIGN)) {
- printf("Unsupported Signer's certificate purpose XKU_CODE_SIGN\n");
- goto out;
- }
-
verok = 1; /* OK */
out:
if (!verok)
借助这个新版本,我可以成功验证 shim 帮助程序模块、GRUB 和内核。现在我可以grub-mkstandalone
使用嵌入式公钥和最小配置来 创建 GRUB 映像启用签名检查并加载主要配置。这涵盖了 GRUB 读取的所有文件,并使用良好的旧 GPG 分离签名。这意味着不需要使用CONFIG_INITRAMFS_SOURCE
(那会非常烦人)。
Artem S. Tashkinov 的回答澄清了内核模块的情况。我想仅此而已。我将创建一个可启动的签名 USB 记忆棒并将密钥保存在那里,我将需要它来更新 GRUB(不应该那么频繁)。
答案1
这不是一个答案,而是一个我想添加的评论,因为我相信它是相关的。
安全启动EFI机制仅有的验证正在运行的二进制文件和库(例如可执行代码)签名,它不会也无法验证除此之外的任何内容,因为这会使启动变得不可能。任何操作系统都需要知道从哪里启动以及这些信息的来源用户可修改的未签名配置文件,例如,/boot/efi/EFI/Microsoft/Boot/BCD
对于 Windows 和/boot/efi/EFI/$distro/grub.cfg
Linux 发行版。
这种情况有点可怕,因为在几乎所有现有发行版都使用的 Linux 中,initrd
攻击者可以修改 initrd 并完全绕过任何保护,以防他对您的设备进行硬件访问。如果你担心你应该有仅有的设备上的加密存储并从加密的 USB 闪存驱动器启动。
最后,再次抱歉没有回答您的问题,此命令将告诉您系统是否在安全启动模式下运行:
dmesg | grep -i secure\ boot\ enabled
最后,modinfo
是否不是验证签名,它只显示它们。内核绝对做到了这一点 - 这是我使用以下命令更改了一些随机字节后的结果hexedit
:
# modprobe orangefs
modprobe: ERROR: could not insert 'orangefs': Key was rejected by service