We have covered how to create secure “throw-away hack boxes” using the Raspberry Pi before, but we thought it was time to go back and take a look at the process again. With all the new Raspberry Pi models and Kali changes from when we last covered this, we found the old process was in need of some updating.

Kali Logo

As a review, what we are trying to accomplish is to create a standalone “leave behind” device that, when discovered, does not make it easy to figure out what you were doing. So we use the LUKS full disk encryption along with the LUKS Nuke capability to put this together. If you have a Raspberry Pi 3 Model B+, or really any other model or similar device, feel free to use the instructions below to set up your own secure system. This updated process is based on our previous documentation, and updated with some community suggestions.

Overview of the process Before we dive into the tech of what we are going to try to accomplish, let’s take a quick look at our goals on setting up our Raspberry Pi 3 Model B+ (henceforth called “RPi”):

  1. Create a normal Kali Linux RPi installation
  2. Prepare the system for encrypted boot with remote disk unlock
  3. Create an initramfs configured with Dropbear and SSH keys to allow the unlock to occur
  4. Backup existing data
  5. Configure the encrypted partitions
  6. Restore our data
  7. Configure LUKS Nuke
  8. Hack away!

This might seem like a lot, but its really pretty straightforward and once completed, we will be left with a RPi that will boot, get an IP from DHCP, and Dropbear will allow us to connect via SSH to provide the LUKS key. This permits us to run the RPi headless, but still keeping our data secure. Then down the road when we are done with it, we can retrieve it or remote in and destroy our data with LUKS NUKE.

Preparing the Base system

To start with, we need to write out the RPi image to a SD card. We won’t get into that here, but you can find information on doing so in the docs.

With that out of the way, we insert the SD card into the RPi and let it boot up. On first boot, we need to increase the size of the root partition using gparted.

root@kali:~# apt update
root@kali:~# apt install gparted
root@kali:~# gparted

We also need to ensure that we delete and recreate a new set of SSH keys, as the Raspberry Pi Kali image comes with pre-built keys!

root@kali:~# rm /etc/ssh/ssh_host_*
root@kali:~# dpkg-reconfigure openssh-server
root@kali:~# service ssh restart

This will start the gparted application within XFCE.

Resize the partition , it isn’t rocket science! Remember to apply the change though.

Next, we connect over SSH (or continue locally), update Kali, and install a few packages we will need.

root@kali:~# apt update
root@kali:~# apt dist-upgrade
root@kali:~# apt install cryptsetup lvm2 busybox dropbear

Doing the Magic-Fu

The RPi is all setup and ready to go so let’s get our hands dirty and dive into things. Take note, once we start this process, we are going to be changing a number of critical files on our RPi installation. It is important to not reboot the device or otherwise shut down the system until you are ready or you will be left with a system that won’t boot.

First off, we need to append a line to /boot/config.txt:

root@kali:~# echo initramfs initramfs.gz followkernel >> /boot/config.txt

Next, we want to validate where our actual root filesystem device is located:

root@kali:~#  cat /etc/fstab
# proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults 0 2
/dev/mmcblk0p2 / ext4 defaults,noatime 0 1

Take special note that our root filesystem lives at /dev/mmcblk0p2. This is what we will use for our examples going forward, so be sure to update the instructions with whatever value you received on your system.

Now that we know our root filesystem location, we will edit the /boot/cmdline.txt. By default, it contains the following:

root@kali:~# cat /boot/cmdline.txt
dwc_otg.fiq_fix_enable=2 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rootflags=noload net.ifnames=0

Take note of the entry reading root=/dev/mmcblk0p2. We are going to update it with a cryptdevice value:

root=/dev/mapper/crypt cryptdevice=/dev/mmcblk0p2:crypt

With the change made, our file looks like this:

root@kali:~# cat /boot/cmdline.txt
dwc_otg.fiq_fix_enable=2 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mapper/crypt cryptdevice=/dev/mmcblk0p2:crypt rootfstype=ext4 rootwait rootflags=noload net.ifnames=0

We also need to edit /etc/fstab and replace the device where our root filesystem currently is to be /dev/mapper/crypt:

root@kali:~# cat /etc/fstab
# proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults 0 2
/dev/mapper/crypt / ext4 defaults,noatime 0 1
#/dev/mmcblk0p2 / ext4 defaults,noatime 0 1

Next, we need to create a /etc/crypttab file containing the following:

crypt /dev/mmcblk0p2 none luks

Take special care here–the separators between the entries have to be tabs, not spaces. Now, before we start to create our initramsfs, we need to do a sly little hack to force cryptsetup to be included. To do this, we will create a fake LUKS filesystem. We dd an empty file, format it as LUKS, mount it, and put a filesystem on it.

root@kali:~# dd if=/dev/zero of=/tmp/fakeroot.img bs=1M count=20
root@kali:~# cryptsetup luksFormat /tmp/fakeroot.img
root@kali:~# cryptsetup luksOpen /tmp/fakeroot.img crypt
root@kali:~# mkfs.ext4 /dev/mapper/crypt

Don’t worry too much about setting a strong password for this fakeroot as it is only used in this instance.

Setting up SSH and Initramfs

Now we are on the home stretch. This part is really cool, as normally when a system running LUKS starts up, the boot process pauses to allow you to unlock the HDD with your LUKS key. If you are running a headless system, that’s not especially convenient.

To work around that, we are going to configure Dropbear to start up, allow you to authenticate with SSH, and then connect you to provide your LUKS password–all from remote!

We start out by creating a file at /etc/dropbear-initramfs/authorized_keys that contains:

command="export PATH='/sbin:/bin/:/usr/sbin:/usr/bin'; /scripts/local-top/cryptroot && kill -9 `ps | grep -m 1 'cryptroot' | cut -d ' ' -f 3` && exit"

NOTE: The official guide misses this step!

Next we need to append our Dropbear SSH keys to the authorized_keys file:

root@kali:~# ssh-keygen -t rsa -N '' -f /etc/dropbear-initramfs/id_rsa
root@kali:~# cat /etc/dropbear-initramfs/id_rsa/id_rsa.pub >> /etc/dropbear-initramfs/authorized_keys

Its important to note, this needs to be all on one line. No line breaks in there at all. If you have this set up right, it should look similar to this:

root@kali:~# cat /etc/dropbear-initramfs/authorized_keys
command="export PATH='/sbin:/bin/:/usr/sbin:/usr/bin'; /scripts/local-top/cryptroot && kill -9 `ps | grep -m 1 'cryptroot' | cut -d ' ' -f 3` && exit" ssh-rsa AAAAB3NzaC... user@system

Next, we make a small change to /usr/share/initramfs-tools/scripts/init-premount/dropbear. This change is due to the fact that we need to slow down Dropbear to ensure that networking is set up before Dropbear kicks in. At the end of the file, where it reads:

# On NFS mounts, wait until the network is configured. On local mounts,
# configure the network in the background (in run_dropbear()) so someone
# with console access can enter the passphrase immediately. (With the
# default ip=dhcp, configure_networking hangs for 5mins or so when the
# network is unavailable, for instance.)
[ "$BOOT" != nfs ] || configure_networking
run_dropbear &
echo $! >/run/dropbear.pid

We want to add in a simple sleep statement like so:

[ "$BOOT" != nfs ] || configure_networking
sleep 5
run_dropbear &
echo $! >/run/dropbear.pid

With that completed, we are finally ready to create our initramfs!

mkinitramfs -o /boot/initramfs.gz

Before proceeding, we ensure that our customized changes made it into the new initramfs:

lsinitramfs /boot/initramfs.gz | grep cryptsetup


And also verify the new SSH setup:

lsinitramfs /boot/initramfs.gz | grep authorized

something something dark side guy

With that validated, we ensure all changes are written to disk and shut the RPi down.

sync && sync
init 0

Backup and Restore

Remove the SD card from your RPi and return to the system you initially used to write the SD card. Let’s prepare the environment:

ls -al /mnt/{chroot,backup,encrypted}
# Please make sure there is nothing here first before you move on, otherwise you will have a bad day.
rm -rf /mnt/{chroot,backup,encrypted}
mkdir -p /mnt/{chroot,backup,encrypted}

Now insert the SD card and validate the device ID. In our case, the device is /dev/sdc2 but yours might be different so adjust as necessary on your system. We mount the device and make a backup of the filesystem:

mount /dev/sdc2 /mnt/chroot/
rsync -avh /mnt/chroot/* /mnt/backup/
umount /mnt/chroot

Once that is done, we delete the existing 2nd partition on the SD card and recreate an empty one, which we set up for LUKS encryption.

echo -e "d\n2\nw" | fdisk /dev/sdc
echo -e "n\np\n2\n\n\nw" | fdisk /dev/sdc

With the partitions updated, we reload them by running partprobe and then configure LUKS on the new partition:

cryptsetup -v -y --cipher aes-cbc-essiv:sha256 --key-size 256 luksFormat /dev/sdc2
cryptsetup -v luksOpen /dev/sdc2 crypt
mkfs.ext4 /dev/mapper/crypt

With that out of the way, we restore the root filesystem backup to the now encrypted partition.

mount /dev/mapper/crypt /mnt/encrypted/
rsync -avh /mnt/backup/* /mnt/encrypted/
umount /mnt/encrypted/
cryptsetup luksClose /dev/mapper/crypt

Testing it out

You can now put the SD card back into the RPi and let it start up. If you watch the boot, you should see Dropbear start up. At that point, you should be able to SSH into the system and unlock the drive.

root@kali:~# ssh -o "UserKnownHostsFile /dev/null" root@
The authenticity of host ' (' can't be established.
ECDSA key fingerprint is SHA256:L+QVP+OmncGDleuEoj77OlRGuCji2gp0c1gMYjUupU0.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '' (ECDSA) to the list of known hosts.
Please unlock disk /dev/mmcblk0p2 (crypt):
cryptsetup (crypt): set up successfully
Connection to closed.

The versatility of these little devices combined with the power of Kali never ceases to amaze us. Now we are left with a nice headless system we can operate with relative confidence that, even if it’s discovered, it won’t be too simple to get into.

But we aren’t done yet! Let’s add in some LUKS NUKE functionality:

cryptsetup luksDump /dev/mmcblk0p2
cryptsetup luksAddNuke /dev/mmcblk0p2

Now when we SSH in, we have one password we can enter to allow the SD card to unlock and continue the boot process, and another that destroys the LUKS header, making the data inaccessible. If you end up in a situation where you can’t retrieve the device, this option to burn the device could be very helpful. If you want to be really fancy, you could also combine this with making the RPi into a wireless access point, allowing you to remote in and unlock/nuke the system all through a wireless connection. This is very useful if you won’t have ongoing direct access to the network the RPi will be attached to.