forensicsfreehard

TrueSecrets

hackthebox

Our cybercrime unit has been investigating a well-known APT group for several months. The group has been responsible for several high-profile attacks on corporate organizations. However, what is interesting about that case, is that they have developed a custom command & control server of their own.

$ ls tags/ techniques/
memory_dump_analysistruecrypt_password_recovery_from_memorytruecrypt_container_decryptionaes256_xts_decryptionpbkdf2_hmac_sha512des_cbc_decryptionfat12_extractionc2_source_code_analysisprocess_memory_dump

TrueSecrets — HackTheBox

Description

Our cybercrime unit has been investigating a well-known APT group for several months. The group has been responsible for several high-profile attacks on corporate organizations. However, what is interesting about that case, is that they have developed a custom command & control server of their own. Fortunately, our unit was able to raid the home of the leader of the APT group and take a memory capture of his computer while it was still powered on. Analyze the capture to try to find the source code of the server.

Files

  • TrueSecrets.raw — 200MB Windows memory dump (extracted from ZIP with password hackthebox)

Analysis

Step 1: Initial Analysis — OS Identification

Used Volatility3 windows.info to identify the system:

  • Windows 7 SP1 32-bit (build 7601)
python3 -m volatility3 -f TrueSecrets.raw windows.info

Step 2: Process Analysis

Ran windows.pslist and identified key processes:

PIDProcessSignificance
2128TrueCrypt.exeTrueCrypt disk encryption — the challenge name hint
21767zFM.exe7-Zip, opened backup_development.zip
3212DumpIt.exeMemory acquisition tool
python3 -m volatility3 -f TrueSecrets.raw windows.pslist

Step 3: File Discovery & Extraction

Ran windows.filescan and found two critical files:

OffsetFile
0xbbf6158\Users\IEUser\Documents\backup_development.zip
0xc50c550\Users\IEUser\Documents\development.tc

Extracted backup_development.zip (304KB) from memory:

python3 -m volatility3 -f TrueSecrets.raw windows.dumpfiles --virtaddr 0xbbf6158

Unzipped to get development.tc — a 300KB TrueCrypt encrypted container.

Step 4: TrueCrypt Password Recovery from Process Memory

This is the critical step. TrueCrypt caches passwords in process memory as a struct:

  • 4-byte length field + password bytes (up to 64) + null padding

Dumped TrueCrypt process memory (PID 2128):

python3 -m volatility3 -f TrueSecrets.raw windows.memmap --pid 2128 --dump

This produced a 69MB process dump. Wrote a Python script to scan for the cached password pattern:

#!/usr/bin/env python3 """Scan TrueCrypt process memory for cached password struct.""" import struct with open("pid.2128.dmp", "rb") as f: data = f.read() for i in range(len(data) - 68): length = struct.unpack_from("<I", data, i)[0] if 4 <= length <= 30: candidate = data[i+4:i+4+length] padding = data[i+4+length:i+68] if all(32 <= b < 127 for b in candidate) and padding.count(b'\x00') > len(padding) * 0.9: print(f"Offset {i:#x}: length={length}, password={candidate.decode()}")

Recovered password: X2Hk2XbEJqWYsh8VdbSYg6WpG9g7

Step 5: TrueCrypt Container Decryption

No TrueCrypt CLI available, so wrote a custom Python decryption script implementing the full TrueCrypt decryption chain:

#!/usr/bin/env python3 """Custom TrueCrypt container decryption (AES-256-XTS).""" from Crypto.Cipher import AES from Crypto.Protocol.KDF import PBKDF2 from Crypto.Hash import SHA512, HMAC import struct password = b"X2Hk2XbEJqWYsh8VdbSYg6WpG9g7" with open("development.tc", "rb") as f: container = f.read() # TrueCrypt header structure: # Bytes 0-63: Salt # Bytes 64-511: Encrypted header salt = container[:64] enc_header = container[64:512] # Derive header key: PBKDF2-HMAC-SHA512, 1000 iterations, 128 bytes output header_key = PBKDF2( password, salt, dkLen=128, count=1000, prf=lambda p, s: HMAC.new(p, s, SHA512).digest() ) # Decrypt header with AES-256-XTS (first 64 bytes of derived key) key1 = header_key[:32] # AES key key2 = header_key[32:64] # XTS tweak key cipher = AES.new(key1 + key2, AES.MODE_XTS) dec_header = cipher.decrypt(enc_header) # Verify: first 4 bytes should be "TRUE" assert dec_header[:4] == b"TRUE", "Decryption failed!" # Extract master key (64 bytes at offset 192) master_key = dec_header[192:256] # Volume metadata from header data_offset = struct.unpack_from(">Q", dec_header, 28)[0] # 131072 data_size = struct.unpack_from(">Q", dec_header, 36)[0] # 45056 # Decrypt data area sector-by-sector (512-byte sectors) data_area = container[data_offset:data_offset + data_size] decrypted = bytearray() mk1 = master_key[:32] mk2 = master_key[32:64] for sector_num in range(len(data_area) // 512): sector_data = data_area[sector_num * 512:(sector_num + 1) * 512] tweak = struct.pack("<QQ", sector_num, 0) # 16-byte tweak cipher = AES.new(mk1 + mk2, AES.MODE_XTS, tweak=tweak) decrypted.extend(cipher.decrypt(sector_data)) with open("decrypted_volume.img", "wb") as f: f.write(decrypted)

The decrypted volume was identified as a FAT12 filesystem.

Step 6: Filesystem Extraction

Used 7-Zip to extract the FAT12 image:

7z x decrypted_volume.img

Contents:

malware_agent/
  AgentServer.cs              # C2 server source code (C#)
  sessions/
    5818acbe-68f1-4176-a2f2-8c6bcb99f9fa.log.enc
    c65939ad-5d17-43d5-9c3a-29c6a7c31a32.log.enc
    de008160-66e4-4d51-8264-21cbc27661fc.log.enc

Step 7: C2 Source Code Analysis

AgentServer.cs is a C# TCP C2 server listening on port 40001:

  • Receives commands from operator, sends to infected machines
  • Receives output, encrypts and logs everything
  • Encryption: DES-CBC with hardcoded keys:
    • Key: AKaPdSgV (8 bytes)
    • IV: QeThWmYq (8 bytes)
  • Logs are base64-encoded after DES encryption, written to .log.enc files

Step 8: Session Log Decryption

#!/usr/bin/env python3 """Decrypt C2 session logs (DES-CBC + Base64).""" from Crypto.Cipher import DES import base64 import os KEY = b"AKaPdSgV" IV = b"QeThWmYq" def decrypt_line(b64_line): ct = base64.b64decode(b64_line.strip()) cipher = DES.new(KEY, DES.MODE_CBC, IV) pt = cipher.decrypt(ct) # Remove PKCS7 padding pad_len = pt[-1] return pt[:-pad_len].decode('utf-8', errors='replace') for logfile in sorted(os.listdir("sessions")): if not logfile.endswith(".log.enc"): continue print(f"\n=== {logfile} ===") with open(f"sessions/{logfile}") as f: for line in f: line = line.strip() if line: print(decrypt_line(line))

Results:

SessionUserCommandsFinding
1johnhostname, whoami, dirNo interesting files
2paulhostname, whoami, dirNo interesting files
3greghostname, whoami, dir, type flag.txtFLAG found!

Session 3 (greg) contained:

> type c:\users\greg\documents\flag.txt
HTB{570r1ng_53cr37_1n_m3m0ry_15_n07_g00d}

Attack Chain Summary

Memory dump (TrueSecrets.raw)
    |
    v
Volatility3: pslist -> find TrueCrypt.exe (PID 2128)
    |
    v
Volatility3: filescan -> find backup_development.zip & development.tc
    |
    v
Volatility3: dumpfiles -> extract backup_development.zip -> development.tc
    |
    v
Volatility3: memmap --pid 2128 -> dump TrueCrypt process memory
    |
    v
Pattern scan -> recover cached password: X2Hk2XbEJqWYsh8VdbSYg6WpG9g7
    |
    v
Custom PBKDF2-HMAC-SHA512 + AES-256-XTS decryption -> FAT12 volume
    |
    v
7z extract -> AgentServer.cs (C2 source) + encrypted session logs
    |
    v
Source code analysis -> DES-CBC key/IV: AKaPdSgV / QeThWmYq
    |
    v
DES-CBC + Base64 decrypt session logs -> flag in greg's session

Key Indicators

Use these techniques when you see:

  • TrueCrypt.exe in process list of a memory dump
  • .tc file extension — TrueCrypt encrypted container
  • Challenge name hinting at "True" + "Secrets" (TrueCrypt)
  • Need to recover encryption passwords from process memory (cached credentials)
  • C# source code with hardcoded DES/AES keys
  • Base64-encoded encrypted log files

Key Insight

The flag message "storing secret in memory is not good" (570r1ng_53cr37_1n_m3m0ry_15_n07_g00d) reinforces the core vulnerability: TrueCrypt caches the volume password in process memory while the container is mounted. A memory dump captures this cached password, allowing full decryption of the container without brute-forcing. This is a fundamental limitation of full-disk encryption when the system is powered on.

$ cat /etc/motd

Liked this one?

Pro unlocks every writeup, every flag, and API access. $9/mo.

$ cat pricing.md

$ grep --similar

Similar writeups