Linux boot security – a discussion
Hi. As some know now, I have an article about encrypting and protecting Linux – while using TPM to prevent the need to enter the encryption passphrase every time the system boots. This is one of the most popular article in this blog, and for a very good reason – people want their computers (especially portable devices) to be secure and well protected against data theft (let’s assume that by trivial means. I tend to believe that if the NSA or any country-class intelligence service is targeting you, your data will be compromised, so don’t get targeted by countries please), but they want to be the least-bothered with entering and re-entering security credentials along the way. Eating the cake while leaving it whole, if you would like.
Using TPM (which is welded into the motherboard of the computer) to keep the encryption passphrase is a great solution – you get to have an encrypted disk, but don’t have to reuse the passphrase on every reboot. Also – you can rest assured that when/if your hard drive is pulled out of the computer, there is nothing to do with it, because the key is kept on the motherboard.
However – this is not enough, and my article does not cover the scenario where a malicious user pulls out our hard drive and puts a different hard drive instead of it. The current process is of keeping the passphrase directly in the TPM2 chip, not as an SSL challenge/response mechanism, but just as a storage for the key. As such – any user with physical access to the machine from within the OS level can obtain it – even by replacing the disk and pulling the key out, or even worse – by patching the boot sequence (which partially resided on /boot, which is not encrypted) and by replacing initramfs/initrd or by modifying GRUB boot flow, and just asking the TPM nicely for the key. So – our system is not that secure. At least not as much as we aim at. I have mentioned that in the disk encryption article, but yet to have provided a solution.
The reason I have not provided a solution has to do with how Canonical (Ubuntu) take a look at the chain of trust, and the different way I look at it, and to bring this issue up front, I am writing this article, which is not really a technical, but more of a discussion of options. If some of my readers respond and enlighten me – I would be grateful for that, as I am truly looking for a feasible solution to my Ubuntu – this or the next one.
A chain of trust is a workflow where every step is trusted by the previous step, on one side, and can trust the next step. My desirable chain of trust in this case would look like this:
- The BIOS is locked and cannot be modified without credentials
- The BIOS allows booting only from a single disk device, and only a specific boot file
- Using SecureBoot, only the bootable binary (GRUB2 in our case) – a signed and un-tempered file, can be called
- GRUB2 EFI executable has its configuration embedded, so that you cannot modify it, add additional parameters, or stop its boot process mid-way.
- GRUB2 is password protected, and allows only a single entry (default) without additional parameters, without authentication.
- GRUB2 loader trusts the kernel it boots to be one pre-signed by some trusted authority.
- GRUB2 loader trusts initramfs/initrd as it is signed in a manner GRUB2 understands and respects, aka – it is unmodified
- Initramfs/initrd is calling the TPM2 to obtain the key, and by doing so, does two things: Opens the disk encryption by using the key, and trusting the BIOS to be un-tempered with, because resetting the BIOS password would reset the TPM2 contents as well (this is good for us).
- The system continues boot correctly.
You can notice that every step of the boot process has the current step trusted by the previous one, and has a trust mechanism towards the next step. This can work by using self-signed certificate to sign GRUB2 EFI executable, and saving this certificate (and its verification!) in the SecureBoot section of the BIOS, overriding any ‘common’ certificates the BIOS knows of. Then – using grub-standalone instead of the regular GRUB2, which means that GRUB configuration and modules are embedded into a single, signed, executable – we prevent injection of GRUB modules (drivers) or change of parameters. Also – GRUB is password protected against non-default boot. Following that, GRUB trusts the kernel, which is signed, and initramfs/initrd, which is also signed (GRUB used to trust GPG signed files, however, and this is the source of this discussion, Canonical changed some things there, and in my opinion – not for the better). And from here and onwards – the boot process works fine.
This chain of trust seems reasonable. It will prevent boot sequence intervention, and will make sure that the unencrypted part of the partition gets files replaced. You cannot load any different GRUB (even if replaced) due to BIOS trust issues, and you cannot change the configuration, kernel of initramfs/initrd, and therefore – you are bounded to this predefined flow.
This does not follow the ‘trusted computing’ flow. Trusted computing is a mechanism that is supposed to prevent the kernel (and the hardware assisting the process) loading unsigned kernel drivers, and it should be enforced by the TPM/SecureBoot as well. This is nice and pretty, but the vulnerable areas in the trusted computing areas are with the loaders. You might try to inject kernel drivers or malicious code when running under privileged context – when GRUB gets called in.
To mitigate this, Ubuntu enforced (starting with grub version 2.04-1ubuntu26.2) using SHIM, which is a pre-loader mechanism. The chain of trust works slightly differently here:
- BIOS trusts SHIM, using Microsoft certificate
- SHIM is signed using Microsoft certificate trusts a different set of certificates – self-signed or Canonical certificates. SHIM is pre-GRUB loader.
- SHIM trusts GRUB EFI binary, which is signed by Canonical
- GRUB trusts the kernel using Canonical certificate (all stock kernels are provided pre-signed by Canonical), or custom kernel (and modules) by self-signed certificate, but it needs to be used for every kernel used, and every module used by this kernel.
- GRUB trusts initramfs/initrd by whatever means (no trust is checked by default there)
The problem with this flow is that it has external sources in the chain of trust, where different components might get replaced or patched. The problems, as I see them (and I would like to get a different opinion about it, so please feel free to share) are:
- SHIM can be replaced with another EFI binary trusted by Microsoft (like MS Windows loader, or some other tool used by whatever 3rd party I do not know). This will result in skipping the entire trust chain, into a different flow, of which we have no control.
- SHIM might be able to get compiled with a self-signed certificate (which then be entered into the BIOS SecureBoot as a trusted certificate), however – this process will prevent easy/automated deployment of SHIM, or will require re-compiling it on every update.
- Using grub-standalone (reviewed above) will require signing it with Canonical certificate (we cannot do this), or with our self-signed. If we use self-signed, we will remove Canonical certificate from SHIM, but then we will have to use our certificate to re-sign the kernel, or we add our self-signed certificate to SHIM, but then, our locked-down GRUB can be replaced with a general GRUB2, signed by Canonical (the default) and worked around the lockdown easily.
- Now GRUB2 (also grub-standalone, as this is part of the change introduced in version 2.04-1ubuntu26.2) will not trust GPG signature for the kernel (allowing us to avoid re-signing the kernel using SSL, but adding a GPG signature, thus – easier automation of processes and trust chains), but only the SSL, which has to be known to SHIM.
- By default – initramfs signature is not checked. At this stage with all the chain breaks along the way – it doesn’t matter much now. An attacker can just hook-in anywhere before, and pull our keys out already, using off-the-shelf security loaders of GRUB EFI binaries.
I was able to recomplie the latest GRUB packages for Ubuntu omitting these three patches, without code modifications, to regain the original security flow I presented above (pending tests. Not done yet):
- 0096-linuxefi-fail-kernel-validation-without-shim-protoco.patch
- 0099-chainloader-Avoid-a-double-free-when-validation-fail.patch
- ubuntu-linuxefi-arm64.patch
Is it to be trusted? I do not know. Is it safe “enough”? Maybe. Would I have to continue maintaining a non-official GRUB package? Maybe. How do I maintain it for Ubuntu 22.04? It seems Canonical has their mind set on their flow, which allows for trusted computing – I cannot ignore that – but fails, to my understanding, to provide “good enough” physical access protection when some of the disk is not encrypted (to be exact: /boot and /boot/efi).
You can take a look at the discussion in this Ubuntu bug thread. You can also get the workflow of using SecureBoot and SHIM in Ubuntu in their official wiki here. I did not discuss MokManager, as a tool used to add certificates to SecureBoot or SHIM since I need to dig deeper into this, but to my belief – it only eases management for some of the tasks which might be set easier from within a working OS.
I would love to hear where my analysing of Canonical workflow is wrong, and where their offered security works better than my (previous) workflow – in regards to physical access to data protection.
I will also link this article to the encrypted disk with TPM2 article, in hope it will draw more interaction with this post.
Thanks for reading this far.
Thanks for this! A very interesting read and I look forward to hearing how your testing goes. In my experience, it is very difficult to account for all possible attack vectors. For example I hadn’t thought of swapping hard drives to pull the key stored in the TPM2 device as a way to unlock the original drive. Have you actually tried doing this? When Ubuntu uses MOKManager to setup secureboot, it asks for a password that also has to be entered into the BIOS when enrolling the key for SHIM. I don’t know if that is a one time verification, or if it something is being signed as part of that process so another drive would have to have used the same password during it’s original setup. If it is the latter, as long as secureboot is enabled that should reasonably prevent against a hard drive swap attack.
I do not trust SHIM. You can swap SHIM with a different EFI executable (signed by MS) and go around the entire boot protection, or you can swap GRUB (and not use grub-standalone, in this case) with a Canonical-signed one (which is legit, by default) and still get around the limitations. No – the entire process must have zero trust to anyone external. I could have lived well with certificate-based trust of the kernel, but Ubuntu’s SHIM does not allow trusting a single file using a specific signature, and excluding any other file from this trust. If the signer is trusted, the signed files are trusted. So – either I sign SHIM with my own certificate, clean SecureBoot from MS certificates and makes my certificate the only valid one, and then make sure SHIM trusts only my certificate, and then I sign using this certificate (my own, mind you) GRUB (as a standalone, for security purposes), kernel (which is signed, originally, by Canonical, so I am required to remove their signature), initramfs, and then all the kernel modules – this is the single workflow using SHIM which will protect my system from a local-attacker, and remain usable (or else the kernel will not load, or its modules will not load. Bad either way).
I want to trust my GRUB, and I have the means (using GPG) for it to trust the files it calls (grub.cfg, kernel, initramfs). This is all, and Ubuntu won’t allow this…
Etzion
Instead of self signing everything, perhaps another option would be to use the TPM PCRs to better secure the encryption key, similar to what BitLocker does. My understanding is that the PCRs contain a hash of everything about the booting process, such as the firmware settings, boot order, boot loader contents (e.g shim, grub), kernel and initrd (https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers). The TPM can be configured such that it will only release the encryption key if the PCRs contain specific values (or rather, exactly the same as it booted before). This helps protect against the data theft case, because if someone swaps out the drive, or mucks with the boot loader, then that will perturb the PCRs and the TPM will refuse to release they key, keeping the data safe. The downside to this is that whenever you do legitimately update the boot loader kernel, etc, you will need to reconfigure your secret in TPM to use new PCR values.
This is a very interesting document you have lined to. The more interesting part is this: https://wiki.archlinux.org/title/Trusted_Platform_Module#Clevis
Using ‘clevis’ with specific PCR values (need to set which are the trusted ones) could solve the main problem here – you cannot both temper with the system and get the key. I will test it. It is interesting.