CREATIVE CHAOS   ▋ blog

Encrypted USB Drive (rev)

PUBLISHED ON 17/05/2023 — EDITED ON 11/12/2023 — 247CTF, INFOSEC

You are presented with a zip file containing the following files:

encrypted_usb.dd
README.txt
recovery_keys_dump.txt

README

Urgent Incident Response help needed!

We have been contacted by a key client, whose external storage devices have all been encrypted by some new and unknown ransomware variant.

Important files which have not been backed up have been encrypted and the client needs access to the files from 1 specific device urgently.

The drive uses BitLocker encryption; however, it was mounted at the time of the attack.

The client will not disclose their BitLocker password; however, we do have access to the BitLocker recovery keys from the asset management team.

Unfortunately, the asset management team didn’t map the recovery keys to specific devices, so we only have a company-wide dump.

The external storage device image (encrypted_usb.dd) and BitLocker recovery key dump (recovery_keys_dup.txt) are attached.

Can you help?

recovery_keys_dump.txt

516735-656957-925056-932288-472819-186862-770136-696813
209418-618481-221875-863288-199571-558844-456147-816495
289237-659109-210156-431433-506114-195851-203202-783829
...

The number of BitLocker keys makes it challenging to try to find the right one by hand.

cat recovery_keys_dump.txt| wc -l
1000

First we need to mount the dd file as block device on the system (Kali). By associating the file with a loop device, we can treat the file as if it were a physical block device such as a hard drive or USB drive. This allows us to perform various operations on the file, such as reading its contents, writing to it, or manipulating it as if it were a real storage device.

losetup /dev/loop0 encrypted_usb.dd

If you don’t have loop load the appropriate kernel module.

modprobe loop

Lets brute force all the keys found in recovery_keys_dump.txt against the encrypted volume. We will use the tool dislocker to try to decrypt BitLocker-encrypted volume. If the decryption process succeeds and the correct key is found, the dislocker command will unlock the encrypted volume under the image/ subfolder the correct key that successfully decrypted the volume will be presented.

mkdir image

while read line; do sudo dislocker -r -V /dev/loop0 -p$line image/ && echo $line; done < recovery_keys_dump.txt

334565-564641-129580-248655-292215-551991-326733-393679

Mount the decrypted dislocker-file to /media/mount.

mount -o loop image/dislocker-file /media/mount

Contents of /media/mount

crypto_passphrase.png.xxx.crypt
cryptor
do_not_open.png.xxx.crypt
meeting_minutes.png.xxx.crypt
passwords.png.xxx.crypt
ransom.txt
salary_screenshot.png.xxx.crypt

ransom.txt

Oh no! Your files are encrypted!

Your files have been encrypted using a secure xor encryption algorithm and are completely unrecoverable!

To decrypt your files, you need your secret encryption key.

To retrieve your secret encryption key, you will need to pay 50 BOTCIINS – otherwise your secret encryption key will be
deleted and you will never be able to access your files again!%

The important detail is the mention of XOR, so we know how we can create our own code to decrypt the files.

To do that, we will write some code, but first to have at least remote success, we need to know the charset and lenght of the passphrase used to encrypt the files.

I got the answer from the hint, but also tried this non-scientific method:

./crypto aaAa
exit code 1
./crypto a1aa
exit code 1
./crypto aaaaa
exit code 1
./crypto a!aa
exit code 1
./crypto aaaa
exit code 0

We can see that lenght is 4 char and the charset is lowercase alpha.

As we know the correct “magic bytes” for PNG files, we can try to brute force each combination. The magic bytes are \x89\x50\x4E\x47\x0D\x0A\x1A\x0A so we need to XOR only first 8 bytes of each file with the potential passphrase. When the bytes are the same, we found our answer.

The solution:

import os
import itertools

# Define the PNG magic bytes
png_magic_bytes = b'\x89\x50\x4E\x47\x0D\x0A\x1A\x0A'

# Define the character space for the password
character_space = 'abcdefghijklmnopqrstuvwxyz'

# Generate all possible combinations of the password
# We know that the passwords is 4 lowercase characters
passwords = itertools.product(character_space, repeat=4)

# Path to the encrypted file
encrypted_file = 'do_not_open.png.xxx.crypt'
#encrypted_file = 'crypto_passphrase.png.xxx.crypt'
#encrypted_file = 'meeting_minutes.png.xxx.crypt'
#encrypted_file = 'passwords.png.xxx.crypt'
#encrypted_file = 'salary_screenshot.png.xxx.crypt'


# Path to save the decrypted file
decrypted_file = 'do_not_open.png'
#decrypted_file = 'crypto_passphrase.png'
#decrypted_file = 'meeting_minutes.png'
#decrypted_file = 'passwords.png'
#decrypted_file = 'salary_screenshot.png'

# Function to perform XOR encryption on a file with a given password
def xor_encrypt_file(file_path, password):
    with open(file_path, 'rb') as file:
        file_bytes = file.read()

    password_bytes = password.encode()
    encrypted_bytes = bytearray()

    for i, byte in enumerate(file_bytes):
        encrypted_byte = byte ^ password_bytes[i % len(password_bytes)]
        encrypted_bytes.append(encrypted_byte)

    return encrypted_bytes

# Read the first 8 bytes from the encrypted file
with open(encrypted_file, 'rb') as f:
    encrypted_bytes = f.read(8)

# Iterate through each password combination and check if it decrypts the file successfully
for password in passwords:
    password_str_4char = ''.join(password)
    # We need 8 bytes long password for test XOR
    password_str = password_str_4char+password_str_4char
    decrypted_bytes = b''

    # XOR each byte with the corresponding byte from the password
    for i in range(8):
        decrypted_byte = encrypted_bytes[i] ^ ord(password_str[i])
        decrypted_bytes += bytes([decrypted_byte])

    # Check if the decrypted bytes match the PNG magic bytes
    if decrypted_bytes == png_magic_bytes:
        print(f"Correct password found: {password_str_4char}")
        print(f"Decrypted file saved as: {decrypted_file}")
        # Decrypt the encrypted file using the current password
        decrypted_bytes = xor_encrypt_file(encrypted_file, password_str_4char)

        # Write the decrypted bytes to a file
        with open(decrypted_file, 'wb') as file:
            file.write(decrypted_bytes)
        break