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)).
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
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.
Check EFI keys:
efi-readvar
Enum Secure Boot state:
bootctl status
mokutil --sb-state
Show EFI boot entries:
efibootmgr
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
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
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
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
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"
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.