Deniable Encryption
I remember years ago when I first made a bootable Kali thumb drive (‘BackTrack’ back then) with encrypted peristence there was an option to create a sort of ...
I remember years ago when I first made a bootable Kali thumb drive (‘BackTrack’ back then) with encrypted peristence there was an option to create a sort of “self destruct” where if you typed a special password the drive would be erased rather than unlocked. Kali still offers this package: cryptsetup-nuke-password.
For some reason this ability still fascinates me. The option to self desctruct your stuff at a moment’s notice. I’ve always used Luks encryption on Linux devices but this specific “nuke password” setup doesn’t seem to exist much outside of Kali.
I continued reading and doing some experimenting on an older laptop and eventually decided to set my Framework 13 laptop up with deniable encryption.
Ok, but what exactlyl does “deniable encryption” mean, and where’s my self descruct button? Wikipedia has a decent page describing what deniable encryption means. Basically for my context this means if anyone were to inspect the laptop or the drive itself there would be no evidence that the drive contained any data or even any encrypted data.
It turns out that with Luks encryption, by default, the first 16MB of a targeted partition for a Luks container is used to store the Luks header and the keyslots for unlocking the Luks container. The “nuke password” option from Kali operates by deleting the keys from the Luks header. This is unrecoverable and basically turns all of your encrypted data into pure entropy. However, the Luks header still exists and by its exxistence implies that there was encrypted data there.
Luks has the option to work with a detached header, whereby you create the header and store it somewhere else. This could be a file, or another disk partition. I experimented with putting both the boot partition and the Luks header on a removable USB drive and using the entire internal drive as a Luks container. With the USB drive removed there is no bootable partition, and the internal drive looks like random data from the first bit to the end.
Mostly for fun and because I like to “try” things in order to learn them. My threat model does not include needing to be able to “nuke” my laptop setup and have plausible deniability about whether or not there was ever any data on it. I’m also going to use this post as a bit of a guide for myself for installing Arch Linux. After the disk and Luks setup, the rest of the steps are pretty much the same.
I have mostly used Ubuntu since first getting in to Linux and had only really heard of Arch, but never seen it. It’s been over 2 years since I first tried Arch and I’ve come to really enjoy it as a distro. It makes you responsible for every aspect of the computer and puts you in control of it all, for better or for worse. I’ve learned a lot more about Linux in general and the Arch Wiki has become my go-to resource for pretty much all Linux questions.
The first step is to boot the Arch Linux ISO. It’s always best to go out and get the most current one from Arch as they publish a new version every month. I’ve been using Ventoy for a while to consolidate dozens of different ISOs on to one physical media. With a new version of the Arch ISO copied over to my Ventoy drive I’ll boot my Framework 13 and mash F12 to get the one-time boot menu. Select my Ventoy drive, and from the menu select the Arch ISO.
Once the Arch installer finishes booting the first step is to identify the disks we’ll be working with. In my case I’ve got an internal NVME drive and a Framework 250GB expansion card occupying one of the expansion card slots. This registers as a USB drive and will end up containing the boot partition as well as the detatched Luks header.
Using the lsblk command to list the current disks:
root@archiso ~ # lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 971.6M 1 loop /run/archiso/airootfs
sda 8:0 0 232.9G 0 disk
sdb 8:16 1 119.5G 0 disk
├─sdb1 8:17 1 119.5G 0 part
│ └─ventoy 253:0 0 1.4G 1 dm
└─sdb2 8:18 1 32M 0 part
nvme0n1 259:0 0 465.8G 0 disk
root@archiso ~ #In this example /dev/sda is the expansion drive (250GB) and /dev/nvme0n1 is the internal NVME drive. /dev/sdb is the USB drive for Ventoy that Arch is booted off of.
Start with the expansion/removable drive.
fdisk /dev/sdaThen g to start an empty GPT style partition table.
n to start a new partition, default number and first sector. For size set it to +1GB to make it a 1GB partition for boot.
n again to a new partition, default number (2) and first sector. For size set it to +17MB. This will be where the detached Luks header is stored.
n one last time to start a third partition with default first and last sector. It’s a 250GB drive, figure it’d be nice to be able to use that space for something.
Lastly, need to set the type for each partition with the t command, and choose the following values.
Partition 1 = '1' for an EFI partition.
Partition 2 = '23' for a "Linux root (x86-64)" type
Partition 3 = '23' for a "Linux root (x86-64)" type
w to write the changes to disk. At this point you could create a partition on the NVME drive, but I’ve found that you can omit this step and just target the whole drive with cryptsetup. In this way, there isn’t even a partition listed when looking at the drive.
Otherwise I’d create an empty GPT partition table on the NVME drive, create 1 partition with default first and last sectors and type 23.
Plausibly deniable encryption wouldn’t quite work if we took a blank disk and started a Luks container on it. The encrypted data only occupies so many blocks on the device and the rest is zeros. A sufficiently skilled adversary might be able to determine certain things about the computer based presence, size and location of the used and unused blocks. It’s going to take a bit of time but I’m going to run shred against the drive first to overwrite every bit position with random data.
root@archiso ~ # shred -n 1 /dev/nvme0n1 -vThis will make one pass of random data to the entire NVME drive with the -v providing verbose output.
Now the actual disk encryption can happen.
root@archiso ~ # cryptsetup luksFormat --use-random -h sha512 /dev/nvme0n1 --header=/dev/sda2
WARNING!
========
This will overwrite data on /dev/sda2 irrevocably.
Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda2:
Verify passphrase:
cryptsetup luksFormat --use-random -h sha512 /dev/nvme0n1 --header=/dev/sda2 24.77s user 0.35s system 130% cpu 19.216 totalHere the --use-random specifies which random number generator to use.
-h sha512 is the hash algorithm to be used and the iterations is left at default.
Then the target drive/partition location followed by the location for the detached header with --header=/dev/sda2.
Next we’ll open the encrypted container
root@archiso ~ # cryptsetup luksOpen /dev/nvme0n1 crypt --header=/dev/sda2
Enter passphrase for /dev/nvme0n1:
cryptsetup luksOpen /dev/nvme0n1 crypt --header=/dev/sda2 7.24s user 0.07s system 134% cpu 5.450 totalNow lsblk will confirm we have a new partition representing our unlocked Luks container.
root@archiso ~ # lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 971.6M 1 loop /run/archiso/airootfs
sda 8:0 0 232.9G 0 disk
├─sda1 8:1 0 1G 0 part
├─sda2 8:2 0 17M 0 part
└─sda3 8:3 0 231.9G 0 part
sdb 8:16 1 119.5G 0 disk
├─sdb1 8:17 1 119.5G 0 part
│ └─ventoy 253:0 0 1.4G 1 dm
└─sdb2 8:18 1 32M 0 part
nvme0n1 259:0 0 465.8G 0 disk
└─crypt 253:1 0 465.8G 0 crypt
root@archiso ~ #Next up we need file systems on these partitions so we can use them.
mkfs.fat -F32 /dev/sda1 to turn the 1GB partition on our external drive in to a FAT32 boot partition.
mkfs.ext4 /dev/mapper/crypt to turn our unlocked Luks container in to an Ext4 partition to hold our Arch Linux install.
Run these commands to mount the boot and root partitions within the live Arch ISO environment.
mount /dev/mapper/crypt /mnt
mkdir -p /mnt/boot
mount /dev/sda1 /mnt/boot
Parts of this will be slightly different due to the detached Luks header encryption setup, but we’ll carry on as if this is a normal install.
Use pacstrap to load some initial packages in to the new blank disk and then arch-chroot for further steps.
pacstrap /mnt base base-devel linux linux-firmware intel-ucode git vim sudo networkmanager
Generate fstab.
genfstab -U /mnt >> /mnt/etc/fstab
chroot into system.
` arch-chroot /mnt`
Create a user account for the new installation.
useradd -m greyed
passwd greyed
Add the user to the wheel group, which will determine who has access to sudo.
gpasswd -a greyed wheel
Run visudo, which will open a file where you can add wheel users to sudoers. Uncomment this relevant section:
## Uncomment to allow members of group wheel to execute any command
%wheel ALL=(ALL) ALL
If you want to increase the time period a sudo auth is good for and change the default editor for sudo now is a good time to add these to visudo as well.
Defaults timestamp_timeout=5
Defaults editor=/usr/bin/vim
Set time locale (choose a relevant locale):
ln -sf /usr/share/zoneinfo/US/Pacific /etc/localtime
Set clock:
hwclock --systohc
Uncomment en_US.UTF-8 UTF-8 en_US ISO-8859-1 or whatever localizations you need in /etc/locale.gen. Now run:
locale-gen
Create locale config file:
locale > /etc/locale.conf
Set the lang variable in the above file (Choose the language code that is relevant to you):
LANG=en_US.UTF-8
LANGUAGE=en_US
Add an hostname (any hostname of your choice as one line in the file. eg. myhostname):
vim /etc/hostname
Update /etc/hosts to contain (replace myhostname with the host name you used above):
127.0.1.1 myhostname.localdomain myhostname
Edit mkinitcpio.conf to add the right kernel hooks for unlocking the disk with a detached header
HOOKS=(base systemd autodetect modconf kms keyboard sd-vconsole plymouth sd-encrypt block filesystems fsck)
Note this has ‘plymouth’ included for a no splash boot, but it can be added later.
Then create the file /etc/crypttab.initramfs to specify that my detached header was on a separate drive.
crypt /dev/nvme0n1 none header=/dev/disk/by-partuuid/15f1a4e5-940b-4a14-b48d-c7af015597d6
where “crypt” is the name of the mapped decrypted Luks container. The next argument is the location of the Luks container, and then the header is specified using the partition UUID. The blkid command can return can return the details for a specific drive
blkid /dev/sda2
/dev/sda2: UUID="f818c840-81ae-4085-a7c1-d44bf62833f1" TYPE="crypto_LUKS" PARTUUID="15f1a4e5-940b-4a14-b48d-c7af015597d6"
Then you can use that PARTUUID in the crypttab file
Regenerate the initramfs:
mkinitcpio -p linux
Install a bootloader:
bootctl --path=/boot/ install
Create bootloader. Edit /boot/loader/loader.conf. Replace the file’s contents with:
default arch
timeout 0
Now to make the loader entry to support booting this detached Luks header setup file at /boot/loader/entries/arch.conf
title Arch Linux
version 6.18.2
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options cryptdevice=/dev/nvme0n1:crypt root=/dev/mapper/crypt rw quiet splash
The cryptdevice should normally be specified by partition UUID but because we targeted the entire NVME drive, there is no partition UUID. This is followed by the name of the opened Luks container, how it will be mapped and then the final quiet splash parts are for a Plymouth setup.
A final check of the /etc/fstab file to make sure it’s using the correct UUIDs
# Static information about the filesystems.
# See fstab(5) for details.
# <file system> <dir> <type> <options> <dump> <pass>
# /dev/mapper/crypt
UUID=a4d5cd3a-7edc-454d-8830-990c75df61b1 / ext4 rw,relatime 0 1
# /dev/sda1
PARTUUID=5fcda863-68ff-4e36-8955-4bf32e8bc667 /boot vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro 0 2
the first is the UUID, from blkid, of the /dev/mapper/crypt disk and then the boot partition referenced as a partition UUID since it might change otherwise at every boot. This also allows us to have a clone of the removable drive as a backup. More on this later.
Exit the arch-chroot
exit
and unmount the drives
umount -R /mnt
after first login, setup Network Manager
systemctl start NetworkManager
systemctl enable NetworkManager
Voila! that’s a basic Arch setup.
As discussed, without the Luks header the data on the encrypted drive is completely unrecoverable. If the external drive that our detatched Luks header was on failed, went missing, or got destroyed we would have no other option but to start from scratch. The Luks header can be backed up to a file at any time with a syntax like this.
$ sudo cryptsetup luksHeaderBackup /dev/nvme0n1 --header-backup-file headerbackup.img
Then that file can be stored safely somewher else.
Additionally every time the kernel is updated the boot partition will get a new initramfs. If we only cloned the Luks header to a new, similar drive this ‘backup’ drive would eventually end up being unbootable as the kernel version deviated from the rest of the system. Running dd every so often on a 250GB drive would be tedious, error prone, and time consuming.
I wrote a small bash script that simplifies this for me. I can plug in my other Framework 250GB expansion drive and run this script to keep a functional backup for myself.
#!/bin/bash
#for cloning the current boot disk to a target disk containing the same partitions
# Must be run as root
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root (sudo)." 1>&2
exit 1
fi
boot_dev=$(findmnt -no SOURCE /boot)
boot_disk=$(lsblk -no PKNAME "$boot_dev")
source_disk="/dev/$boot_disk"
echo "Source Disk: $source_disk"
UUID=$(blkid -s UUID -o value "$boot_dev")
duplicate=$(blkid -s UUID | grep "$UUID" | grep -v "^$boot_dev")
if [[ -n $duplicate ]]; then
echo "Target disk found:"
echo " $duplicate"
clone_dev=$(blkid -s UUID | grep "$UUID" | grep -v "^$boot_dev" | awk -F: '{print $1}')
clone_disk=$(lsblk -no PKNAME "$clone_dev")
target_disk="/dev/$clone_disk"
echo "Source: $source_disk"
echo "Target: $target_disk"
echo "---------------------"
read -p "Check kernel versions before cloning? (yes/no): " answer1
if [[ "$answer1" = "yes" ]]; then
mntdir="/mnt/targetboot"
mkdir $mntdir
mount "${target_disk}1" $mntdir
source_ver=$(lsinitcpio /boot/initramfs-linux.img -a | grep 'Kernel' | awk -F: '{print $2}')
target_ver=$(lsinitcpio "${mntdir}/initramfs-linux.img" -a | grep 'Kernel' | awk -F: '{print $2}')
umount $mntdir
rmdir $mntdir
echo "Source Kernel Version:$source_ver"
echo "Target Kernel Version:$target_ver"
echo "---------------------"
fi
if [[ "$source_disk" == "$target_disk" ]]; then
echo "Error: source and target disks are the same!"
exit 1
fi
read -p "This will completely overwrite $target_disk. Continue? (yes/no): " answer2
if [[ "$answer2" != "yes" ]]; then
echo "Clone aborted."
exit 1
fi
dd if="${source_disk}1" of="${target_disk}1" bs=4M status=progress
dd if="${source_disk}2" of="${target_disk}2" bs=4M status=progress
else
echo "Target disk NOT found."
fiI remember years ago when I first made a bootable Kali thumb drive (‘BackTrack’ back then) with encrypted peristence there was an option to create a sort of ...
Natural Language Passphrases Previously I talked about making small modules as an excuse to practice making tools and focus on making something that works we...
Size Doesn’t Matter When I made my first PowerShell module it was mostly an experiment to see if I could understand how they worked. It was simply tools.psm...
The more time I spend living in the CLI the more I appreciate learning and adopting shorthand for operations. In Powershell the aliases for Where-Object and...
I got the opportunity this week to attend the 2024 Powershell Summit in Bellevue Washington. If you have an opportunity to go to this, whether you’re brand ...
SecretManagement module is a Powershell module intended to make it easier to store and retrieve secrets. The secrets are stored in SecretManagement extens...
With more and more people working remotely there’s been a huge uptick in VPN usage. A lot of organizations have had to completely rethink some of their prev...
Hi all. Just wanted to provide a brief status update. It’s been a while since my last post and while I have been busy, and making frequent use of Powershel...
Getting GPS Coordinates From A Windows Machine Since 2020 a lot of organizations have ended up with a more distributed workforce than they previously had. T...
I was writing a new function today. Oddly enough I was actually re-writing a function today and hadn’t realized it. Let me explain. Story Time About a hal...
I’ve had an itch lately to do something with AES encryption in Powershell. I’ve tossed around the idea of building a password manager in Powershell, but I g...
“If all you have is a hammer, everything looks like a nail” - Abraham Maslow. I use a variation of this quote a lot, and I typically use it in jest, but it’s...
Introduction I’ve had some exposure to Microsoft Defender here and there, but I was in a class with Microsoft recently where they were going over some more f...
In my previous post I explained a bit about some of my justifications for logging in Powershell. My interest in logging has continued since then and I spent...
Everyone has a different use for Powershell. Some people use it for daily administrative tasks at work. Some people are hard at work developing Powershell m...
Early on when I first started using Powershell I was dealing with some firewall logs from a perimeter firewall. They were exported from a SIEM in CSV format...
One of the tools I feel like I’ve been using for years is Netstat. It exists in both Linux and Windows (with some differences) and has similar syntax. It’s ...
A coworker from a neighboring department had an interesting request one day. They wanted a scheduled task to run on a server. Through whatever mechanism the ...
When I first started getting in to Powershell I was working in an IT Security position and was sifting through a lot of “noise” in the SIEM alerts. The main...
“Hello World” and all that. What started as a small conversation turned in to an Idea that I couldn’t shake: I wanted a blog. But I didn’t want a WordPress...