Lockpick 3.0 is a Hard-rated HackTheBox forensics challenge involving a ransomware infection. You are given a disk image and asked to recover five flags. The encryption scheme looks custom at first glance but turns out to be a textbook implementation mistake that makes full recovery possible.
Initial image triage
sudo mount -o ro,loop,offset=$((512*2048)) lockpick.img /mnt/lockpick
find /mnt/lockpick/Users -newer /mnt/lockpick/Windows/System32/ntoskrnl.exe \
-not -path "*/AppData/Local/Temp/Low/*" 2>/dev/null | head -20
The ransomware binary is still in AppData\Local\Temp as svchost32.exe, named to blend in with the legitimate svchost.exe processes in System32.
Unpacking the binary
die svchost32.exe
# PE32: compiler: Python 3.x (PyInstaller)
python3 pyinstxtractor.py svchost32.exe
uncompyle6 svchost32.exe_extracted/svchost32.pyc > svchost32.py
The encryption flaw
The decompiled source shows the key derivation:
import 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()
iv = hashlib.md5(victim_id.encode()).digest()
return key, iv
The victim ID is stored in plaintext on disk and is also in the ransom note. Both the AES key and the IV are deterministically derived from that value alone with no additional entropy. Knowing the victim ID means knowing the key.
Decrypting the files
python3 << EOF
import hashlib, glob
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
victim_id = open("/mnt/lockpick/ProgramData/victim_id.txt").read().strip()
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)
with open(locked.replace(".locked", ""), "wb") as f:
f.write(plain)
print(f"Decrypted: {locked}")
except Exception as e:
print(f"Failed {locked}: {e}")
EOF
Flags 1 through 4 are in the decrypted document contents. Flag 5 is in the Windows registry run key written by the ransomware during execution, recoverable by parsing the NTUSER.DAT hive with regipy.
The lesson
Deterministic key derivation from attacker-visible inputs is a fundamental ransomware design flaw. A properly implemented scheme generates a random symmetric key per victim, encrypts that key with the attacker’s public RSA key, and discards the symmetric key. Without the attacker’s private key decryption is impossible. Using the victim ID as the sole entropy source, stored on the victim’s own machine, makes the entire scheme trivially reversible once you understand the implementation.