Lockpick 3.0 | HTB Forensics (Hard)

28 January 2024 | justruss.tech

Lockpick 3.0 is a Hard-rated HackTheBox forensics challenge. You are given a disk image of a Windows system that has been hit by ransomware. Five flags are hidden across encrypted files, the registry, and memory artefacts. The encryption is not
as strong as it looks.

Initial image mounting and triage

# Mount the image read-only
sudo mkdir /mnt/lockpick
sudo mount -o ro,loop,offset=$((512*2048)) lockpick.img /mnt/lockpick

# Check for the ransomware binary
find /mnt/lockpick -name "*.exe" -newer /mnt/lockpick/Windows/System32/notepad.exe 2>/dev/null
# Returns: /mnt/lockpick/Users/victim/AppData/Local/Temp/svchost32.exe

The binary name is a classic masquerading technique — naming malware after a legitimate Windows process. The real svchost.exe lives in System32 and is never in AppData.

Identifying the packer

$ die svchost32.exe
PE32: compiler: Python 3.x (PyInstaller)
PE32: packer: PyInstaller 5.x

PyInstaller packages Python scripts into standalone executables. The Python bytecode is extractable.

# Extract with pyinstxtractor
python3 pyinstxtractor.py svchost32.exe
# Creates: svchost32.exe_extracted/

# Find the main script
ls svchost32.exe_extracted/
# svchost32.pyc  PYZ-00.pyz  ...

# Decompile the bytecode
pip install uncompyle6
uncompyle6 svchost32.exe_extracted/svchost32.pyc > svchost32.py

Analysing the encryption

The decompiled source reveals the key derivation:

import os, hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

VICTIM_ID = open("C:\ProgramData\victim_id.txt").read().strip()

def get_key_iv(victim_id):
    key = hashlib.sha256(victim_id.encode()).digest()   # 32 bytes
    iv  = hashlib.md5(victim_id.encode()).digest()       # 16 bytes
    return key, iv

def encrypt_file(filepath):
    key, iv = get_key_iv(VICTIM_ID)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    with open(filepath, "rb") as f:
        data = f.read()
    encrypted = cipher.encrypt(pad(data, AES.block_size))
    with open(filepath + ".locked", "wb") as f:
        f.write(encrypted)
    os.remove(filepath)

The victim ID is stored in plaintext at C:\ProgramData\victim_id.txt and also in the ransom note. The key is fully deterministic from that value.

Recovering the victim ID and decrypting

cat /mnt/lockpick/ProgramData/victim_id.txt
# VIC-8821-KXZP

# Decrypt all .locked files
python3 << EOF
import hashlib, os, glob
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

victim_id = "VIC-8821-KXZP"
key = hashlib.sha256(victim_id.encode()).digest()
iv  = hashlib.md5(victim_id.encode()).digest()

for locked in glob.glob("/mnt/lockpick/Users/victim/Documents/*.locked"):
    with open(locked, "rb") as f:
        data = f.read()
    cipher = AES.new(key, AES.MODE_CBC, iv)
    try:
        plain = unpad(cipher.decrypt(data), AES.block_size)
        outpath = locked.replace(".locked", "")
        with open(outpath, "wb") as f:
            f.write(plain)
        print(f"Decrypted: {outpath}")
    except Exception as e:
        print(f"Failed {locked}: {e}")
EOF

Flags 1-4 are embedded in the decrypted document contents as HTB{…} strings.

Flag 5: registry persistence

# Extract the NTUSER.DAT hive
cp /mnt/lockpick/Users/victim/NTUSER.DAT /tmp/

# Parse with regipy
pip install regipy
registry-explorer /tmp/NTUSER.DAT -p "Software\Microsoft\Windows\CurrentVersion\Run"

# Output:
# Key: Software\Microsoft\Windows\CurrentVersion\Run
# Value: WindowsUpdate
# Data:  C:\ProgramData\svchost32.exe /silent
# Modified: 2024-01-15 03:22:41

The persistence key data contained a base64-encoded string appended after the binary path. Decoding it gave Flag 5.

Forensic lesson

The critical flaw in this ransomware is using the victim ID as the sole source of entropy for key derivation. A proper implementation would generate a random symmetric key, encrypt it with an asymmetric public key (keeping the private key on the
attacker’s server), and discard the symmetric key after encryption. Without the attacker’s private key, decryption would be impossible. Deterministic key derivation from publicly visible material makes recovery trivial once you understand the
scheme.