{"id":185,"date":"2026-02-03T09:00:00","date_gmt":"2026-02-03T09:00:00","guid":{"rendered":"http:\/\/justruss.tech\/index.php\/2024\/01\/28\/lockpick-3-0-htb-forensics-hard\/"},"modified":"2026-05-15T10:34:55","modified_gmt":"2026-05-15T10:34:55","slug":"lockpick-3-0-htb-forensics-hard","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2026\/02\/03\/lockpick-3-0-htb-forensics-hard\/","title":{"rendered":"Lockpick 3.0 | HTB Forensics (Hard)"},"content":{"rendered":"<p>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.<\/p>\n<h3>Initial image triage<\/h3>\n<pre>sudo mount -o ro,loop,offset=$((512*2048)) lockpick.img \/mnt\/lockpick\nfind \/mnt\/lockpick\/Users -newer \/mnt\/lockpick\/Windows\/System32\/ntoskrnl.exe \\\n  -not -path \"*\/AppData\/Local\/Temp\/Low\/*\" 2&gt;\/dev\/null | head -20<\/pre>\n<p>The ransomware binary is still in AppData\\Local\\Temp as svchost32.exe, named to blend in with the legitimate svchost.exe processes in System32.<\/p>\n<h3>Unpacking the binary<\/h3>\n<pre>die svchost32.exe\n# PE32: compiler: Python 3.x (PyInstaller)\n\npython3 pyinstxtractor.py svchost32.exe\nuncompyle6 svchost32.exe_extracted\/svchost32.pyc &gt; svchost32.py<\/pre>\n<h3>The encryption flaw<\/h3>\n<p>The decompiled source shows the key derivation:<\/p>\n<pre>import hashlib\nfrom Crypto.Cipher import AES\nfrom Crypto.Util.Padding import pad, unpad\n\nVICTIM_ID = open(\"C:\\\\ProgramData\\\\victim_id.txt\").read().strip()\n\ndef get_key_iv(victim_id):\n    key = hashlib.sha256(victim_id.encode()).digest()\n    iv  = hashlib.md5(victim_id.encode()).digest()\n    return key, iv<\/pre>\n<p>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.<\/p>\n<h3>Decrypting the files<\/h3>\n<pre>python3 &lt;&lt; EOF\nimport hashlib, glob\nfrom Crypto.Cipher import AES\nfrom Crypto.Util.Padding import unpad\n\nvictim_id = open(\"\/mnt\/lockpick\/ProgramData\/victim_id.txt\").read().strip()\nkey = hashlib.sha256(victim_id.encode()).digest()\niv  = hashlib.md5(victim_id.encode()).digest()\n\nfor locked in glob.glob(\"\/mnt\/lockpick\/Users\/victim\/Documents\/*.locked\"):\n    with open(locked, \"rb\") as f:\n        data = f.read()\n    cipher = AES.new(key, AES.MODE_CBC, iv)\n    try:\n        plain = unpad(cipher.decrypt(data), AES.block_size)\n        with open(locked.replace(\".locked\", \"\"), \"wb\") as f:\n            f.write(plain)\n        print(f\"Decrypted: {locked}\")\n    except Exception as e:\n        print(f\"Failed {locked}: {e}\")\nEOF<\/pre>\n<p>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.<\/p>\n<h3>The lesson<\/h3>\n<p>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&#8217;s public RSA key, and discards the symmetric key. Without the attacker&#8217;s private key decryption is impossible. Using the victim ID as the sole entropy source, stored on the victim&#8217;s own machine, makes the entire scheme trivially reversible once you understand the implementation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A ransomware decryption challenge where the encryption scheme looks custom at first<br \/>\nbut turns out to be a misused AES implementation.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-185","post","type-post","status-publish","format-standard","hentry","category-hackthebox"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=185"}],"version-history":[{"count":3,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/185\/revisions"}],"predecessor-version":[{"id":257,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/185\/revisions\/257"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}