LUKS Header Shredder (BusKill Self-Destruct Trigger)

This post will describe how to add a trigger that initiates a "self-destruct" sequence when your BusKill laptop kill cord's connection is severed--rendering your data permanently & irrevocably destroyed in the event that your laptop were physically separated from you (ie: by a snach-and-run thief).

LUKS Header Shredder

Many people were disappointed when the original post introducing BusKill only alluded to a self-destruct trigger, without actually describing how to use it with BusKill. This was done for two reasons:

  1. Most people probably don't actually want an accidental false-positive to destroy all their data and
     
  2. A self-destruct sequence should be taken seriously. Its implementation should be thoroughly thought-out, tested, and forensically analyzed

This article will provide that thorough analysis and explain to the reader how to implement a self-destruct trigger with BusKill on linux machines that have FDE with LUKS.

Note: This is a very long article providing a detailed explanation on our forensic analysis in Kali to prove the efficacy of our self-destruct script. If you'd like, you can skip to the Install Steps.

But please read the disclaimer first, to understand the risks.

Disclaimer

This guide contains experimental files, commands, and software. The information contained in this article may or may not lead to corruption or total permanent deletion of some or all of your data. We've done our best to carefully guide the reader so they know the risks of each BusKill trigger, but we cannot be responsible for any data loss that has occurred as a result of following this guide.

The contents of this guide is provided openly and is licensed under the CC-BY-SA license. The software included in this guide is licensed under the GNU GPLv3 license. All content here is consistent with the limitations of liabilities outlined in its respective licenses.

We highly recommend that any experiments with the scripts included in this article are used exclusively on a disposable machine containing no valuable data.

If data loss is a concern for you, then leave now and do not proceed with following this guide. You have been warned.

Release Note

Also be aware that, due to the risks outlined above, BusKill will not be released with this "self-destruct" trigger.

BusKill will not be released with this "self-destruct" trigger. If you purchase a BusKill cable, it will only ship with non-destructive triggers

If you purchase a BusKill cable, it will only ship with non-destructive triggers that lock the screen or shutdown the computer. Advanced users can follow guides to add additional destructive triggers, such as the one described in this post, but they should do so at their own risk--taking carefully into consideration all of the warnings outlined above and throughout this article.

If you buy a BusKill cable, the worst that can happen is your computer will abruptly shutdown.

Investigating the LUKS Header

Before we go through the process of wiping our FDE LUKS header, let's take some time to dig into the LUKS header's structure and poke at it. That way, we can compare the actual bits on the storage drive before & after the self-destruct trigger has run, and we can do a forensic analysis to validate that our self-destruct trigger is working.

Some of this data will necessarily be unencrypted metadata areas, some will be a bunch of 0s buffering between areas, and some will be areas of just uninteligible/encrypted data.

Create Example LUKS Volume

In this article, I'm going to be publishing hexdumps of an encrypted LUKS volume's header. This header contains the master key, salts, and other data that you absolutely never want to share with others.

For the purposes of this guide, we'll be creating an ephemeral LUKS volume on a file. This has the added benefit of permitting the reader to follow-along with the commands on their own system without requiring our device names to match.

First, let's create a 20M file from random data

user@host:~$ head -c 20M /dev/urandom > luksVol1
user@host:~$ du -sh luksVol1
20M	luksVol1
user@host:~$ 

Now make it a LUKS volume. This command will make you type 'YES' to confirm before it will overwrite 'luksVol1'. It will also ask you for a passphrase.

user@host:~$ sudo cryptsetup luksFormat luksVol1
[sudo] password for user: 

WARNING!
========
This will overwrite data on luksVol1 irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase for luksVol1: 
Verify passphrase: 
user@host:~$ sudo cryptsetup -v isLuks luksVol1
Command successful.
user@host:~$ 

Success! Now that we have a luks volume setup in our file, we can start poking at the bytes on that file to see how the LUKS header is encoded. This is critical to how we design our self-destruct trigger script, so that we can verify that we're actually wiping the master encryption key, salt, etc--rendering all the drive's data useless to an adversary--even if the passphrase were known.

LUKS 0xbabe

Clemens Fruhwirth had a bit of fun with this one. The beginning of every LUKS partition (the magic number field) starts with LUKS BABE!

More specifically, in hex that's

0x4c55 0x4b53 0xbabe

And where

0x4c = L
0x55 = U
0x4b = K
0x53 = S

We can see this, for example, on our newly created luks container file using `head` to grab the first 6 bytes and pass it to `od`.

user@host:~$ head --bytes 6 luksVol1 | od --format c --endian big
0000000   L   U   K   S 272 276
0000006
user@host:~$ head --bytes 6 luksVol1 | od --format x2 --endian big
0000000 4c55 4b53 babe
0000006
user@host:~$ 

A better tool for this is hexdump:

user@host:~$ hexdump -Cn 6 luksVol1 
00000000  4c 55 4b 53 ba be                                 |LUKS..|
00000006
user@host:~$ 

Hexdumps refresher

Before proceeding, I'll provide a brief refresher for those of us that don't look at hexdumps every day. If you do look at hexdumps often, feel free to skip this section.

First remember that a single bit can represent two values: a zero or a one. Four bits together (1111) can represent up to 1+2+4+8 = 15. Including zero, that's 16 values.

A single digit in hex (0-F = base-16) can also represent up to 16 values.

A byte has 8 bits. That's twice the length of the four-bits or a single hex digit shown above. Therefore, hexdumps usually group hex digits together in twos by defaut, as that represents a single byte.

Also conveniently, 8 bits = 2 hex digits = 1 ASCII character.

Now let's consider this hexdump of the first 128 (decimal = base-10) = 0x80 (hex = base-16) = 1000 0000 (binary = base-2) bytes.

user@host:~$ hexdump -Cn 128 luksVol1 
00000000  4c 55 4b 53 ba be 00 02  00 00 00 00 00 00 40 00  |LUKS..........@.|
00000010  00 00 00 00 00 00 00 03  00 00 00 00 00 00 00 00  |................|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000040  00 00 00 00 00 00 00 00  73 68 61 32 35 36 00 00  |........sha256..|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  63 48 e4 17 34 7a be 49  |........cH..4z.I|
00000070  cd 9a 15 8b 81 88 07 ef  f6 04 16 f4 fc 7d 36 aa  |.............}6.|
00000080
user@host:~$ 

Every line in the above output is broken into 3 sections:

  1. On the left: The offset (or start) byte of the line, encoded in hex
  2. In the middle: The data encoded in hex
  3. On the right: The data encoded as ASCII characters (whare a dot (.) means that the value cannot be represented in ASCII)

[1] The first section shows an offset of '00000000', telling us that this line of data starts at the 0th byte of data on our encrypted container luksVol1. The second line is '00000010'. Again, that's implicitly in hex, so 00000010 (hex) = 16 (decimal) = 0001 0000 (binary).

The last line with data is '00000070', so the first byte on that last line (0xcd) is the 0x70th (hex) = 112th (decimal) byte in our 'luksVol1' file. And the last byte on that line is the 0x7Fth (hex) = 127th (decimal) byte. Remember that we're counting from 0 here.

The next byte (not shown) that would appear in this file is the 0x80th (hex) = 128th byte of the file. Note that we passed `hexdump` the '-n 128' argument, indicating that we only wanted to see the first 128 bytes (in decimal). Note that this `hexdump -n` argument, counts from 1, not 0.

[2] shows all our data in hex. Again, a single digit in hex fits 4 binary bits. 0xF (hex) = 15 (decimal) = 1111 (binary). So each hex digit is grouped in twos before being delimited by a space, such that the space between the hex data here delimits each byte (8 bits).

Here we see the first byte of the first row is 0x4c (hex) = 76 (decimal) = 0100 1100 (binary). A single line in a hexdump contains 32 hex digits = 16 bytes. The first set of 8 bytes are delimited by the second set of 8 bytes on a single line by a double-space.

On the first line of our hexdump output above, "0x02" is the 7th (decimal) byte and "0x00" is the 8th (decimal) byte of our LUKS container. The last byte of our first line (0x00) is the 15th (decimal) byte of the file, and the next byte that drops down to the next line (also 0x00) is the 16th (decimal) byte of the file.

[3] Delimited at the end of the line in pipes (|...|) is the same data represented by [2], but displayed as characters in ASCII instead of hex. An ASCII character is represented by a single byte = two hex digits. Here we see the first four bytes of the device are 0x4c554b53, which translates into "LUKS" in ASCII.

The next two bytes (0xbabe) translate into "Masculine ordinal indicator" (º) followed by "Fraction three quarters" (¾) -- which apparently our hexdump tool decided not to print, and just put a dot (.) instead *shrug*

Decoding the LUKS container's header

At the time of writing, there's actually two distinct specifications for how the bits are encoded on a LUKS container.

The first is LUKS1, which was first described in a paper titled LUKS On-Disk Format Specification published by Clemens Fruhwirth for his master's thesis in 2005.

The second is LUKS2, which was formally documented in a paper titled LUKS2 On-Disk Format Specification published by Milan Brož (Principal Program Manager, Red Hat) in 2018.

In this article, our example LUKS containers all use LUKS2. LUKS2 was

  1. first added in `cryptsetup` v2.0.0 in 2017,
  2. became the default LUKS version in `cryptsetup` v2.1.0 in 2019, and
  3. became the default LUKS version with Debian 10 released in 2020.

The On-Disk Format for LUKS2 is very different from LUKS1. The only two data fields that match exactly are the 'LUKS 0xbabe' "magic number" field and the version field.

offset length name data type description
0 6 magic number byte[] 'L','U','K','S', 0xba, 0xbe
6 2 version uint16_t LUKS version

As the table above shows, our first field is the so-called LUKS_MAGIC field that starts at byte 0 and is 6 bytes long.

user@host:~$ hexdump -Cn 6 -s 0 luksVol1 
00000000  4c 55 4b 53 ba be                                 |LUKS..|
00000006
user@host:~$ 

Next is the version; it's 2 bytes long, starts at byte 6, and it's an unsigned 16-bit unsigned integer (uint16_t). Here we can see that our new LUKS file volume is using LUKS version 2.

user@host:~$ hexdump -Cn 2 -s 6 luksVol1 
00000006  00 02                                             |..|
00000008
user@host:~$ 

After that is where the two On-Disk Formats diverge.

luksErase

In January 2014, Kali Linux published an article titled How to Nuke your Encrypted Kali Installation that described how to use an old `cryptsetup` patch (from 2008 by Juergen Pabel) that would permit the user to type a special duress password when decrypting the drive on-boot. When entered, this duress password would wipe (nuke) the encrypted drive where Kali was installed.

This sparked a discussion on the dm-crypt mailing list that ultimately lead to the creation of the luksErase command in `cryptsetup` v1.6.4 (released February 2014).

In 2008, Juergen Pabel submitted his patch for the LUKS "nuke" feature to Clemens Fruhwirth (then maintainer of LUKS), but it was never merged into `cryptsetup`.
 
In 2018 (ten years later), a feature request was submitted again, but it was rejected by the current maintainer of LUKS (Milan Brož)

Calling `luksErase` overwrites the keyslots area--effectively rendering all of the encrypted data permanently irrecoverable. However, it does not wipe the plaintext metadata in the header.

I submitted a feature request asking for the `luksErase` command to have the ability to wipe the plaintext metadata in the header as well, but--meanwhile--we'll just have to do that ourselves.

LUKS plaintext metadata

In order to wipe the entire LUKS header, including the plaintext metadata, we'll call `cryptsetup luksErase` followed by a command to overwrite the plaintext metadata area of the LUKS container. But, to do this, we first must determine the start byte and end byte of the metadata area.

And it's good to be precise. If we overwrite past the end of the header, then the encrypted data bit will be corrupted (breaking the header restore process in the event that a user wishes to recover from a false-positive self-destruct). If we overwrite too little, then we may provide partial metadata as forensic evidence to the user's adversary.

LUKS1 metadata

Image of a table showing the LUKS1 encoding
Excerpt from the LUKS1 whitepaper showing Figure 1: PHDR layout

In LUKS1, the partition header is a short and relatively fixed-length of 1052672 or 2097152 bytes. The exact length can be determined by inspecting the 'payload-offset' field and multiplying it by 512 (see "Figure 1: PHDR layout" of LUKS1 On-Disk Format Specification). This field starts at an offset of 104 bytes and is 4 bytes long.

For example, the following hexdump shows the payload-offset field has a value of 0x1000 (hex) = 4096 (decimal). Multiplying that by 512, we know that the LUKS container's header ends at byte 4096*512 = byte 2097152.

root@disp4117:~# hexdump -Cs 104 -n 4 luksVol1
00000068  00 00 10 00                                       |....|
0000006c
root@disp4117:~# 

Overwriting the first 2097152 bytes of a LUKS1 header is actually superfluous; it overwrites the plaintext metadata and the keyslots area (that we'll already overwrite with `cryptsetup luksErase`)--but it's an easier boundry to decode, and overwriting 2MiB is fast enough for our needs..

LUKS2 metadata

In LUKS2, the header is actually made-up of six distinct headers, including

  1. A primary binary header (exactly 4096 bytes)
  2. The "1st JSON" area (16384-4194304 bytes)
  3. A secondary binary header (exactly 4096 bytes)
  4. The "2nd JSON" area (16384-4194304 bytes)
  5. The Keyslots area (variable length)
  6. The Alignment Padding area
Figure shows the difference in the LUKS1 header vs LUKS2 header
Excerpt from the LUKS2 Whitepaper showing the difference between the LUKS1 and LUKS2 headers

The easiest way to determine the end byte of a LUKS2 header's metadata is to get the `hdr_size` field and double it.

In LUKS2, the `hdr_size` field is the size of the first two header areas (the "primary binary header" area plus the "1st header area). So double that ends where the keyslots area begins--which is where the metadata ends.

OK, with all that prerequisite knowledge out of the way, let's proceed with installing a fresh version of Ubuntu, nuking it, and analyzing the result.

Self-Destruct Test

In this section, we will do a forensics analysis of a drive before & after BusKll triggers a self-destruct sequence.

ATA Secure Erase

First, we'll trigger an ATA Secure Erase on the laptop's SSD, and we'll do a forensic analysis of this drive in its fresh state before installing Ubuntu with FDE.

To preform this analysis, I'll be using Kali Linux and its `bulk_extractor` tool.

After booting to Kali, we do a quick check on the drive's partition structure. What you see here was created by a previous Ubuntu Desktop install.

root@kali:~# fdisk -l /dev/sda
Disk /dev/sda: 111.81 GiB, 120034123776 bytes, 234441648 sectors
Disk model: WDC WDS120G2G0B-
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xcb6eced1

Device     Boot   Start       End   Sectors   Size Id Type
/dev/sda1  *       2048   1050623   1048576   512M  b W95 FAT32
/dev/sda2       1052670 234440703 233388034 111.3G  5 Extended
/dev/sda5       1052672   2549759   1497088   731M 83 Linux
/dev/sda6       2551808 234440703 231888896 110.6G 83 Linux
root@kali:~# 

Now--in order to proceed with the ATA Secure Erase--we have to suspend to disk to unfreeze the drive.

root@kali:~# hdparm -I /dev/sda | grep frozen
                frozen
root@kali:~# echo -n mem > /sys/power/state

After it comes back, we go through the process to trigger an ATA Secure Erase. Note that these commands may vary (or not be applicable at all) to your storage device:

root@kali:~# hdparm -I /dev/sda | grep frozen
        not     frozen
root@kali:~# hdparm --user-master u --security-set-pass PASSWD /dev/sda
security_password: "PASSWD"

/dev/sda:
 Issuing SECURITY_SET_PASS command, password="PASSWD", user=user, mode=high
root@kali:~# hdparm --user-master u --security-erase PASSWD /dev/sda
security_password: "PASSWD"

/dev/sda:
 Issuing SECURITY_ERASE command, password="PASSWD", user=user
root@kali:~# 

Note that the dive's partition structure has been factory reset, and it's filled with all zeros.

root@kali:~# fdisk -l /dev/sda
Disk /dev/sda: 111.81 GiB, 120034123776 bytes, 234441648 sectors
Disk model: WDC WDS120G2G0B-
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
root@kali:~# 
root@kali:~# hexdump -C /dev/sda
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
1bf2976000
root@kali:~# 

Forensic Analysis 0 (after factory reset)

And, just to be sure, we'll attempt to find any recognizable patterns. We'll also be doing this again after the Ubuntu install to compare the differences.

Note that, in addition to using all the default scanners in `bulk_extractor`, we're also passing-in regular expressions of the following byte sequences specific to our LUKS header:
 

  1. magic = 0x4c554b53babe
  2. "keyslot" = 0x6b6579736c6f74
  3. "stripe" = 0x737472697065
  4. "offset" = 0x6f6666736574
  5. "encrypt" = 0x656e6372797074
  6. "segment" = 0x7365676d656e74
root@kali:~# mkdir bulk_out
root@kali:~# bulk_extractor -o bulk_out -f $'\x4c\x55\x4b\x53\xba\xbe' -f $'\x6b\x65\x79\x73\x6c\x6f\x74' -f $'\x73\x74\x72\x69\x70\x65' -f $'\x6f\x66\x66\x73\x65\x74' -f $'\x65\x6e\x63\x72\x79\x70\x74'  -f $'\x73\x65\x67\x6d\x65\x6e\x74' /dev/sda
...
root@kali:~# cat bulk_out/*.txt
root@kali:~# 

No surprise; it found nothing.

Install Ubuntu

Now, let's install Ubuntu on our freshly wiped drive.

Of course, I'll be choosing FDE at install time.

Screenshot of Ubuntu Install Wizard
BusKill assumes the user enabled FDE. This screenshot shows the option to enable FDE when installing Ubuntu 2020.04


Forensic Analysis 1 (after Ubuntu install)

Now that Ubuntu is installed, let's poke at the drive again in Kali and see what we can learn without decrypting it.

First, we see that the drive now has 4 partitions:

  1. /dev/sda1 is an unencrypted 512M W95 FAT32 dumb partition with an MBR
  2. /dev/sda2 is an unencrypted "extended" partition containing two "logical" partitions
  3. /dev/sda5 is an unencrypted 731M logical /boot parititon
  4. /dev/sda6 is the LUKS-encrypted 111G / partition
root@kali:~/bulk_out_sda6# fdisk -l /dev/sda
Disk /dev/sda: 111.81 GiB, 120034123776 bytes, 234441648 sectors
Disk model: WDC WDS120G2G0B-
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x8d666a9a

Device     Boot   Start       End   Sectors   Size Id Type
/dev/sda1  *       2048   1050623   1048576   512M  b W95 FAT32
/dev/sda2       1052670 234440703 233388034 111.3G  5 Extended
/dev/sda5       1052672   2549759   1497088   731M 83 Linux
/dev/sda6       2551808 234440703 231888896 110.6G 83 Linux
root@kali:~/bulk_out_sda6# 

Note that Ubuntu's default partition layout may differ from above if, for example, you have disks larger than 2T

We can verify this by doing a `hexdump` on the encrytped '/dev/sda6' partition, which shows the LUKS magic number, version, 'hdr_size', etc.

root@kali:~# hexdump -Cn 4832 /dev/sda6
00000000  4c 55 4b 53 ba be 00 02  00 00 00 00 00 00 40 00  |LUKS..........@.|
00000010  00 00 00 00 00 00 00 03  00 00 00 00 00 00 00 00  |................|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000040  00 00 00 00 00 00 00 00  73 68 61 32 35 36 00 00  |........sha256..|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  5e 4d 69 a0 3c 96 b1 9c  |........^Mi.<...|
00000070  f7 84 2f 71 82 bb af 05  2f 4a 4c fe 60 f5 74 16  |../q..../JL.`.t.|
00000080  ea 52 a8 8b 34 7a 0f b4  a9 36 82 2a c1 4f 3f 89  |.R..4z...6.*.O?.|
00000090  c7 4e e4 5d 4a d1 39 b3  c7 e1 c5 20 4d bf c3 a8  |.N.]J.9.... M...|
000000a0  53 01 38 33 a4 f0 2e ae  31 31 37 35 39 64 62 61  |S.83....11759dba|
000000b0  2d 61 33 36 32 2d 34 30  38 37 2d 39 31 34 66 2d  |-a362-4087-914f-|
000000c0  38 61 62 64 32 61 61 66  31 35 66 33 00 00 00 00  |8abd2aaf15f3....|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001c0  7b cf a2 6c 86 ae 22 03  48 75 49 7a 8b 92 1e 28  |{..l..".HuIz...(|
000001d0  fa 64 a0 bc cf e7 7a 83  1b 75 d9 96 09 79 b5 8e  |.d....z..u...y..|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001000  7b 22 6b 65 79 73 6c 6f  74 73 22 3a 7b 22 30 22  |{"keyslots":{"0"|
00001010  3a 7b 22 74 79 70 65 22  3a 22 6c 75 6b 73 32 22  |:{"type":"luks2"|
00001020  2c 22 6b 65 79 5f 73 69  7a 65 22 3a 36 34 2c 22  |,"key_size":64,"|
00001030  61 66 22 3a 7b 22 74 79  70 65 22 3a 22 6c 75 6b  |af":{"type":"luk|
00001040  73 31 22 2c 22 73 74 72  69 70 65 73 22 3a 34 30  |s1","stripes":40|
00001050  30 30 2c 22 68 61 73 68  22 3a 22 73 68 61 32 35  |00,"hash":"sha25|
00001060  36 22 7d 2c 22 61 72 65  61 22 3a 7b 22 74 79 70  |6"},"area":{"typ|
00001070  65 22 3a 22 72 61 77 22  2c 22 6f 66 66 73 65 74  |e":"raw","offset|
...
A screenshot in Kali with a hexdump showing a valid LUKS header
A forensic analysis of the LUKS header after a fresh Ubuntu install

Now let's use `bulk_extractor` again on our new encrypted LUKS partition.

root@kali:~# bulk_extractor -q -1 -o bulk_out_sda6 -f $'\x4c\x55\x4b\x53\xba\xbe' -f $'\x6b\x65\x79\x73\x6c\x6f\x74' -f $'\x73\x74\x72\x69\x70\x65' -f $'\x6f\x66\x66\x73\x65\x74' -f $'\x65\x6e\x63\x72\x79\x70\x74'  -f $'\x73\x65\x67\x6d\x65\x6e\x74' /dev/sda6
...
root@kali:~# 
root@kali:~# cd bulk_out_sda6
root@kali:~/bulk_out_sda6# find . -name \*.txt | grep -vi histogram | xargs cat
# BANNER FILE NOT PROVIDED (-b option)
# BULK_EXTRACTOR-Version: 1.6.0 ($Rev: 10844 $)
# Feature-Recorder: json
# Filename: /dev/sda6
# Feature-File-Version: 1.1
4828    {"keyslots":{"0":{"type":"luks2","key_size":64,"af":{"type":"luks1","stripes":4000,"hash":"sha256"},"area":{"type":"raw","offset":"32768","size":"258048","encryption":"aes-xts-plain64","key_size":64},"kdf":{"type":"argon2i","time":4,"memory":612334,"cpus":4,"salt":"ohyuAKaNOvY/727uMiYgZ6zgVR/FSVTc4UTunJjkXh8="}}},"tokens":{},"segments":{"0":{"type":"crypt","offset":"16777216","size":"dynamic","iv_tweak":"0","encryption":"aes-xts-plain64","sector_size":512}},"digests":{"0":{"type":"pbkdf2","keyslots":["0"],"segments":["0"],"hash":"sha256","iterations":66737,"salt":"VQ+dkN5lo0vw4oxAuJY4wU0DGP+1X6lhouurxBOi180=","digest":"Qc1HkrLGbL2iqQb1TqZq7UKjReRmLcObGqScyqUi3Vo="}},"config":{"json_size":"12288","keyslots_size":"16744448"}}      03101a951dfb1f6b277f90081e2f0331
21212   {"keyslots":{"0":{"type":"luks2","key_size":64,"af":{"type":"luks1","stripes":4000,"hash":"sha256"},"area":{"type":"raw","offset":"32768","size":"258048","encryption":"aes-xts-plain64","key_size":64},"kdf":{"type":"argon2i","time":4,"memory":612334,"cpus":4,"salt":"ohyuAKaNOvY/727uMiYgZ6zgVR/FSVTc4UTunJjkXh8="}}},"tokens":{},"segments":{"0":{"type":"crypt","offset":"16777216","size":"dynamic","iv_tweak":"0","encryption":"aes-xts-plain64","sector_size":512}},"digests":{"0":{"type":"pbkdf2","keyslots":["0"],"segments":["0"],"hash":"sha256","iterations":66737,"salt":"VQ+dkN5lo0vw4oxAuJY4wU0DGP+1X6lhouurxBOi180=","digest":"Qc1HkrLGbL2iqQb1TqZq7UKjReRmLcObGqScyqUi3Vo="}},"config":{"json_size":"12288","keyslots_size":"16744448"}}      03101a951dfb1f6b277f90081e2f0331
# BANNER FILE NOT PROVIDED (-b option)
# BULK_EXTRACTOR-Version: 1.6.0 ($Rev: 10844 $)
# Feature-Recorder: find
# Filename: /dev/sda6
# Feature-File-Version: 1.1
0       LUKS\xBA\xBE    LUKS\xBA\xBE\x00\x02\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00
4098    keyslot \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00{"keyslots":{"0":{"type":
4165    stripe  "type":"luks1","stripes":4000,"hash":"
4218    offset  :{"type":"raw","offset":"32768","size"
4251    encrypt size":"258048","encryption":"aes-xts-pl
4424    segment }},"tokens":{},"segments":{"0":{"type":
4456    offset  "type":"crypt","offset":"16777216","si
4508    encrypt "iv_tweak":"0","encryption":"aes-xts-pl
4591    keyslot type":"pbkdf2","keyslots":["0"],"segmen
4608    segment eyslots":["0"],"segments":["0"],"hash":
4802    keyslot _size":"12288","keyslots_size":"1674444
20482   keyslot \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00{"keyslots":{"0":{"type":
20549   stripe  "type":"luks1","stripes":4000,"hash":"
20602   offset  :{"type":"raw","offset":"32768","size"
20635   encrypt size":"258048","encryption":"aes-xts-pl
20808   segment }},"tokens":{},"segments":{"0":{"type":
20840   offset  "type":"crypt","offset":"16777216","si
20892   encrypt "iv_tweak":"0","encryption":"aes-xts-pl
20975   keyslot type":"pbkdf2","keyslots":["0"],"segmen
20992   segment eyslots":["0"],"segments":["0"],"hash":
21186   keyslot _size":"12288","keyslots_size":"1674444
# BANNER FILE NOT PROVIDED (-b option)
# BULK_EXTRACTOR-Version: 1.6.0 ($Rev: 10844 $)
# Feature-Recorder: email
# Filename: /dev/sda6
# Feature-File-Version: 1.1
935334046       d7R@y5.LU       \x86\xEFO\xE7%睁F\x90\x05*\x9F\xC5\xD6\xC1d7R@y5.LU\xC8/\xB7\x9FP\xE0|m\xEF\x0A\x89\xA1\xAE\x0C\x80\xA2
# BANNER FILE NOT PROVIDED (-b option)
# BULK_EXTRACTOR-Version: 1.6.0 ($Rev: 10844 $)
# Feature-Recorder: domain
# Filename: /dev/sda6
# Feature-File-Version: 1.1
935334050       y5.LU   %睁F\x90\x05*\x9F\xC5\xD6\xC1d7R@y5.LU\xC8/\xB7\x9FP\xE0|m\xEF\x0A\x89\xA1\xAE\x0C\x80\xA2
root@kali:~/bulk_out_sda6# 

The above output shows that `bulk_extractor` picked-up the following from our LUKS-encrypted partition:

  1. The 'json' scanner picked-up the two plaintext metadata JSON areas from the LUKS header.
  2. The 'find' scanner picked-up 21 distinct entries that matched one of our byte sequences
  3. The 'email' scanner picked-up a bogus email address "d7R@y5.LU" -- which is probably just a coincidence of random data
  4. Same as above, the 'domain' scanner picked-up the same bogus "y5.LU" domain from above

And for completeness, let's use `bulk_extractor` again on the entire '/dev/sda' drive--not just the LUKS-encrypted partition:

root@kali:~# bulk_extractor -q -1 -o bulk_out -f $'\x4c\x55\x4b\x53\xba\xbe' -f $'\x6b\x65\x79\x73\x6c\x6f\x74' -f $'\x73\x74\x72\x69\x70\x65' -f $'\x6f\x66\x66\x73\x65\x74' -f $'\x65\x6e\x63\x72\x79\x70\x74'  -f $'\x73\x65\x67\x6d\x65\x6e\x74' /dev/sda 
...
root@kali:~#
root@kali:~# cd bulk_out
root@kali:~/bulk_out# find . -name \*.txt | grep -Evi '_histogram|_services' | xargs tail --silent -n+6 | wc -l
2400
root@kali:~/bulk_out# 

I've simplified the details of the report above for brevity, but it's sufficient to note that it detected 2,400 matches from the following features: url, telephone, find, email, domain, zip, elf, and zip.

Interestingly, `bulk_extractor` found 8 LUKS magic number sequences across the entire disk, but only one of them actually looks like the start of a LUKS container. My best guess is that the other 7 are from binaries or the magic patterns database file.

root@kali:~/bulk_out# grep 'LUKS\\xBA\\xBE' find.txt 
701434826       LUKS\xBA\xBE    \xFE\xF0\x07ONE\x00LVM2 001\x00LUKS\xBA\xBE\x00CD\x0D\x00\xF3\x0CCDROM\x00JFS
765575408       LUKS\xBA\xBE    _mega\x1C\x002SUB\xC5\x00\xF0\x07\x00LUKS\xBA\xBE\x00SKUL\xBA\xBE\x00crypto_\x15
782257838       LUKS\xBA\xBE    \x0A\x00access denied\x00LUKS\xBA\xBE\x00%s != %s\x0A\x00Ciphe
795806644       LUKS\xBA\xBE    \xFE\xF0\x07ONE\x00LVM2 001\x00LUKS\xBA\xBE\x00CD\x0D\x00\xF3\x0CCDROM\x00JFS
883016223       LUKS\xBA\xBE    _mega\x1C\x002SUB\xC5\x00\xF0\x07\x00LUKS\xBA\xBE\x00SKUL\xBA\xBE\x00crypto_\x15
900664248       LUKS\xBA\xBE    \xFE\xF0\x07ONE\x00LVM2 001\x00LUKS\xBA\xBE\x00CD\x0D\x00\xF3\x0CCDROM\x00JFS
975291012       LUKS\xBA\xBE    _mega\x1C\x002SUB\xC5\x00\xF0\x07\x00LUKS\xBA\xBE\x00SKUL\xBA\xBE\x00crypto_\x15
1306525696      LUKS\xBA\xBE    \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LUKS\xBA\xBE\x00\x02\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00
root@kali:~/bulk_out# hexdump -Cs 701434826 -n 80 /dev/sda
29cf0bca  4c 55 4b 53 ba be 00 43  44 0d 00 f3 0c 43 44 52  |LUKS...CD....CDR|
29cf0bda  4f 4d 00 4a 46 53 31 00  5f 42 48 52 66 53 5f 4d  |OM.JFS1._BHRfS_M|
29cf0bea  00 52 65 49 73 45 72 34  08 00 23 46 73 09 00 15  |.ReIsEr4..#Fs...|
29cf0bfa  32 0a 00 af 33 46 73 00  4f 43 46 53 56 32 e8 00  |2...3Fs.OCFSV2..|
29cf0c0a  0c 39 67 7a 69 07 82 2a  3e 14 2c 47 11 63 f7 b5  |.9gzi..*>.,G.c..|
29cf0c1a
root@kali:~/bulk_out# hexdump -Cs 765575408 -n 80 /dev/sda
2da1c0f0  4c 55 4b 53 ba be 00 53  4b 55 4c ba be 00 63 72  |LUKS...SKUL...cr|
2da1c100  79 70 74 6f 5f 15 00 33  00 00 40 a9 01 15 80 92  |ypto_..3..@.....|
2da1c110  0e 03 08 00 13 02 08 00  13 04 08 00 13 08 08 00  |................|
2da1c120  14 10 c2 18 03 08 00 02  41 00 c0 44 4d 5f 69 6e  |........A..DM_in|
2da1c130  74 65 67 72 69 74 79 0d  00 00 0a 10 70 74 79 5f  |tegrity.....pty_|
2da1c140
root@kali:~/bulk_out# hexdump -Cs 782257838 -n 80 /dev/sda
2ea04eae  4c 55 4b 53 ba be 00 25  73 20 21 3d 20 25 73 0a  |LUKS...%s != %s.|
2ea04ebe  00 43 69 70 68 65 72 20  25 73 20 69 73 6e 27 74  |.Cipher %s isn't|
2ea04ece  20 61 76 61 69 6c 61 62  6c 65 00 69 6e 76 61 6c  | available.inval|
2ea04ede  69 64 20 6b 65 79 73 69  7a 65 20 25 64 00 65 63  |id keysize %d.ec|
2ea04eee  62 00 70 6c 61 69 6e 00  63 62 63 2d 00 70 63 62  |b.plain.cbc-.pcb|
2ea04efe
root@kali:~/bulk_out# hexdump -Cs 795806644 -n 80 /dev/sda
2f6f0bb4  4c 55 4b 53 ba be 00 43  44 0d 00 f3 0c 43 44 52  |LUKS...CD....CDR|                                                 
2f6f0bc4  4f 4d 00 4a 46 53 31 00  5f 42 48 52 66 53 5f 4d  |OM.JFS1._BHRfS_M|                                                 
2f6f0bd4  00 52 65 49 73 45 72 34  08 00 23 46 73 09 00 15  |.ReIsEr4..#Fs...|                                                 
2f6f0be4  32 0a 00 af 33 46 73 00  4f 43 46 53 56 32 e8 00  |2...3Fs.OCFSV2..|                                                 
2f6f0bf4  0c 39 67 7a 69 07 82 2a  3e 14 2c 47 11 63 f7 b5  |.9gzi..*>.,G.c..|                                                 
2f6f0c04                                                                                                                       
root@kali:~/bulk_out# hexdump -Cs 883016223 -n 80 /dev/sda                                                                     
34a1c21f  4c 55 4b 53 ba be 00 53  4b 55 4c ba be 00 63 72  |LUKS...SKUL...cr|                                                 
34a1c22f  79 70 74 6f 5f 15 00 33  00 00 40 a9 01 15 80 92  |ypto_..3..@.....|                                                 
34a1c23f  0e 03 08 00 13 02 08 00  13 04 08 00 13 08 08 00  |................|                                                 
34a1c24f  14 10 c2 18 03 08 00 02  41 00 c0 44 4d 5f 69 6e  |........A..DM_in|                                                 
34a1c25f  74 65 67 72 69 74 79 0d  00 00 0a 10 70 74 79 5f  |tegrity.....pty_|                                                 
34a1c26f                                                                                                                       
root@kali:~/bulk_out# hexdump -Cs 900664248 -n 80 /dev/sda                                                                     
35af0bb8  4c 55 4b 53 ba be 00 43  44 0d 00 f3 0c 43 44 52  |LUKS...CD....CDR|                                                 
35af0bc8  4f 4d 00 4a 46 53 31 00  5f 42 48 52 66 53 5f 4d  |OM.JFS1._BHRfS_M|                                                 
35af0bd8  00 52 65 49 73 45 72 34  08 00 23 46 73 09 00 15  |.ReIsEr4..#Fs...|                                                 
35af0be8  32 0a 00 af 33 46 73 00  4f 43 46 53 56 32 e8 00  |2...3Fs.OCFSV2..|                                                 
35af0bf8  0c 39 67 7a 69 07 82 2a  3e 14 2c 47 11 63 f7 b5  |.9gzi..*>.,G.c..|
35af0c08
root@kali:~/bulk_out# hexdump -Cs 975291012 -n 80 /dev/sda
3a21c284  4c 55 4b 53 ba be 00 53  4b 55 4c ba be 00 63 72  |LUKS...SKUL...cr|
3a21c294  79 70 74 6f 5f 15 00 33  00 00 40 a9 01 15 80 92  |ypto_..3..@.....|
3a21c2a4  0e 03 08 00 13 02 08 00  13 04 08 00 13 08 08 00  |................|
3a21c2b4  14 10 c2 18 03 08 00 02  41 00 c0 44 4d 5f 69 6e  |........A..DM_in|
3a21c2c4  74 65 67 72 69 74 79 0d  00 00 0a 10 70 74 79 5f  |tegrity.....pty_|
3a21c2d4
root@kali:~/bulk_out# hexdump -Cs 1306525696 -n 80 /dev/sda
4de00000  4c 55 4b 53 ba be 00 02  00 00 00 00 00 00 40 00  |LUKS..........@.|
4de00010  00 00 00 00 00 00 00 03  00 00 00 00 00 00 00 00  |................|
4de00020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
4de00040  00 00 00 00 00 00 00 00  73 68 61 32 35 36 00 00  |........sha256..|
4de00050
root@kali:~/bulk_out# 

Self-Destruct

Now we'll boot into Ubuntu, setup BusKill with this self-destruct trigger, and trip it!

First, download the lockscreen and self-destruct triggers from the 'buskill-linux' repo on GitHub and put them in /user/local/bin/

sudo wget -O /usr/local/bin/buskill-lock.sh https://raw.githubusercontent.com/BusKill/buskill-linux/master/triggers/buskill-lock.sh
sudo wget -O /usr/local/bin/buskill-selfdestruct.sh https://raw.githubusercontent.com/BusKill/buskill-linux/master/triggers/buskill-selfdestruct.sh

sudo chmod +x /usr/local/bin/buskill-lock.sh
sudo chmod +x /usr/local/bin/buskill-selfdestruct.sh

Then add the udev rule that will call our `self-destruct.sh` trigger script when the BusKill cable's connection is severed. In this guide, we identify our BusKill-specific drive with the 'ENV{ID_MODEL}=="Micromax_A74"' udev property. You should replace this property with one that matches your BusKill-specific drive.

Note: to determine how to query your USB drive for device-specific identifiers, see Introducing BusKill: A Kill Cord for your Laptop.

cat << EOF | sudo tee /etc/udev/rules.d/buskill.rules
################################################################################
# File:    /etc/udev/rules.d/buskill.rules
# Purpose: Add buskill rules. More info: https://buskill.in/luks-self-destruct
# Authors: Michael Altfield <michael@buskill.in>
# Created: 2020-06-07
# License: GNU GPLv3
################################################################################
ACTION=="remove", SUBSYSTEM=="usb", ENV{ID_MODEL}=="Micromax_A74", RUN+="/usr/local/bin/buskill-selfdestruct.sh --yes"
EOF
sudo udevadm control --reload

That's it! When you remove a USB drive from your system matching the udev property defined in the udev rule above, the `selfdestruct.sh` script will be triggered.

Fact: When I did this, I set a stop-watch on my first execution of the buskill self-destruct, and it counted 5.16 seconds between the time that I executed it and the time that my machine was fully shut-off. Excellent!


Forensic Analysis 2 (after self-destruct)

Did our self-destrut trigger work? Let's check.

First evidence: well, I can't boot to Ubuntu anymore. But let's dig deeper in Kali.

Screenshot with black terminal background and text reding: "Cannot process volume group vgubuntu"
The error message that's shown when attempting to boot to Ubuntu after the self-destruct

A `hexdump` on the encrytped '/dev/sda6' partition, no longer shows the LUKS magic number, version, 'hdr_size', etc. Good so far.

A screenshot in Kali with a hexdump showing random data
A forensic analysis of the LUKS header after the self-destruct script ran shows it has been fully overwritten with random data

Re-running `bulk_extractor` shows that it still finds the coincidental email/domain 'd7R@y5.LU' and a new false-positive domain '0W.ER'.

But it appears that the buskill self-destruct script totally erradicated all of the json and the 21 LUKS byte sequence matches! Mission accomplished!

root@kali:~# bulk_extractor -q 19 -o bulk_out_sda6 -f $'\x4c\x55\x4b\x53\xba\xbe' -f $'\x6b\x65\x79\x73\x6c\x6f\x74' -f $'\x73\x74\x72\x69\x70\x65' -f $'\x6f\x66\x66\x73\x65\x74' -f $'\x65\x6e\x63\x72\x79\x70\x74'  -f $'\x73\x65\x67\x6d\x65\x6e\x74' /dev/sda6
...
root@kali:~# 
root@kali:~# cd bulk_out_sda6/
root@kali:~/bulk_out_sda6# find . -name \*.txt | grep -vi histogram | xargs cat
# BANNER FILE NOT PROVIDED (-b option)
# BULK_EXTRACTOR-Version: 1.6.0 ($Rev: 10844 $)
# Feature-Recorder: email
# Filename: /dev/sda6
# Feature-File-Version: 1.1
935334046       d7R@y5.LU       \x86\xEFO\xE7%睁F\x90\x05*\x9F\xC5\xD6\xC1d7R@y5.LU\xC8/\xB7\x9FP\xE0|m\xEF\x0A\x89\xA1\xAE\x0C\x80\xA2
36662462829     xhV@0W.ER       y4\x07w\xE2\xE6\xEBI\x1F1f\x0C,\x9AxhV@0W.ER`\x8E\x1DZ8\x0B\xEA\xDD\xF2\x8EȈ,\x8A\x8C#
# BANNER FILE NOT PROVIDED (-b option)
# BULK_EXTRACTOR-Version: 1.6.0 ($Rev: 10844 $)
# Feature-Recorder: domain
# Filename: /dev/sda6
# Feature-File-Version: 1.1
935334050       y5.LU   %睁F\x90\x05*\x9F\xC5\xD6\xC1d7R@y5.LU\xC8/\xB7\x9FP\xE0|m\xEF\x0A\x89\xA1\xAE\x0C\x80\xA2
36662462833     0W.ER   \xE2\xE6\xEBI\x1F1f\x0C,\x9AxhV@0W.ER`\x8E\x1DZ8\x0B\xEA\xDD\xF2\x8EȈ,\x8A\x8C#
root@kali:~/bulk_out_sda6# 

And a scan on the entire '/dev/sda' drive (not just the LUKS-encrypted parititon) shows:

root@kali:~# bulk_extractor -q 9 -o bulk_out_sda -f $'\x4c\x55\x4b\x53\xba\xbe' -f $'\x6b\x65\x79\x73\x6c\x6f\x74' -f $'\x73\x74\x72\x69\x70\x65' -f $'\x6f\x66\x66\x73\x65\x74' -f $'\x65\x6e\x63\x72\x79\x70\x74'  -f $'\x73\x65\x67\x6d\x65\x6e\x74' /dev/sda
bulk_extractor version: 1.6.0
...
root@kali:~# cd bulk_out_sda
root@kali:~/bulk_out_sda# find . -name \*.txt | grep -Evi '_histogram|_services' | xargs tail --silent -n+6 | wc -l
3205
root@kali:~/bulk_out_sda#

Interestingly, this run after the self-destruct yielded more matches (3,205 vs 2,400) on the entire '/dev/sda' device than before. My best guess is that this is a consequence of installing Ubuntu updates after the previous run of `bulk_extractor` (including a kernel upgrade from 5.4.0-14 to 5.4.0-18), which added additional files to the unencrypted '/boot' partition '/dev/sda5'.

This is a great example of the type of data that might not be wiped when the BusKill self-destruct trigger is executed. Not only will all of these files be intact, but their metadata is also intact. Therefore, a forensics team would be able to collect enough evicence to conclude with relative or near-certainty:

  1. Which Linux distro you were running
  2. Which kernel version you were running
  3. What day you installed your OS
  4. What day you updated all previous kernel versions

Note that the metadata indicating the timestamp that kernels were downlaoded/installed might be able correlate your previous browsing activites with network survalence records, even if you were browsing on the Tor anonymitity network.

root@kali:/# mount -o ro /dev/sda5 /mnt
root@kali:/# ls -lah /mnt
total 194M
drwxr-xr-x 5 root root 4.0K Mar 20 18:05 .
drwxr-xr-x 1 root root  160 Mar 20 18:41 ..
-rw-r--r-- 1 root root 233K Jan 13 11:26 config-5.4.0-1002-oem
-rw-r--r-- 1 root root 233K Feb  6 22:30 config-5.4.0-14-generic
-rw-r--r-- 1 root root 233K Mar  7 16:23 config-5.4.0-18-generic
drwxrwxr-x 2 root root 4.0K Mar 20 13:43 efi
drwxr-xr-x 4 root root 4.0K Mar 20 18:05 grub
lrwxrwxrwx 1 root root   27 Mar 20 18:03 initrd.img -> initrd.img-5.4.0-18-generic
-rw-r--r-- 1 root root  79M Mar 20 18:05 initrd.img-5.4.0-14-generic
-rw-r--r-- 1 root root  79M Mar 20 18:05 initrd.img-5.4.0-18-generic
lrwxrwxrwx 1 root root   27 Mar 20 13:46 initrd.img.old -> initrd.img-5.4.0-14-generic
drwx------ 2 root root  16K Mar 20 13:42 lost+found
-rw-r--r-- 1 root root 179K Feb 13 23:09 memtest86+.bin
-rw-r--r-- 1 root root 181K Feb 13 23:09 memtest86+.elf
-rw-r--r-- 1 root root 181K Feb 13 23:09 memtest86+_multiboot.bin
-rw------- 1 root root 4.5M Jan 13 11:26 System.map-5.4.0-1002-oem
-rw------- 1 root root 4.5M Feb  6 22:30 System.map-5.4.0-14-generic
-rw------- 1 root root 4.6M Mar  7 16:23 System.map-5.4.0-18-generic
lrwxrwxrwx 1 root root   24 Mar 20 18:03 vmlinuz -> vmlinuz-5.4.0-18-generic
-rw-r--r-- 1 root root  12M Mar  9 07:53 vmlinuz-5.4.0-14-generic
-rw------- 1 root root  12M Mar  7 16:25 vmlinuz-5.4.0-18-generic
lrwxrwxrwx 1 root root   24 Mar 20 13:49 vmlinuz.old -> vmlinuz-5.4.0-14-generic
root@kali:/# 
root@kali:~# cd /mnt
root@kali:/mnt# stat *5.4.0*
...
  File: System.map-5.4.0-1002-oem
  Size: 4697966         Blocks: 9176       IO Block: 4096   regular file
Device: 805h/2053d      Inode: 13          Links: 1
Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-01-13 11:26:40.000000000 +0000
Modify: 2020-01-13 11:26:40.000000000 +0000
Change: 2020-03-20 13:43:07.934486042 +0000
 Birth: -
  File: System.map-5.4.0-14-generic
  Size: 4714929         Blocks: 9216       IO Block: 4096   regular file
Device: 805h/2053d      Inode: 14          Links: 1
Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-02-06 22:30:48.000000000 +0000
Modify: 2020-02-06 22:30:48.000000000 +0000
Change: 2020-03-20 13:43:08.014486046 +0000
 Birth: -
  File: System.map-5.4.0-18-generic
  Size: 4732337         Blocks: 9248       IO Block: 4096   regular file
Device: 805h/2053d      Inode: 316         Links: 1
Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-20 18:02:55.482467287 +0000
Modify: 2020-03-07 16:23:40.000000000 +0000
Change: 2020-03-20 18:00:44.106622375 +0000
 Birth: -

Limitations/Improvements/Notes

This section will describe limitations, potential improvements, and additional notes about the information that was presented in this article.

Not a suitable replacement for TAILS

TAILS Logo
TAILS is by far the best OS to use for security-critical situations. Click here to learn how to use BusKill in TAILS.

If you are an investigative journalist, activist, or political dissident operating in an oppressive regime where an adversary having access to your Internet activity could cause pain, suffering, or loss-of-life, then you should consider using TAILS.


ATA Secure Erase

Ah, the good 'ol days of magnetic spinning disks. Unfortunately, since the advent of SSDs, wear-leaveling can translate a command to "overwrite this sector" to actually "overwrite who-knows-what."

While LUKS was specifically designed with an anti-forensics information splitter to inflate the size of the master key, making it exponentially less likely that all of the key is remapped (rather than actually overwritten) when attempting to destroy the LUKS header, this technique (quoting the LUKS2 white paper) is "no longer much effective on modern storage devices."

The best way to ensure safe erasure of the LUKS header on an SSD is therefore to use hdparam to initiate an ATA Secure Erase.

Unfortunately, adding an ATA Secure Erase step to your self-destruct trigger necessarily means that all of your data is permanently lost, so even restoring a backup of the LUKS header would not permit data recovery in the event of a false-positive trigger.

Block Backup/Restore

In most cases, the LUKS header could be restored (regaining access to the drive's data) after self-destruct sequence if a backup were made of the LUKS header.

The procedure for making a backup of the LUKS header and restoring it is outside the scope of this article, but suffice it to say that such a backup should probably be encrypted using something like `gpg`, `openssl` or LUKS, and that backup should be physically located in safe space, such as a lock box in an free country, avoiding the 14 eyes, and any countries that limit digital encryption rights.

Detached LUKS Header

LUKS supports using a detached header. With this configuration, your actual encrypted storage drive could be setup without any LUKS header actually stored on it--giving you total plausible deniability as your entire drive's contents would be indistinguishable from a random distribution of bits.

Using a detached LUKS header, you could in theory get a 2-32 MB USB drive to store your LUKS header on, then have BusKill just overwrite the entire drive when triggered. This is a more robust solution to ensure your master key's destruction (less error prone, even for future iterations of LUKS), and it would also be more fail-safe to recover from a false-positive: just download your (client-side encrypted) LUKS header from the cloud or a safety deposit box located in a free country, and use that to decrypt & access your data again.

Cold Boot Attacks

One theoretical attack against an anti-forensics tool like the BusKill self-destruct trigger described in this article is a so-called Cold Boot attack.

As far as I know, there is no evidence to suggest that a single political prisoner was caught or convicted by an opressive regime due to the results of a cold boot attack. At the time of writing, cold boot attacks exist in a purely academic realm.

Nevertheless, a welcome improvement to this BusKill self-destruct trigger would be to add a step to overwrite the RAM prior to shutdown. But such an implementation should be very carefully tested to ensure that it actually functions as intended across all linux distros--and that it doesn't prevent the machine from being able to actually power-off. Otherwise, a poor implementation could actually make your self-destruct sequence less secure.

If your risk model is high enough to require defenses against cold boot attacks, you should thoroughly consider switching to TAILS--which has a well-tested implementation of RAM shredding.

Or you could just use an ultrabook with RAM soldered in-place. Or get yourself some superglue.

Unencrypted Drives unaffected

This trigger has proven to leave zero digital persional evience on the laptop after the buskill trigger, but it was far from leaving zero digital forensic evidence.

By default, Ubuntu still has over a gig of unencrypted partitions that have a ton of useful forensic data that is not wiped by the self-destruct sequence.

I intentionally designed this self-destruct trigger not to wipe this data because

  1. These unencrypted partitions should only be storing generic data. While it will tell a story of what OS you're using, which version, and potentially when you downloaded updates, it shouldn't have any files here that are unique to you and don't already exist on thousands of other machines around the world
     
  2. Writing >1G of random data to disk takes time. The self-destruct sequence described in this article was intentionally designed to prioritize cutting power to the RAM ASAP.

That said, a welcome improvement may be a partitioning scheme that minimizes the size of the necessarily unencrypted partitions so that they could be quickly overwritten during the self-destruct sequence.

PRs Welcome!

Don't like this BusKill self-destruct trigger implementation? Wish it did a RAM wipe? Wish it did an ATA secure erase? Wish it wiped all drives (not just LUKS ones)? Or wish it didn't wipe all LUKS drives (only the rootfs one)?

Feel free to fork our buskill-linux repo on github, modify the buskill-selfdestruct.sh trigger, add your own trigger, and send us your a pull request.

Just be sure to share your work with the community using #BusKill on twitter or mastodon. And if you'd like your own trigger to be featured on this blog, feel free to contact us about it.

Further Reading

  1. New Methods in Hard Disk Encryption (Anti-Forensics Information Splitter) by Clemens Fruhwirth, 2004.
  2. LUKS1 On-Disk Format Specification by Clemens Fruhwirth and Milan Broz, 2005-2018.
  3. Cryptsetup FAQ (outdated)
  4. Wipe LUKS Header - Arch Wiki

Similar Projects

  1. Nuke My LUKS (doesn't work for LUKS2)
  2. USBKill
  3. Silk Guardian
  4. USB Guard
  5. Suicide Crypt
  6. Bus Killer (No relation)

If you'd like to purchase a BusKill cable, click here.

Michael Altfield

View posts by Michael Altfield
Hi, I'm Michael Altfield. I write articles about opsec, privacy, and devops About Michael

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top