Skip to content

Instantly share code, notes, and snippets.

@tothi
Last active April 25, 2024 19:13
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tothi/c7fdaaca3d61b7e3298863ada358fc1e to your computer and use it in GitHub Desktop.
Save tothi/c7fdaaca3d61b7e3298863ada358fc1e to your computer and use it in GitHub Desktop.
Full Disk Encryption with unattended auto-unlock using TPM2; hardened with Secure Boot on Kali

Full Disk Encryption w/ TPM2 on Kali

Short HOWTO about setting up Full Disk Encryption with unattended auto-unlock using TPM2 on Kali.

Useful for rogue devices (auto-connecting to C2), headless pentest boxes, etc. storing confidential information but lacking physical security.

NOTE: In order to maintain integrity and protect the encryption key, hardening the boot process with Secure Boot is a must. For making Secure Boot work (without messing up the default UEFI keys stored in the hardware), the Microsoft-signed UEFI shim loader is used (available in the Kali repo) which is able to load securely an arbitrary ELF image as 2nd stage signed by the so-called custom "Machine Owner Key" (also stored in EFI vars but it won't overwrite the default PK, KEK and db(x)).

Configuration steps

0. Recompile kernel with module signing facility enabled.

First of all, for making Secure Boot work, signed kernel modules are needed. Unfortunately, at the time of writing this howto, Kali ships with not only kernel modules without signature by default, but also the official kernel image binary in the repo does not include the module signing facility. Thus, recompiling a properly configured kernel is needed. Because this takes time, it is recommended to do it prior to anything else, preferably on a computer with more processing power (does not need to be compiled on the target).

Steps to recompile the kernel:

Might need some dependencies:

sudo apt install build-essential libncurses5-dev fakeroot xz-utils bc kmod cpio flex libelf-dev libssl-dev dwarves bison pahole

Install the source:

sudo apt install linux-source
mkdir ~/kernel
cd ~/kernel
tar xJvf /usr/src/linux-source-*.tar.xz
cd linux-source-*

Reconfigure kernel with module signing facility enabled (use the official default configuration as a base, and only add "Enable loadable module support / Module signature verification (CONFIG_MODULE_SIG=y)". "Automatically sign all modules (CONFIG_MODULE_SIG_ALL=y)" does not harm, if not enabled, we have to sign all modules manually (so enable it):

cp /boot/config-6.1.0-kali9-amd64 .config
make menuconfig

Build (and grab some coffee :) ):

make clean
make bindeb-pkg LOCALVERSION=-signed KDEB_PKGVERSION=$(make kernelversion)-1 -j4

1. Assuming we have a pre-installed EFI Kali system with Full Disk Encryption applied.

By using the official installer, it is recommended to use the partitioning scheme: "Guided - use entire disk and set up encrypted LVM". Also make sure it'll be an EFI system (boot the installer using EFI and it'll install in EFI mode).

During install, set up a secure passphrase for protecting the encryption key, it'll be used as a backup method besides TPM.

2. Here are some useful enumeration commands related to the setup process.

Check EFI keys:

efi-readvar

Enum Secure Boot state:

bootctl status
mokutil --sb-state

Show EFI boot entries:

efibootmgr

3. Install the kernel supporting module signatures (and preferably signed) compiled in step 0.

After moving the deb package to the target (if compiled elsewhere):

dpkg -i linux-image-6.1.27-signed_6.1.27-1_amd64.deb

Check signature (of a random module):

modinfo /lib/modules/6.1.0*-amd64/kernel/fs/ext4/ext4.ko

Alternatively, if not signing the kernel modules automatically, you may sign it manually (but the CONFIG_MODULE_SIG=y is mandatory even if using manual signing):

sudo apt install linux-headers-amd64
sudo find /lib/modules/6.1.0*-amd64/kernel -type f -name "*.ko" -exec /lib/modules/6.1.0*-amd64/build/scripts/sign-file sha1 /etc/efi-keys/module-signing.key /etc/efi-keys/module-signing.pem {} \;

4. Generate the Machine Owner Key (used for signing the 2nd stage combined kernel+initrd ELF image).

mkdir /etc/efi-keys
cd /etc/efi-keys

openssl req -x509 -new -nodes -newkey rsa:2048 -keyout MOK.key -sha256 -days 3650 -out MOK.pem -subj '/CN=Machine Owner Key/'
openssl x509 -in MOK.pem -outform DER -out MOK.der

5. Install the shim (signed) loader.

It is already installed (by default), but let's set it as default:

cp /usr/lib/shim/shimx64.efi.signed /boot/efi/EFI/BOOT/BOOTX64.EFI
cp /usr/lib/shim/mmx64.efi.signed /boot/efi/EFI/BOOT/mmx64.efi

Add shim to EFI boot list (labelled as "shim"):

efibootmgr -c -d /dev/sda1 -l '\EFI\BOOT\BOOTX64.EFI' -L shim

6. Create the kernel+initrd combined ELF image using Dracut.

Install the Dracut initrd builder package:

apt install dracut

Configure Dracut (with UEFI, sign the image with our MOK, add cmdline for LVM LUKS root and enable TPM2):

cat > /etc/dracut.conf.d/local.conf <<<EOF
hostonly=yes
uefi=yes

uefi_secureboot_cert=/etc/efi-keys/MOK.pem
uefi_secureboot_key=/etc/efi-keys/MOK.key

kernel_cmdline="rd.luks.uuid=<UUID> rd.luks rd.lvm root=/dev/kali-vg/root rootflags=rw,relatime"

add_dracutmodules+=" tpm2-tss "
EOF

Replace "<UUID>" above with the UUID of the crypted LUKS volume. Enum it with blkid.

And build the image:

dracut -f --regenerate-all

Copy the generated combined ELF image to its destination (shim loader loads the file named grubx64.efi by default):

cp /boot/efi/EFI/Linux/linux*.efi /boot/efi/EFI/BOOT/grubx64.efi

View and check signatures (shim: BOOTX64.EFI and 2nd stage: grubx64.efi):

sbverify --list /boot/efi/EFI/BOOT/BOOTX64.EFI
sbverify --list /boot/efi/EFI/BOOT/grubx64.efi

7. Import the Machine Owner Key certificate to shim:

Prepare import:

mokutil --import /etc/efi-keys/MOK.der

Check:

mokutil --list-new

Reboot and complete the import process ("MOK management / Enroll MOK" in the boot menu after reboot)

reboot

Verify after enrolling and rebooting:

mokutil --list-enrolled

8. Turn on Secure Boot (in BIOS).

After turning it on, check:

bootctl status

VMware Player note (if testing it with VM), in the vmx file:

firmware = "efi"
uefi.secureBoot.enabled = "TRUE"

9. Add decryption key for the encypted (root) LVM volume in TPM.

Make sure tpm2-tools is installed:

apt install tpm2-tools

Check if tpm2-tss is enabled in Dracut (it was activated above, but let's double check), in /etc/dracut.conf.d/local.conf:

add_dracutmodules+=" tpm2-tss "

Check LUKS properties:

cryptsetup luksDump /dev/sda3

Enroll the encryption key for unlocking in TPM (usually device list is only one device, so auto-select is working):

systemd-cryptenroll --tpm2-device=list
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+4+7 /dev/sda3

Used PCR banks 0+4+7 for protecting the unlock key:

  • PCR0: core firmware
  • PCR4: authenticode hash of executed binary (unified kernel image) + systemd-stub
  • PCR7: EFI secure vars (including MokListRT) + SecureBoot state

This means that if any of these are changed, the unlock key will be invalid, so giving protection against boot process altering attacks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment