The Missing Recipe
The Missing Recipe
Platform: HackTheBox | Category: Forensics | Type: Challenge | Difficulty: Medium | OS: NA | Author: H4N5 | Date: 2026-08-21 | Status: Solved Techniques: pcap analysis -> DNS exfiltration (base32 chunks) -> zstd reassembly -> TXT-record key drop -> AES-128-CBC decryption
Summary
Analysis of the-missing-recipe.pcap (43k frames). Most traffic is noise: repeated AI-generated PNG downloads over HTTP, fake FTP logins (alice/P@ssw0rd123, bob/secret123), a test SMTP mail, and empty ICMP. The real attack is DNS exfiltration: 15 base32-encoded subdomain queries to targwuwrnhos.com carrying two zstd-compressed messages, plus the AES key delivered in a DNS TXT response to update.targwuwrnhos.com.
Flag: brunner{k33p_53nd1ng_th3_me55ag3s}
Recon
Protocol hierarchy
bash read-only • copy allowedtshark -r the-missing-recipe.pcap -q -z io,phs
| Proto | Frames | Notes |
|---|---|---|
| HTTP | 35 | 17x PNG downloads (planning.png, SOC.png, mandag.png, socMenu.png, fyn.png) - all identical AI-gen images with C2PA caBX chunks = decoys ("do not look at the pictures") |
| FTP | 88 + 1 data | Logins only; FTP-DATA payload literally FILEDATA:PNGCONTENTS = decoy |
| SMTP | 39 | One Subject: Test / Hello mail = decoy |
| ICMP | 3 | All-zero payloads = decoy |
| DNS | 59 | The real channel |
DNS enumeration
bash read-only • copy allowedtshark -r the-missing-recipe.pcap -Y "dns" -T fields -e frame.number -e dns.qry.name -e dns.txt
Key findings:
- 15 long
[a-z2-7]subdomains undertargwuwrnhos.com(base32 alphabet). update.targwuwrnhos.comanswered with TXT:KLUv/SAQgQAAQnJ1bm4zckszeUFFU0NCQw==(starts with zstd magic28 b5 2fin b64).- Final query
brunnerlocked.targwuwrnhos.com-> CNAMEstatus..., TXTDone, get out!
Solution Approach
- Collect exfil queries chronologically:
bash read-only • copy allowedtshark -r the-missing-recipe.pcap -Y 'dns.qry.name contains "targwuwrnhos.com"' \ -T fields -e frame.number -e dns.qry.name
- Split into two transfers: an
update.beacon separates chunk sets, and only then does each set's total base32 length satisfy%8 in {0,2,4,5,7}(transfer 1: 196 chars, transfer 2: 426 chars). Concatenating everything gives 622 (%8=6) which fails. - Base32-decode each transfer -> both start with zstd magic
28 B5 2F FD. - Decompress: message 1 is plaintext and ends mid-flag with the hint "Send the encryption key, and We include the IV"; message 2 is ciphertext.
- Decode the TXT blob (b64 -> zstd) -> key
Brunn3rK3yAESCBC. - Message 2 plaintext blob = IV(16) || AES-128-CBC ciphertext(240).
Steps
python read-only • copy allowedimport base64, subprocess from Crypto.Cipher import AES t1 = ["fc2s77jar2gqgafci4mbu4clou2gfciqcerci","lmng3ntqmsxaq6prsqh6sxsoasodd6ng7", "duzfupucvdeillyi5ljlkb6afiqwyfrjnay5pnklod","qoxtixy7ehdpwja3jtornpdzu2jj6ytgrasxv4", "5hxgw3orxpsqbhpoe5erkscaqeabosaecyavwisqqj4yjq"] t2 = ["fc2s77laaaaaccaaawssaz5yzsflg72nnx22cvaj6s55","5mlpk3pxrnxylqcen2zxo6qb444dug4lpqbgvqmi2vusir5hkq", "jeqx2io53yrgk6y7wn6bht6s7aogmxh66a6xjytmn","3j4iya4bqkzebuwup56b26juiwwkefh3yf5", "pnucvyn4jxwhwxpotatrtyjkt7cu4vlzga5evd72b","p2epbp6ywvt4tfvvndripkc57eo5vsdor2ejefs4a", "sfjoijdi7nk6xb4eb7dwaqd6ojaro56qyj4g","vszaghqmrticvg2wkgsoh556dd7vsb3yg3pgay", "54ianusrqrf2pxckbrjoiyqgq6oqjphrfxt55c2albqkln4wna","rspvrbpw7t3t4zo4wc3sh7svfun4mgxl6v4675jru4nl5cahnq"] def unzst(b32chunks): s = "".join(b32chunks).upper() s += "=" * ((8 - len(s) % 8) % 8) return subprocess.run(["zstd","-d"], input=base64.b32decode(s), capture_output=True).stdout m1 = unzst(t1) # "... brunner{k33p_53nd\nSend the encryption key, and We include the IV " m2 = unzst(t2) # 256 bytes key = subprocess.run(["zstd","-d"], input=base64.b64decode("KLUv/SAQgQAAQnJ1bm4zckszeUFFU0NCQw=="), capture_output=True).stdout # b'Brunn3rK3yAESCBC' pt = AES.new(key, AES.MODE_CBC, m2[:16]).decrypt(m2[16:]) print(pt.decode())
Decrypted message 2 contains the Brunsviger recipe and the flag tail.
Flag
brunner{k33p_53nd1ng_th3_me55ag3s}
Lessons
- DNS subdomain exfil chunks may not align to standalone base32 boundaries; use length
%8validity and embedded magic bytes (28 B5 2F FD) to find correct grouping/order. - Beacon-style queries (
update.) can separate logical transfers inside one noisy capture. - Always dump TXT responses of the C2 domain - keys are often dropped out-of-band right before encrypted payloads.
- Identical repeated downloads (same md5) and literal placeholder payloads (
FILEDATA:PNGCONTENTS) mark decoy channels fast.