呈现 GRUB2 BLS
2025年7月18日 | Alberto Planas | 无许可证
通过全盘加密保护openSUSE免受恶意设备攻击
openSUSE 现在有多种方法来配置全盘加密 (FDE) 安装。一种非常安全且简单(通过 YaST2)的方法是使用用户空间工具,正如我们多次描述的那样(例如此处、此处或此处)。此解决方案基于 systemd 工具集,如 systemd-cryptenroll、systemd-pcrlock 和 systemd-cryptsetup 等,并由内部 sdbootutil 脚本进行协调。
使用这种 systemd 方法的主要优点之一是可以集成多种身份验证方法。除了在引导时 initrd 阶段要求的传统密码外,我们现在还可以使用证书、TPM2 或 FIDO2 密钥来解锁系统。我们可以混合使用其中一些方法来创建多个 LUKS2 密钥槽,例如,使用 TPM2 以无人值守的方式解锁设备,并使用 FIDO2 密钥作为恢复机制。
坦白说,TPM2 及其 TPM2+PIN 变体对用户来说是最相关的。如其他帖子中所述,TPM2 是一个(有时是虚拟的)设备,可以使用称为 度量启动 (measured boot) 的机制来验证我们系统的健康状况。
其简短版本是,启动过程的每个阶段,从固件开始,都将在委托执行之前加载并“度量”下一个阶段。例如,这意味着在启动过程的最新阶段,UEFI 固件将从磁盘将引导加载程序加载到内存中。这可以是 shim、systemd-boot 或 grub2-bls。它将计算哈希值(通常是 SHA256),并命令 TPM2 对其中一个内部寄存器 (PCR) 执行“扩展”操作。
扩展是一种加密计算,非常容易计算,但不可能复制。它针对其中一个内部寄存器 (PCR) 进行,包括计算 PCR 旧值与我们正在度量的组件哈希值(再次为 SHA256)的哈希。这个新值将替换当前的 PCR 值,因为这是更改这些寄存器的唯一方法。安全特性在于,加密上不可能强制在这些 PCR 之一上写入所需值,但计算最终值却非常容易。
因此,这意味着如果引导链过程的所有组件都经过度量(UEFI 固件中的所有阶段、固件配置、引导加载程序、命令行、内核甚至 initrd),则最终的 PCR 值可以与我们的预期进行比较,并发现系统是否已使用已知良好的软件和配置进行引导,从而使我们能够立即知道引导链中的某个组件是否未经我们同意被黑客入侵或修改。
这是一个强大的特性,但更有趣的是,我们可以拥有只有在我们处于这些良好或识别状态之一时才能打开的秘密。例如,我们可以使用 TPM2 加密(密封)打开加密磁盘的密钥,同时使用一项策略,该策略将仅当且仅当我们使用相同的 TPM2 并且 PCR 值在预期列表中时才解密(解封)相同的密钥。这些策略可能非常复杂,可能包括额外的密码、证书或其他将在 TPM2 解封密钥之前进行验证的检查。
借助 systemd 工具,通过这种机制,如果系统处于健康状态,我们现在可以避免输入密码来解锁加密磁盘。健康意味着我们通过加密方式保证引导过程中使用的代码和配置是预期的,并且没有人输入 init=/bin/bash 到我们的内核命令行中,或者用易受攻击的内核或 initrd 替换了它们,例如。
通过我们在 openSUSE 中对此模型的集成,我们可以对系统进行更新,包括引导加载程序或内核,并且 sdbootutil 将透明地生成新的预期 PCR 值预测,这些值现在被认为是安全的。这意味着 TPM2 策略的更新,这将在下次引导时考虑在内,从而自动解锁将成功。如果出现问题且未达到预期的 PCR 值,用户将需要输入存储在不同 LUKS2 密钥槽中的密码来打开设备,以审计系统并验证它。
设计缺陷
如前所述使用 TPM2 明显提高了安全级别,但这并不是最终答案。安全始终是渐近逼近。
几年前,Windows BitLocker FDE 解决方案的物理攻击被描述。BitLocker 也以与之前描述类似的方式使用 TPM2,但没有使用加密会话与设备通信。截获 SPI 总线被证明可以恢复解锁磁盘的密码。systemd 从中吸取教训并早期使用了加密会话,但如果用于解封密钥的策略也要求用户输入 PIN 或密码,则此攻击也可以避免。现在,TPM2 只有在 PCR 处于正确状态并且提供的密码正确时才能解封秘密。应该注意的是,据我所知,SPI 嗅探可能适用于 Clevis,请参见此处。
但最近,第二次攻击被公开,它完全影响了原始提案,并且不需要原始攻击的复杂性。(披露:此攻击在几个月前也曾独立在内部描述过,并且很早就采取了一些反制措施)
该文章描述了如何通过检查 initrd 中用于挂载加密设备的 filesystem UUID 来进行攻击。此信息存储在 initrd 中的 /etc/crypttab 中,它将执行类似以下的操作
systemd-cryptsetup attach cr_root /dev/disk/by-uuid/$UUID ‘none’ ‘tpm2-device=auto’
如果在引导过程中使用了预期的固件、配置文件、内核和 initrd,那么 TPM2 的 PCR 寄存器将具有与解锁设备的策略匹配的值,并且密封密钥现在可以由 TPM2 解封,磁盘将被解锁,切换根目录将成功,并且引导过程将在 rootfs 中继续。
但是,如果原始驱动器被一个具有相同 UUID(毕竟这是一个公开信息)且也加密的驱动器替换了呢?那么 PCR 将处于相同的正确状态。请注意,在度量启动中,是前一阶段在委托执行之前度量下一阶段。然后 systemd-cryptsetup 将尝试使用 TPM2 使用 TPM2 成功解封的密钥来解锁设备,然后……当然会打开失败。恶意设备可能在 LUKS2 头部有一个 TPM2 密钥槽,但肯定不能用这个 TPM2 也不能用秘密密码打开。
在这种情况下,systemd-cryptsetup 将要求输入密码来解锁设备,攻击者可以输入一个密码,这次将打开恶意设备。切换根目录会发生,但现在它将在虚假的 rootfs 中继续启动过程,存储在那里的程序可以向 TPM2 提问,TPM2 仍然包含正确的 PCR 值。其中一个问题可以是使用当前策略解封密钥。这次(正如之前所做的那样),TPM2 将同意将密钥交给恶意程序。游戏结束。
当然,针对这种攻击有解决方案。
一种是再次使用 TPM2+PIN 而不是 TPM2,与嗅探攻击的解决方案相同。在这种情况下,第一次 systemd-cryptsetup 调用将失败,并且将要求输入密码来解锁设备。但现在恶意程序不能要求 TPM2 使用当前策略解封设备。PCR 值将匹配,但策略还要求输入真实用户知道的秘密 PIN 或密码,没有它,解封将失败,密钥将保持安全。
另一个解决方案是某种程度上使策略无效,在切换根目录之前扩展一些相关的 PCR,这样之后就不能再应用该策略了。这可以通过 systemd-cryptsetup 在 /etc/crypttab 中使用 measure-pcr=yes 自动完成。通过此选项,PCR15 将使用卷密钥进行扩展,卷密钥是一个只能通过了解一些设备密钥才能提取的秘密。为了使此解决方案生效,PCR15 需要包含在当前策略中,其预期值为 0x000..00,即默认值。一旦黑客提供的密码打开了恶意设备,PCR15 将自动扩展,并且该值将与 0x000..00 不同,从而在切换根目录之前使策略失效。
这是一个很好的解决方案,但对我们来说不适用。在日常情况下,用户需要更新系统,并且需要计算一个新策略来替换旧策略(例如,当内核更新时)。因为使用 systemd-pcrlock,策略存储在 TPM2 的一个非易失性 RAM 槽 (NVIndex) 中,我们需要以某种方式保护它,使其不能被其他进程替换。为此,systemd 将一个秘密密钥(恢复 PIN)存储在另一个 NVIndex 中,该 NVIndex 被相同的策略密封!如果密钥无法自动恢复,因为策略不再适用,那么将向用户询问恢复 PIN,如果策略总是无效,这将使更新过程变得有点不愉快。
最后,解决此问题的另一种方法是,如果我们检测到设备不是预期的设备,则停止引导过程。我们可以考虑一个新的服务,它存在于 initrd 中,并在切换根目录之前最后时刻执行,如果存储 rootfs 的设备不是预期的设备,它可以停止引导过程(可能会停止系统)。
为此,PCR15 仍然是一个很好的解决方案。它包含了一个秘密(卷密钥)的度量,该秘密只能由真实用户知道,并且攻击者无法复制。理想情况下,我们可以为 PCR15 创建一个预测,并使该服务将有效值与预期值进行比较,如果它们不同,则可以停止引导过程。
这就是 sdbootutil 的 measure-pcr-validator 服务所做的工作。sdbootutil 首先为所有在 initrd 期间打开的加密设备生成一个预测,并检查 /etc/crypttab 中是否存在正确的标签。为了访问卷密钥,该工具需要 root 密码,因此此预测仅在真正必要时(例如添加新的加密设备时)才更新。此预测由存储在主机中的私钥签名,作为额外的安全度量,但由于公钥也存储在 ESP 中,因此实际上并没有增加太多安全性。
一个额外的服务 (measure-pcr-generator) 将对加密设备的打开顺序进行一些排序,因为这个顺序对于产生单个可能的 PCR15 值至关重要。如果我们只有一个设备,度量顺序不相关,但如果我们有三个(例如 rootfs、/home 和 swap),我们可以有六个可能的且有效的不同 PCR15 值。
最后一步是 initrd 中的 dracut-pcr-signature 服务将从 ESP 导入预测、签名和公钥,以便 measure-pcr-validator 可以检查签名并比较 PCR 值。
就这样!
这种方法也与新的 systemd-validatefs 所做的工作有点相似,但它是在文件系统级别进行的。
类别: 博客
标签