Encrypted USB Drive
An important USB drive containing sensitive information has been encrypted by some new ransomware variant. Can you reverse the ransomware encryption function and recover the files?
1. Initial Analysis
We are provided with a BitLocker-encrypted USB image (encrypted_usb.dd) and a large list of potential recovery keys (recovery_keys_dump.txt).
❯ file encrypted_usb.ddencrypted_usb.dd: DOS/MBR boot sector, code offset 0x58+2, OEM-ID "-FVE-FS-", ... FAT (32 bit) ... NTFS ...The goal is to find the correct recovery key, mount the image, and then deal with the “ransomware” that has encrypted the files inside.
2. BitLocker Decryption
Attempting John the Ripper
First, I tried using bitlocker2john to extract the hash and then cracked it with the provided recovery keys.
❯ bitlocker2john -i encrypted_usb.dd > hash❯ john --wordlist=./recovery_keys_dump.txt --fork=6 hash...0 password hashes cracked, 4 leftJohn didn’t seem to find the key directly (possibly due to format mismatch or configuration). Instead of debugging the hash format, I moved to a more direct approach: brute-forcing the mount command using dislocker.
Brute-forcing with Dislocker
I wrote a simple bash script to iterate through the recovery_keys_dump.txt and attempt to mount the image.
#!/usr/bin/env bash
IMG_FILE="encrypted_usb.dd"MNT_DIR="/mnt/bitlocker_img"USB_DIR="/mnt/unlocked_usb"KEYS_FILE="recovery_keys_dump.txt"
if [[ $EUID -ne 0 ]]; then echo "[-] Error: This script must be run as root." exit 1fi
mkdir -p "$MNT_DIR" "$USB_DIR"
while IFS= read -r key; do # -p specifies the recovery password if dislocker -V "$IMG_FILE" -p"$key" -- "$MNT_DIR" 2>/dev/null; then echo -e "\n[+] Recovery Key Found: $key" break fidone < "$KEYS_FILE"Running the script successfully identified the key:
[+] Recovery Key Found: 334565-564641-129580-248655-292215-551991-326733-393679
❯ sudo mount /mnt/bitlocker_img/dislocker-file /mnt/unlocked_usb3. Ransomware Analysis
Inside the mounted drive, we find several encrypted files and the ransomware binary itself:
❯ ls -lh /mnt/unlocked_usb/total 3.2M-rwxrwxrwx 1 root root 474K Oct 8 2022 crypto_passphrase.png.xxx.crypt-rwxrwxrwx 1 root root 15K Oct 8 2022 cryptor-rwxrwxrwx 1 root root 9.2K Oct 8 2022 do_not_open.png.xxx.crypt-rwxrwxrwx 1 root root 133K Oct 8 2022 meeting_minutes.png.xxx.crypt-rwxrwxrwx 1 root root 889K Oct 8 2022 passwords.png.xxx.crypt-rwxrwxrwx 1 root root 386 Oct 8 2022 ransom.txt-rwxrwxrwx 1 root root 1.7M Oct 8 2022 salary_screenshot.png.xxx.cryptThe ransom.txt claims to use a “secure XOR encryption algorithm”.
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.4. Recovery (Known Plaintext Attack)
Since the files are PNGs, we can perform a Known Plaintext Attack. We know that PNG files always start with the same 8-byte magic header: 89 50 4E 47 0D 0A 1A 0A.
By XORing the first 8 bytes of an encrypted file with this known PNG header, we can recover the XOR key.
Using CyberChef:
- Input the first few bytes of
do_not_open.png.xxx.crypt. - XOR with the PNG magic bytes
89 50 4E 47 0D 0A 1A 0A. - The result reveals the key repeats as
66 63 6f 79(ASCII:fcoy).
Applying the XOR key fcoy to the entire file do_not_open.png.xxx.crypt recovers the original image containing the flag.
247CTF{[flag redacted]}
5. Deep Dive: Reversing the cryptor Binary
I was curious about how the binary actually worked, so I threw it into IDA Pro.
Main Logic
The program expects a 4-character key as a command-line argument. It then iterates through the current directory, looking for files with the .xxx extension.
__int64 __fastcall main(int a1, char **a2, char **a3){ // ... // Key must be exactly 4 bytes long if ( a1 != 2 || strlen(a2[1]) != 4 || (unsigned int)check_key_validity(a2[1]) != 1 ) return 1;
dirp = opendir("."); if ( dirp ) { while ( (v5 = readdir(dirp)) != 0 ) { if ( (unsigned int)is_target_extension(v5->d_name) == 1 ) { strcpy(dest, v5->d_name); strcat(dest, ".crypt"); // Append .crypt to original name encrypt_file(v5->d_name, dest, a2[1]); } } closedir(dirp); } return 0;}Encryption Function
The encryption is indeed a simple byte-by-byte XOR using the 4-byte key provided.
unsigned __int64 __fastcall encrypt_file(char *source, char *dest, char *key){ // ... stream = fopen(source, "rb"); v16 = fopen(dest, "wb"); key_len = strlen(key);
do { // Read chunks matching the key length bytes_read = fread(ptr, 1u, key_len, stream); for ( i = 0; i < bytes_read; ++i ) *((_BYTE *)ptr + i) ^= key[i]; // XOR logic fwrite(ptr, 1u, bytes_read, v16); } while ( bytes_read == key_len );
fclose(stream); fclose(v16); return ...;}The analysis confirms the Known Plaintext Attack was the correct approach, as the key length was short (4 bytes) and applied cyclically.