← Back to Writeups
HTBMediumForensics

The Missing Recipe

H4N59/2/20263 min read
#forensics#htb#medium#**-pcap-analysis-->-dns-exfilt

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
tshark -r the-missing-recipe.pcap -q -z io,phs
ProtoFramesNotes
HTTP3517x 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")
FTP88 + 1 dataLogins only; FTP-DATA payload literally FILEDATA:PNGCONTENTS = decoy
SMTP39One Subject: Test / Hello mail = decoy
ICMP3All-zero payloads = decoy
DNS59The real channel

DNS enumeration

bash
tshark -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 under targwuwrnhos.com (base32 alphabet).
  • update.targwuwrnhos.com answered with TXT: KLUv/SAQgQAAQnJ1bm4zckszeUFFU0NCQw== (starts with zstd magic 28 b5 2f in b64).
  • Final query brunnerlocked.targwuwrnhos.com -> CNAME status..., TXT Done, get out!

Solution Approach

  1. Collect exfil queries chronologically:
bash
tshark -r the-missing-recipe.pcap -Y 'dns.qry.name contains "targwuwrnhos.com"' \
  -T fields -e frame.number -e dns.qry.name
  1. 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.
  2. Base32-decode each transfer -> both start with zstd magic 28 B5 2F FD.
  3. 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.
  4. Decode the TXT blob (b64 -> zstd) -> key Brunn3rK3yAESCBC.
  5. Message 2 plaintext blob = IV(16) || AES-128-CBC ciphertext(240).

Steps

python
import 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 %8 validity 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.