$ / 3 min read/forensics

Ghost in the Machine

Every packet in the capture is byte-identical. The message is in the gaps between them, and the source ports spell out the XOR key.

section
CSAW26
event
ctf.csaw.io ↗
category
forensics
status
● solved
provided
capture.pcap

Summary

Every packet in the capture is identical, so nothing is carried in the bytes. The message lives in the gaps between packets, and the XOR key that unlocks it is spelled out by the source ports.

1. The capture

504 UDP packets, one direction only:

  • every payload is the literal PING
  • every IP.len is 32
  • IP.id is pinned at 4919 (0x1337)

Nothing varies except TTL (64 or 113), source port, and arrival time.

2. Separating signal from noise

Of 121 distinct source ports, 6 carry 64–65 packets each and the other 115 carry one to three. Keeping only the six busy ports leaves 385 real packets; the 115 stragglers are chaff.

3. The key

The six real ports, in first-seen order, are a direct ASCII encoding:

Port minus 40000 ASCII
40115 115 s
40104 104 h
40052 52 4
40100 100 d
40111 111 o
40119 119 w

→ sh4dow

4. The covert channel

385 packets give 384 inter-arrival deltas, falling into two clean, non-overlapping bands:

  • 245 short gaps, 35.5–64.8 ms
  • 139 long gaps, 135.1–164.8 ms

Threshold at 100 ms, map long → 1 and short → 0, pack MSB-first into bytes, then XOR with the repeating key.

bits = "".join('1' if x >= 100 else '0' for x in deltas)
ct   = bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8))
flag = bytes(ct[i] ^ b'sh4dow'[i % 6] for i in range(len(ct)))

Decoy

TTL also varies two ways (64/113) and looks like an obvious bit channel. Decoding it yields nothing.

Scripts: ~/ctf/work/pcap/ (an.py, t.py, t2.py)

## challenge files

7 files · 93 KB
  • an.py
  • capture.pcap
  • dec.py
  • fixed.pcap
  • t.py
  • t2.py
  • original/