FDE/LUKS: Insecure usage of TPM 🔐

FDE/LUKS: Insecure usage of TPM 🔐

July 30, 2024

Introduction

Encrypting the root partition is one way to protect user data if the computer is lost/stolen. To allow the system to boot, a decryption passphrase must be provided by the user but it comes with these two drawbacks:

  • In the case of a multi-user machine, the passphrase must be shared with all its users. However, the knowledge of the passphrase is sufficient to see any file or alter the system without booting this system.
  • If evil maid attacks are part of the threat model, the trustworthiness of the boot environment must be attested before the user is prompted for the decryption passphrase.

To solve these issues, the TPM chip can be used to automatically but conditionally unlock the drive at boot only if the trustworthiness of the boot environment is attested. In this condition, there is no need to prompt the user for a passphrase. This is achieved in combination with Secure Boot:

  • With Secure Boot enabled, the firmware verifies the first element in the boot chain, which in turn, verifies the next one, and so on. A chain of trust is established.
  • These verified elements can measure other elements which are not part of the chain of trust. These measurements are trusted because they come from the chain of trust.
  • The TPM can conditionally unseal a secret if the result of those measurements match the expected PCR values.

In 2024, many Linux distributions offer FDE at installation but lack a solid support of Verified/Measured Boot with TPM-based unlocking. Recent versions of systemd (v253+) provide the relevant tools to get things done securely such as systemd-ukify or systemd-pcrlock. Despite the availability of these tools, they are not quiet popular among users and OS distributors. I still often see people sharing insecure instruction to use TPM . The security threats discussed below are not specific to systemd initramfs. Gentoo, Arch Linux (when using busybox-style initrd) or Alpine Linux users may find this article relevant for them.

Before going further, I invite readers to take a look at this blog post, from Lennart Poettering, which details how it should be done, and why.

This article introduces security holes commonly found in insecure TPM guides/tutorials shared as references. The theoretical description itself should convince on the feasibility of these attacks. I invite readers to assess their setup against these vulnerabilities.

The vulnerabilities exposed below are caused by configuration error from users or OS distributors and are valid even in an environment where the TPM cannot be directly attacked. Other vulnerabilities related to the dFTP/fTPM implementation or the system’s firmware (TPM-fail, faulTPM, logoFAIL, bus sniffing…) are outside the scope of this post.

Secure usage of TPM

  1. The boot environment should be verified in chain starting from Secure Boot. Components which are not part of the verified chain but which can affect the boot behavior should be measured in their respective PCR, and those PCRs should be part of the sealing policy ;
  2. The boot environment should give limited control to the user, the user should never get the ability to run arbitrary commands from the boot environment, under any circumstance, even if the boot environment fails to load the requested OS ;
  3. Unsealing the secret once the boot environment has handed over control to an unverified OS should be prevented ;

In this post, the “boot environment” designates software included with the operating system which is responsible for looking for the system partition, unlocking it (if encrypted), mounting it, and starting the execution of the operating system itself. This is generally what lies in the ESP partition :

  • In the case of Windows: the “Windows Boot Manager” with all its files/resources ;
  • In the case of Linux: shim, grub, systemd-boot, the grub.cfg file, the entries file, the kernel, the initrd, the cmdline passed to the kernel, …

Not satisfying one of these 3 conditions is sufficient to make the break into the system.

Attack 1: The boot environment is not fully verified/measured

Description

While using the traditional boot path (shim → grub → kernel/initrd), some guides seal the LUKS secret against PCR from 0 to 7. PCR7 is a measurement of the Secure Boot configuration made by the firmware. By making PCR7 part of the sealing policy, the user effectively uses Secure Boot to prevent tampering of shim, GRUB or the kernel. However, the initrd and the kernel cmdline can be modified without being detected by Secure Boot. Indeed, these elements being host-specific, they cannot be signed by the OS distributor for each machine.

Taking advantage of this vulnerability

An attacker can alter these two components without affecting PCRs 0-7 to take control of the system in the early stages of the boot process:

  • By modifying the initrd, an attacker can trivially save/print the secret obtained from TPM somewhere, and a lot of other things.
  • By modifying the cmdline, an attacker can obtain a root shell by setting the init=/bin/bash boot option.

Mitigation

In the case of Fedora:

  • Secure Boot verifies the signature of the shim binary which is signed by Microsoft, and which embeds the distribution’s certificate
  • Shim verifies the signature of the GRUB binary based on the distribution certificate
  • GRUB verifies the signature of the kernel whose signature also matches the distribution’s certificate
  • GRUB measures read files in PCR9
  • The kernel measures the received command line and initrd(s) in PCR9.

The Secure Boot chain includes shim, the grub binary and the kernel. The initrd and the cmdline are not part of the verified chain (they are often host-specific), but they are measured by verified components (GRUB or kernel) in PCR8 and PCR9.

Therefore, by making PCR 7+8+9 part of the TPM policy, all boot components are either verified through Secure Boot (in chain) or are measured in PCRs which are part of the TPM policy.

For end users, UKI are probably easier to use because they contain everything (kernel, initrd and cmdline) in a single file which is verified by Secure Boot. In this case, PCR7 is sufficient to cover these components, and re-enrolling is not necessary after each update.

Attack 2: The boot environment can be tricked into booting a rogue operating system

Description

This attack is known for a long time and described by Microsoft in its BitLocker documentation. Despite that, almost all Linux attempts to use TPM are vulnerable to this one.

This attack is possible because:

  1. The boot chain and the TPM policy may entirely guard the boot environment (kernel, initrd and cmdline), the operating system is not part of the verified boot chain. Indeed, the job of an init script (like this one or this one) running in the initramfs is to find the root filesystem through the specified UUID or LABEL, it mounts it at /sysroot or /new_root and then it performs the exec switch_root ... command to boot the actual OS. The verified initramfs execute the next link of the chain without any kind of verification, terminating the chain of trust here. The operating system is not part of the chain of trust.
  2. The PCRs involved in the TPM sealing policy (PCR from 0 to 9) are not extended between the initramfs phase, from which the secret is expected to be released by TPM, and the moment when control is given to the operating system. As a consequence, ANY operating system can retrieve the secret for the same reason that the initramfs can.

Taking advantage of this vulnerability

A typical partitioning scheme looks like this:

/dev/sda1 -> /efi or /boot/efi
/dev/sda2 -> /boot  (optional)
/dev/sda3 -> contains the LUKS volume -> contains the ext4/btrfs/xfs root filesystem with the wanted UUID 

Because the root filesystem is not available prior to decryption, decryption should occur before the root filesystem can be found, mounted, and before switch_root can be executed.

But if an attacker overwrites the content of /dev/sda3 with a filesystem whose UUID is the same as the one specified in the cmdline, that filesystem can be found without even attempting the decryption. It will be mounted and the switch_root will occur.

In such an attack, the boot environment and the partition table remain untampered, PCR0-9 and Secure Boot will not detect the attack.

This was an example for a root filesystem specified through its fs UUID, the same logic applies to root filesystem specified by other means: Label, LVM logical name, btrfs subvolume, Discoverable Partitions Specification, …

Mitigation

This issue can be mitigated by making one of two statements above false.

To invalidate the first statement, the chain of trust could be extended to make the OS part of it, it can be accomplished with dm-verity. It can work nicely with immutable systems or OS images. Other solutions are possible but they also seem specific to some environment.

The second statement is easier to invalidate. The issue is caused because PCRs have the same value in the verified boot environment and in the unverified operating system. An additional PCR can be added to the TPM policy to differentiate the boot environment from the OS environment. This is the approach used by Microsoft Bitlocker. When sealing the secret in TPM, Bitlocker requires PCR11 to be zero. The boot environment extends PCR11 before handing over control to the operating system. The Bitlocker secret cannot be retrieved once past the boot environment, no matter if booting the original OS or a rogue OS.

Systemd measures different boot phases in PCR11, so it can also be used to seal secrets to specific boot phases. In contact with Bitlocker, PCR11 is not “0 in the boot environment, extends by 1 before transitioning in the OS”. When using UKI with systemd-stub, PCR11 is first measured with the sections of the UKI. Also, systemd does not seal against rigid values of PCR11, it uses signed policies instead, which permit updating the UKI (which results in different PCR11 measurements) without re-enrolling.

For non-systemd initramfs (Arch Linux, Gentoo and Alpine Linux…): the Bitlocker method could be implemented by adding scripts/hooks in the initrd.

Attack 3: The boot environment can be tricked into giving a shell to an attacker

Description

When failing to mount or transition into a root filesystem, the default behaviour of most initramfs is to drop a shell to the user for debugging purpose, or to let the user manually mount and run the switch_root command. This is the case for systemd-based initramfs.

Taking advantage of this vulnerability

The attacker could erase the content of the partition containing the root filesystem (but keep the partition table unchanged). The initramfs will not be able to find the root filesystem and after a time, the attacker will get an emergency shell. Because the boot environment is expected to be able to retrieve the LUKS secret from TPM, the attacker can use the binaries available in that boot environment to get the secret.

Mitigation

The emergency shell can be disabled, anyway, who needs it? For systemd initramfs, set the rd.shell=0 and rd.emergency=reboot options to the cmdline. If the system fails to boot, it can still be troubleshooted from a bootable USB stick.

Additional notes

In the addition of preventing these 3 configuration mistakes, I also recommend using a discrete TPM (rather than fTPM). Because systemd uses encrypted parameter, bus sniffing attacks are not a threat. A TPM PIN (which can be a password/passphrase) can be set for attended machines as it is an improvement over a PIN-less auto-unlocking. The TPM PIN is protected by TPM’s Dictionary Attack lockout mechanism, which make brute-force attacks impractical. It allows for a lower entropy password (easier to remember) without downgrading the security. Also, by using TPM with a PIN, the decryption secret is still conditionally released if the boot environment is not tampered. A user/attacker cannot access the data without going through a verified boot process and only on the configured computer. In other words, knowledge of the secret is not sufficient to access the data, in contrast with the LUKS passphrase.

Some people don’t like the idea of an attacker being able to boot their operating and getting the local authentication interface (GDM, TTY…). At this point, the security is in the hands of the operating system, it has nothing to do anymore with encryption or TPM. The same concern holds for a system which was manually unlocked and which can be locally or remotely accessed because SSH and local authentication is handled by PAM in both cases.

Finally, systemd-homed can be used to keep user directories encrypted even if the root system is automatically decrypted with TPM. After all, maybe the encryption of the root filesystem is not as useful as we think.

Conclusion

To summarize this article:

  • Secure Boot and the TPM policy must cover all components of the boot environment ;
  • The TPM policy should not allow retrieval of the LUKS secret after the boot environment, because the boot environment transfers full control to any operating system without any kind of verification ;
  • The emergency shell from the initrd should be disabled to prevent an attacker to run arbitrary commands from the secured boot environment ;
  • dTPM should be preferred over fTPM, assuming encrypted parameter is used ;
  • A TPM PIN should be set for attended machines ;
  • User data should be encrypted with systemd-homed.