Skip to content
All posts
forensicsJune 2, 20266 min read

PCAP triage: finding the flag in a hundred thousand packets

Network forensics challenges hand you a capture and no question. The triage order that finds the answer fast - protocol hierarchy, conversations, streams - plus the exfiltration channels people actually hide flags in: DNS, ICMP, USB, and TLS you can decrypt.

A packet capture is the least structured artifact a CTF will give you. There is no entry point, no obvious payload, and often a hundred thousand packets of which four matter. Opening it in Wireshark and scrolling is the wrong move, and it is what almost everyone does first.

The right move is to narrow before you read. Every triage step below reduces the capture from 'all packets' to 'this conversation', and only at the very end do you look at individual bytes.

Step 1: what is in here?

The protocol hierarchy is the single most informative view in network forensics. It tells you what the capture is *about* in one screen, and any protocol that looks out of place in the mix is where the challenge lives.

# Protocol breakdown by packet and byte count
tshark -r capture.pcap -q -z io,phs

# Who talked to whom, ranked by volume
tshark -r capture.pcap -q -z conv,tcp
tshark -r capture.pcap -q -z conv,udp

# How many packets, over what time span
capinfos capture.pcap
In Wireshark these are Statistics → Protocol Hierarchy and Statistics → Conversations.

Read the output like a triage nurse. Ninety-eight percent TCP with a single large HTTP conversation means the answer is a transferred file. A capture that is mostly DNS, with far more queries than any host would legitimately make, means exfiltration. A handful of ICMP packets with unusually large payloads means the same thing through a different channel. Fifty thousand USB packets means this is a peripheral capture and no networking knowledge is involved at all.

Step 2: the free wins

Before any protocol analysis, run the two checks that cost nothing and solve a surprising number of challenges outright.

# The flag may simply be sitting in a payload, unencrypted
strings capture.pcap | grep -iE 'flag\{|ctf\{|picoCTF\{'

# Export every file that crossed HTTP, then look at all of them
mkdir -p out && tshark -r capture.pcap --export-objects http,out
file out/*
Wireshark’s File → Export Objects does the same for HTTP, SMB, TFTP, IMF, and DICOM.

Object export is worth doing even when you do not think the challenge is about a file transfer. Images, archives, and scripts pulled from HTTP are common carriers, and an exported ZIP is often the *next* challenge rather than the answer - stego inside a download inside a capture is a completely standard nesting.

Step 3: follow the stream

TCP splits application data across packets, so reading individual packets is reading fragments. Reassembling a stream shows you the conversation as the application saw it - the full HTTP request and response, the entire FTP control session, the whole line-based protocol exchange.

# Reassemble stream 0 as ASCII (indices start at 0)
tshark -r capture.pcap -q -z follow,tcp,ascii,0

# Every HTTP request, one per line
tshark -r capture.pcap -Y http.request \
  -T fields -e ip.src -e http.host -e http.request.uri

# Cleartext credential protocols, all at once
tshark -r capture.pcap -Y 'ftp || telnet || http.authorization || pop || imap'

Cleartext protocols deserve a targeted pass because they hand over credentials verbatim: FTP USER/PASS, HTTP Basic authorization headers (base64, not encryption), Telnet typed a character at a time, SMTP AUTH LOGIN. If the challenge is 'find the password', it is almost always in one of these.

Step 4: the covert channels

DNS exfiltration

DNS is the classic exfiltration channel because it is almost never blocked and every query is logged as legitimate infrastructure traffic. Data is encoded into subdomain labels and sent to a nameserver the attacker controls. In a capture the giveaway is unmistakable: hundreds of queries to subdomains of one domain, each label a chunk of base32 or hex.

tshark -r capture.pcap -Y dns.flags.response==0 \
  -T fields -e dns.qry.name | sort -u | head -40

# Typical shape:
#   MFRGGZDF.exfil.example.com
#   MZXW6YTB.exfil.example.com
# Strip the domain, concatenate the labels, base32-decode the result.

Base32 rather than base64 is the norm here, because DNS labels are case-insensitive and base32's alphabet is uppercase-only. Watch the ordering: queries can arrive out of order, and some encodings prefix each label with a sequence number for exactly that reason.

ICMP tunnelling

An ICMP echo request carries an arbitrary payload that most networks pass without inspection. A normal ping sends a fixed pattern, frequently the bytes 0x10 through 0x37 on Linux; a payload that varies from packet to packet, or that is printable ASCII, is carrying data.

tshark -r capture.pcap -Y 'icmp.type==8' -T fields -e data.data \
  | tr -d '\n' | xxd -r -p

USB captures

If the capture is full of USB URB traffic, it is a peripheral recording and the payload is keystrokes or mouse movement. HID keyboard reports are 8 bytes: byte 0 is the modifier mask (0x02 and 0x20 are the shift keys), byte 1 is reserved, and bytes 2 through 7 are up to six simultaneous keycodes - 0x04 is a, 0x05 is b, and so on up through the HID usage table.

tshark -r usb.pcap -Y 'usb.capdata' -T fields -e usb.capdata
# 00:00:0b:00:00:00:00:00   -> 'h'
# 02:00:0c:00:00:00:00:00   -> 'I'  (0x02 = left shift)
Mouse captures are the same idea with relative X/Y deltas; plotting the cumulative position draws the flag.

Step 5: encrypted traffic you can still read

TLS is not automatically the end of the road. If the challenge provides a key log file - the SSLKEYLOGFILE format that browsers and curl can write - the session keys are in it and the traffic decrypts fully. This is common in CTFs precisely because the author wants you to find the file first.

tshark -r capture.pcap -o tls.keylog_file:sslkeys.log \
  -Y http2 -T fields -e http2.data.data

# In Wireshark: Preferences -> Protocols -> TLS -> (Pre)-Master-Secret log filename

Two other cases worth recognising. An RSA private key decrypts a session only if the handshake used RSA key exchange - with any ECDHE suite, forward secrecy means the private key alone is useless, which trips people up when a challenge supplies a .key file that does not help. And a WPA handshake in a wireless capture can be cracked with a wordlist, after which the wireless payloads decrypt too.

Filters worth memorising

FilterFinds
http.request.method == "POST"Submitted data - logins, uploads, form contents
frame contains "flag"The literal string anywhere in a packet
tcp.flags.syn == 1 && tcp.flags.ack == 0Connection attempts, including port scans
tcp.analysis.retransmissionTrouble spots, often where an author truncated a stream
dns.qry.name contains "exfil"Suspicious lookups once you have identified the domain
data.len > 100 && icmpOversized ICMP payloads
ftp-dataThe actual bytes of an FTP transfer, on its separate connection
tcp.stream == 4One conversation, once you know which one you want

The order, condensed

  1. strings | grep flag. It works often enough to justify the five seconds.
  2. Protocol hierarchy. What is this capture about, and what does not belong?
  3. Conversations by volume. The big one is usually a transfer; the odd one is usually the challenge.
  4. Export objects and identify every file that comes out.
  5. Follow the interesting streams end to end.
  6. Sweep the cleartext protocols for credentials.
  7. If the mix looked wrong: DNS labels, ICMP payloads, USB HID data.
  8. If TLS dominates: hunt for a key log file before you conclude it is unreadable.

Notice that reading packets appears at step five, not step one. Network forensics is a filtering discipline, and the filtering is the skill - by the time you are looking at bytes, the challenge should already be nearly solved.

Further reading

pcapwiresharktsharknetwork-forensicsdns-exfiltlsusb