All Writings
Feature BreakdownSystems

Solving the Hackerverse DFIR CTF: Ransomware Analysis & Windows Credential Forensics

In this hands-on investigation for the EC-Council Hackerverse CTF Competition (Digital Forensics & Incident Response Track), I analyzed malware artifacts, reversed custom ransomware logic, performed memory forensics, and recovered encrypted browser credentials.

Miracle Oladapo
Miracle Oladapo
Frontend Engineer & Systems Architect
October 2, 2026•4 min read
CTF Hackerverse
CTF EC-Council

In this hands-on investigation for the EC-Council Hackerverse CTF Competition (Digital Forensics & Incident Response Track), I analyzed malware artifacts, reversed custom ransomware logic, performed memory forensics, and recovered encrypted browser credentials.

Competition Overview

This edition of Hackerverse focuses on Digital Forensics, covering steganography, network forensics, Windows credential analysis, and malware/ransomware analysis.

Across four progressive levels, I investigated hidden artifacts, reconstruct attacker activity from network traffic, recover credentials from forensic evidence, and analyze ransomware to recover encrypted data.

Each level introduces a different forensic investigation scenario, requiring to identify artifacts, analyze evidence, and uncover the information needed to progress.


What I Did: A Step-by-Step Breakdown

Level 1: Cryptic Canvas – "The Hidden Voice"

Scenario & Artifacts

An audio file named Audio.wav in /root/Stego Artifacts on Kali Linux contained Morse code embedded inside acoustic frequencies.

Initial Friction: Running CLI automated decoders (morse2ascii) produced timing distortions (tttta etttn...) due to incorrect word-per-minute (WPM) and silence gap detection.

  • Cleaned the audio frequency range using sox:
bash
sox "/root/Stego Artifacts/Audio.wav" -c 1 clean_audio.wav highpass 300 lowpass 1200 norm -1
  • Processed clean_audio.wav with a custom Python script using scipy.io.wavfile and numpy to measure signal pulse durations, split dot/dash thresholds, and output the raw bitstream.
  • Discovered a 6-bit lead-in offset skewing standard byte boundaries. Wrote a Python bit-shift scanner to align byte boundaries across 8-bit shifts:
python
bits = "10011001101100011000010110011101111011011000100110100101101110011000010111001001111001010111110110000101110101011001000110100101101110010111110111001101100101011000110111001001100101011101000111001101111101"

for shift in range(8):
  shifted = bits[shift:]
  bytes_list = [
      int(shifted[i : i + 8], 2)
      for i in range(0, len(shifted) // 8 * 8, 8)
  ]
  text = "".join(chr(b) if 32 <= b <= 126 else "?" for b in bytes_list)
  if "flag" in text.lower():
    print(f"Shift {shift}: {text}")

Extracted the embedded secret file from fixed_cat.jpg using steghide:

bash
steghide extract -sf fixed_cat.jpg -p ''

Decoded the resulting Base64 string inside flag.txt:

bash
echo "Q1RGe3N0ZWcwXzFzX2NvMGx9" | base64 -d

Level 2: Network Intrusion Forensics – "ChatOps Breached"

Scenario & Artifacts

Synixon Technologies experienced a security breach on their newly deployed ChatOps collaboration platform. The Security Operations Center (SOC) flagged unapproved password resets, unauthorized account creation, and abnormal API requests. The network packet capture synixon_breach_capture.pcapng was retrieved from /root/Pcap Artifacts on Kali Linux.

  1. Protocol Hierarchy & Traffic Baseline: Opened synixon_breach_capture.pcapng in tshark to establish baseline protocol distribution, focusing on HTTP/HTTPS, TCP sessions, and REST API interactions.
  2. Exploitation Chain Reconstruction:
    • Filtered for HTTP POST/PUT requests directed at user management and authentication endpoints (/api/v1/auth, /api/v1/users/reset-password).
    • Tracked session tokens and parameter tampering payloads used by the attacker to bypass authentication controls and trigger unauthorized password resets.
  3. Impact & Endpoint Mapping: Isolated the attacker's IP address, enumerated the newly created privileged accounts, and reconstructed exported database records and stolen API secrets.

Level 3: Windows Credential Forensics – "The ChromeCracker"

Scenario & Artifacts

An attacker accessed several online services on a compromised Windows system, including chaincrypto.com. The objective was to recover saved browser credentials by analyzing an LSASS memory dump (lsass.DMP) and Google Chrome profile data stored inside chrome_cracker.zip on the Desktop.

Step 1: Memory Forensics & DPAPI MasterKey Extraction

  • Extracted chrome_cracker.zip to expose lsass.DMP and the Google\Chrome\User Data directory structure.
  • Parsed the LSASS process dump using pypykatz to dump LSA secrets and DPAPI master keys:
bash
pypykatz lsa minidump "C:\Users\LabUser\Desktop\chrome_cracker\chrome_cracker\lsass.DMP"
  • Located the LogonSession corresponding to the user Admin (LUID 658540, SID S-1-5-21-1077705597-68872450-308104863-1001).
  • Extracted the 128-character hex DPAPI MasterKey tied to key GUID f7ecfe82-d268-4507-8761-3ccdbef6496b:9c3bca41e3e8ca91ce49a1837f2c2c6fbceb141578e0131e83ab64f81de05ffc56a43082969875842346c6f16a7fd6607a80f4565901293e0a96c48478237e48
  • Retained the first 10 characters for Challenge 4:

Step 2: Chrome Master AES Key Decryption

  • Located Chrome's Local State JSON file containing os_crypt.encrypted_key.
  • Decoded the Base64 string and stripped the initial 5-byte DPAPI magic header to expose the raw DPAPI blob.
  • Decrypted the DPAPI blob using the extracted MasterKey to reveal Chrome's raw 32-byte (64-hex character) AES-256 key.

Step 3: SQLite Query & Password Decryption

  • Queried Chrome's Login Data SQLite database for the target site:
bash
SELECT origin_url, username_value, password_value FROM logins WHERE origin_url LIKE '%chaincrypto%';
  • Extracted the encrypted password blob containing the v10 prefix (bytes 0–2), 12-byte initialization vector (bytes 3–14), and payload plus GCM tag.
  • Decrypted the payload using AES-256-GCM via Python's cryptography library (AESGCM), yielding the plaintext username and password for chaincrypto.com.
python
import base64, json, sqlite3, subprocess
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

masterkey_hex = '9c3bca41e3e8ca91ce49a1837f2c2c6fbceb141578e0131e83ab64f81de05ffc56a43082969875842346c6f16a7fd6607a80f4565901293e0a96c48478237e48'
local_state_path = r'C:\Users\LabUser\Desktop\chrome_cracker\chrome_cracker\Google\Chrome\User Data\Local State'
login_data_path = r'C:\Users\LabUser\Desktop\chrome_cracker\chrome_cracker\Google\Chrome\User Data\Default\Login Data'

with open(local_state_path, 'r', encoding='utf-8') as f:
    enc_key_b64 = json.load(f)['os_crypt']['encrypted_key']
enc_bytes = base64.b64decode(enc_key_b64)[5:]

with open('blob.bin', 'wb') as f:
    f.write(enc_bytes)

# Decrypt DPAPI blob via pypykatz CLI
out = subprocess.run(f'pypykatz dpapi blob -k {masterkey_hex} blob.bin', shell=True, capture_output=True, text=True).stdout

# AES-256-GCM Password Decryption
aesgcm = AESGCM(decrypted_aes_key)
conn = sqlite3.connect(login_data_path)
for url, user, enc_pass in conn.execute("SELECT origin_url, username_value, password_value FROM logins WHERE origin_url LIKE '%chaincrypto%'"):
    plaintext_password = aesgcm.decrypt(enc_pass[3:15], enc_pass[15:], None).decode('utf-8')
    print(f"Credentials: {user} / {plaintext_password}")
I couldn't solve the last 3 questions in Challenge 4: Enter the starting 10 characters of the masterkey. Challenge 5: Enter the starting 10 characters of the Final Secret Key. (Answer Format: NxxNxxxNxx). Challenge 6: What is the username and password? (Answer Format: XxxxXxxxxxxx / X@$$xNxx_XxxNxx!NN)

Level 4: Ransomware Analysis and Decryption – "The RansomBreak"

Scenario & Artifacts

Ransomware (Rasome.exe) encrypted target documents inside C:\Users\Admin\Documents\CTF_Vault\ on the Windows Victim VM, appending .locked to filenames and creating a ransom note (READ_ME.txt).

Step 1: Binary Unpacking & Decompilation

  1. Identified that Rasome.exe was packed using MPRESS executable compression.
  2. Executed de4dot to strip the packer and reconstruct valid .NET Intermediate Language (IL) metadata, generating Rasome-cleaned.exe.
  3. Loaded Rasome-cleaned.exe into dnSpyEx to analyze the decompiled C# assembly code inside the Rasome.Program class.

Step 2: Cryptographic Logic Reverse Engineering

Analysis of Rasome.Program decompiled source code revealed:

  • Key Generation Method: GenerateKeyFromMachineNameMD5. It converts Environment.MachineName.ToUpperInvariant() to UTF-8 bytes and computes an MD5 hash.
    • System machine name: WIN10.
    • Calculated AES Key (Hex): MD5("WIN10") = 037E39A5C57380EA9357167684CA4DD7.
  • IV Generation Method: GetIV(string fileName). Computes the SHA-256 hash of the filename string (e.g., "Flag.txt") and extracts the first 16 bytes.
  • Encryption Scheme: AES-128 in CBC mode with PKCS7 padding.
bash
// Decompiled key derivation snippet from dnSpyEx
private static byte[] GenerateKeyFromMachineNameMD5() {
    string text = Environment.MachineName.ToUpperInvariant();
    using (MD5 md = MD5.Create()) {
        return md.ComputeHash(Encoding.UTF8.GetBytes(text));
    }
}

Step 3: Custom Native Decryption Scripting

To decrypt Flag.txt.locked without relying on missing third-party Python modules, we wrote a Python script interfacing directly with the Windows Native Cryptography API (bcrypt.dll) via Python's ctypes library:

python
import ctypes, hashlib

machine_name = 'WIN10'
key_bytes = hashlib.md5(machine_name.encode('utf-8')).digest()
iv_bytes = hashlib.sha256('Flag.txt'.encode('utf-8')).digest()[:16]

with open(r'C:\Users\Admin\Documents\CTF_Vault\Flag.txt.locked', 'rb') as f:
    ciphertext = f.read()

# Win32 BCrypt setup
bcrypt = ctypes.windll.bcrypt

class BCRYPT_KEY_DATA_BLOB_HEADER(ctypes.Structure):
    _fields_ = [("dwMagic", ctypes.c_uint32), ("dwVersion", ctypes.c_uint32), ("cbKeyData", ctypes.c_uint32)]

header = BCRYPT_KEY_DATA_BLOB_HEADER(0x4D42444B, 1, len(key_bytes)) # "KDBM"
blob = bytes(header) + key_bytes

hAlg, hKey = ctypes.c_void_p(), ctypes.c_void_p()
bcrypt.BCryptOpenAlgorithmProvider(ctypes.byref(hAlg), "AES", None, 0)
bcrypt.BCryptSetProperty(hAlg, "ChainingMode", "ChainingModeCBC".encode('utf-16le'), 28, 0)
bcrypt.BCryptImportKey(hAlg, None, "KeyDataBlob", ctypes.byref(hKey), None, 0, blob, len(blob), 0)

cbOutput = ctypes.c_uint32()
output = ctypes.create_string_buffer(len(ciphertext))
iv_buf = ctypes.create_string_buffer(iv_bytes, len(iv_bytes))

bcrypt.BCryptDecrypt(hKey, ciphertext, len(ciphertext), None, iv_buf, len(iv_buf), output, len(ciphertext), ctypes.byref(cbOutput), 0)

# Unpad PKCS7
raw = output.raw[:len(ciphertext)]
pad_val = raw[-1]
flag = raw[:-pad_val].decode('utf-8')
print(f"Decrypted Flag: {flag}")

Forensic Challenges & Remediation Matrix

Environment FrictionTechnical ImpactEngineering Remediation
MPRESS Packer ObfuscationHidden C# IL bytecode prevented direct decompilation in dnSpyEx. Unpacked Rasome.exe using de4dot CLI to reconstruct metadata.
Missing pycryptodome / CryptoPython scripts threw ModuleNotFoundError during AES routines.Replaced third-party cryptography dependencies with native Win32 bcrypt.dll calls via ctypes.
PowerShell Syntax Variable StrippingInline PowerShell scripts stripped $variable names when passed as quotes.Converted inline PowerShell snippets into dedicated, standalone Python scripts.
pypykatz Import Path RefactoringVersion 0.6.13 internal path shifts broke from pypykatz.dpapi.dpapi import DPAPIBlobEncapsulated pypykatz dpapi blob execution inside a subprocess pipe.
Output Byte TruncationIncomplete buffer reads clipped the trailing flag charactersAdjusted buffer array sizing and modified PKCS7 unpadding logic to print the full flag string.

Tags & Topics
#CTF#hackervers#digital forensics#ransomware#network forensics#steganography#malware

Found this perspective valuable?

Share with engineers and founders or discuss it on Twitter / X.