Category: Forensics
Flag: apoorvctf{b1ts_wh1sp3r_1n_th3_l0w3st_b1t}
Challenge Description
Routine system checks were performed on the city’s communication network after reports of instability.
Operators sent brief messages between nodes to confirm everything was running smoothly.
Most of the exchanges are ordinary status updates, but one message stands out as… different.
Analysis
This one was a packet-forensics challenge where almost everything looked like normal short status chatter, so the fastest way in was to find the one TCP conversation that was much larger than the rest.
tshark -r challenge.pcap -q -z conv,tcp...
127.0.0.1:33610 <-> 127.0.0.1:5001 8 6232 bytes 3 206 bytes 5 6026 bytes
...That outlier matched the challenge hint perfectly, so I pivoted straight to the suspicious packet and checked its payload length + byte edges.
# parse_frame14_info.py
import subprocess
line = subprocess.check_output([
'tshark', '-r', 'challenge.pcap',
'-Y', 'frame.number==14', '-T', 'fields', '-e', 'tcp.len', '-e', 'data'
], text=True).strip()
length, hexdata = line.split('\t')
print(f'tcp.len={length}')
print(f'data_prefix={hexdata[:40]}')
print(f'data_suffix={hexdata[-40:]}')python parse_frame14_info.pytcp.len=5688
data_prefix=3fd8ffe000104a46494600010102001c001c0000
data_suffix=14514514514514514514514514514514515fffd9The payload looked like a JPEG (...d8 ff e0 ... JFIF ... ff d9) except for the first byte being 3f instead of ff, so I wrote a tiny extraction/repair script to rebuild the image exactly from that packet.
# extract_frame14.py
import subprocess
hexdata = subprocess.check_output([
'tshark', '-r', 'challenge.pcap',
'-Y', 'frame.number==14', '-T', 'fields', '-e', 'data'
], text=True).strip()
payload = bytes.fromhex(hexdata)
fixed = bytes([0xFF]) + payload[1:]
open('frame14_fix.jpg', 'wb').write(fixed)
print(f'tcp_payload_bytes={len(payload)}')
print(f'prefix_before_fix={payload[:8].hex()}')
print(f'prefix_after_fix={fixed[:8].hex()}')python extract_frame14.pytcp_payload_bytes=5688
prefix_before_fix=3fd8ffe000104a46
prefix_after_fix=ffd8ffe000104a46At that point it turned into a neat stego check, and the empty-password extraction worked immediately.

steghide extract -sf frame14_fix.jpg -p "" -xf realflag.txt -fwrote extracted data to "realflag.txt".I still checked the visible QR layer and got baited by a decoy flag in the image, which is exactly the kind of troll this category loves.
zbarimg frame14_fix.jpgQR-Code:apoorvctf{this_aint_it_brother}
scanned 1 barcode symbols from 1 images in 0.02 seconds
The real flag was in the steghide output file, so reading that file gave the final answer.
# print_flag.py
flag = open('realflag.txt', 'r').read().strip()
print(flag)python print_flag.pyapoorvctf{b1ts_wh1sp3r_1n_th3_l0w3st_b1t}Solution
# extract_frame14.py
import subprocess
hexdata = subprocess.check_output([
'tshark', '-r', 'challenge.pcap',
'-Y', 'frame.number==14', '-T', 'fields', '-e', 'data'
], text=True).strip()
payload = bytes.fromhex(hexdata)
fixed = bytes([0xFF]) + payload[1:]
open('frame14_fix.jpg', 'wb').write(fixed)python extract_frame14.py
steghide extract -sf frame14_fix.jpg -p "" -xf realflag.txt -f
python print_flag.pyapoorvctf{b1ts_wh1sp3r_1n_th3_l0w3st_b1t}