Step 241. Advanced pcap Analysis: Wireshark Advanced Filters, tshark — Reconstructing an Incident from Ten Thousand Packets
Level 3 — Forensics Track | Difficulty ★★★★☆ | Estimated time: 5 hours
Prerequisites: Steps 83–84 (Wireshark basics, protocols and anomaly detection) complete. You know basic display-filter syntax and what normal TCP/DNS/HTTP looks like.
⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.
- What you need: tshark on WSL Ubuntu (measured: 4.2.2), a scapy venv (measured: scapy 2.7.0).
- Caution: today’s evidence file is a scenario pcap you build yourself with scapy. All tshark output is measured; Wireshark GUI screens are "Screen examples."
In CTFs and real incidents, pcap files arrive with tens of thousands of packets. Reading them line by line is impossible — and unnecessary. A working analyst’s craft is a three-stage system: narrow with statistics, dig with filters, read with streams. Today you build a pcap for the scenario "an office PC got infected and credentials leaked," then reconstruct the entire attack flow inside it — login eavesdropping, DNS tunneling, payload download — using tshark alone.
1. Learning Objectives
By the end of this chapter, you will be able to:
- Decide "where to dig first" with
-z io,phs(protocol hierarchy) and-z conv,tcp(conversation list) - Combine advanced filters (
http.request.method,dns.qry.name contains,tcp.stream eq) - Extract exactly the fields you want with
-T fieldsand pipe them into shell pipelines - Read an entire conversation’s contents at once with
follow,tcp - Reconstruct an attack scenario as a timeline from a single pcap
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language/environment | WSL Ubuntu bash, tshark 4.2.2 (measured), scapy 2.7.0 venv (measured) |
| Today’s commands | tshark -r f -q -z io,phs, -z conv,tcp, -Y filter, -T fields -e …, -z follow,tcp,ascii,N |
| Concepts needed | Display filters vs. statistics, TCP stream numbers, DNS tunneling, conversations |
| Today’s deliverable | A scenario pcap + an attack timeline + 5 reusable tshark filters |
2-1. The Three-Stage Analysis System — From Zoom-Out to Zoom-In
When a big pcap lands on your desk, the order is fixed:
- Draw the map with statistics — protocol hierarchy (what’s there, and how much), conversation list (who talked to whom, and how much)
- Dig the suspicious zones with filters — abnormally large conversations, protocols that shouldn’t be there, cleartext credentials
- Read the scene with streams — reassemble one conversation’s packets to restore the actual content in full
Scrolling a packet list with your eyes is the verification step after all three stages are done. Invert the order and you’re hunting a needle in the ocean with bare hands.
2-2. Display Filters vs. Statistics Filters
tshark -Y "filter" is a packet-selecting filter (a display filter). Options like -z io,phs, by contrast, are statistics commands that aggregate the whole file. Mixing the two is today’s core skill: find the big conversation with -z conv,tcp, then dig into that conversation’s stream number with -Y "tcp.stream eq 5".
2-3. TCP Streams — The Index of a Conversation
Wireshark/tshark bundles packets sharing the same 5-tuple (source/destination IP, ports, protocol) and assigns a stream number, starting at 0. Once you know the stream number, -z follow,tcp,ascii,N reassembles that conversation’s application data in order — for HTTP, the entire request and response appear together. It’s the same feature as the GUI’s "Follow TCP Stream."
2-4. DNS Tunneling — Smuggling a Secret Letter Through the Phone Book
DNS is a cleartext protocol allowed on nearly every network, which makes it a favorite exfiltration channel in real incidents. The trick is simple — encode the stolen data with base64 or similar and attach it as a subdomain label in a query. A query like c3VwM3JzZWNyZXQ=.exfil.evil-example.net is exactly that. Detection hints: the subdomains are abnormally long, and queries under the same domain repeat.
3. Follow Along
3-1. Staging the Evidence — Building the Scenario pcap
Analysis practice needs evidence whose answer you already know. You’ll stage the crime scene with scapy (full script at tmp_test/step241_pcap.py; all output in this chapter measured 2026-09-09):
from scapy.all import IP, TCP, UDP, DNS, DNSQR, DNSRR, Raw, wrpcap
# Scenario:
# ① Two normal DNS queries (portal.example.com, cdn.example.com)
# ② HTTP POST /login — cleartext credentials (username=admin&password=sup3rsecret)
# ③ Three suspicious DNS queries — base64 chunks attached as subdomains (exfil.evil-example.net)
# ④ The C2 server (203.0.113.99:4444) connects first and pushes 300KB (reverse connection + bulk transfer)
...
wrpcap("scenario.pcap", pkts)
scenario.pcap created: 139 packets
3-2. Stage 1: Drawing the Map with Statistics
First, look at "what’s there, and how much":
tshark -r scenario.pcap -q -z io,phs
===================================================================
Protocol Hierarchy Statistics
Filter:
ip frames:139 bytes:306319
udp frames:10 bytes:942
dns frames:10 bytes:942
tcp frames:129 bytes:305377
http frames:2 bytes:297
urlencoded-form frames:1 bytes:194
data frames:120 bytes:304800
(Measured 2026-09-09.)
How to read the output: three things should catch your eye. ① A urlencoded-form inside HTTP — there’s a form submission. Possibly a login. ② TCP labeled data at 120 packets and 304KB — an unparsed bulk transfer, 99% of all bytes. ③ DNS at 10 packets — small, but the contents demand a look. The next question is "who is that bulk transfer talking to?":
tshark -r scenario.pcap -q -z conv,tcp
================================================================================
TCP Conversations
| <- | | -> | | Total |
203.0.113.99:4444 <-> 192.168.10.50:40001 1 40 bytes 122 304 kB 123 304 kB
192.168.10.50:40000 <-> 192.168.10.10:80 2 143 bytes 4 314 bytes 6 457 bytes
(Measured 2026-09-09.)
How to read it: there are only two conversations. The one with the portal (192.168.10.10:80) is small and normal. The problem is the top row — 122 packets and 304KB poured in one direction from 203.0.113.99:4444 to our PC. The <- column has just 1 packet; the -> column has 122. A waterfall with no question attached. This conversation gets dug first.
3-3. Stage 2: Digging with Filters — Forms, DNS, Streams
First, see what that form submission was:
tshark -r scenario.pcap -Y 'http.request.method == "POST"'
8 0.350000 192.168.10.50 → 192.168.10.10 HTTP 194 POST /login HTTP/1.1 (application/x-www-form-urlencoded)
A login request. Split it into fields:
tshark -r scenario.pcap -Y 'http.request.method == "POST"' -T fields \
-e frame.number -e ip.src -e http.host -e http.request.uri -e urlencoded-form.key -e urlencoded-form.value
8 192.168.10.50 portal.example.com /login username,password admin,sup3rsecret
(Measured 2026-09-09.)
How to read it: the urlencoded-form.key/value fields decompose the form into name,value pairs. The credentials of a cleartext HTTP login were riding whole inside a single packet — this is the real-world object behind the phrase "cleartext protocols are writing postcards in the street."
Next, DNS. Select only the query packets and look at the names:
tshark -r scenario.pcap -Y 'dns.flags.response == 0' -T fields -e dns.id -e dns.qry.name
0x1111 portal.example.com
0x2222 cdn.example.com
0x3300 c3VwM3JzZWNyZXQ=.exfil.evil-example.net
0x3301 YWRtaW4=.exfil.evil-example.net
0x3302 ZG9uZQ==.exfil.evil-example.net
(Measured 2026-09-09.)
How to read it: the first two lines are normal. The bottom three — subdomains are long base64-looking strings, with consecutive queries under the same exfil.evil-example.net. That’s the DNS-tunneling pattern from 2-4. Decode the chunks:
import base64
for f in ["c3VwM3JzZWNyZXQ=", "YWRtaW4=", "ZG9uZQ=="]:
print(f, "->", base64.b64decode(f).decode())
c3VwM3JzZWNyZXQ= -> sup3rsecret
YWRtaW4= -> admin
ZG9uZQ== -> done
(Measured 2026-09-09.) The credentials leaked in 3-3’s cleartext login escaped to the outside disguised as DNS queries. This is the moment two pieces of evidence prove each other.
3-4. Stage 3: Reading the Scene with Streams
Reassemble the full contents of the HTTP conversation:
tshark -r scenario.pcap -q -z follow,tcp,ascii,0
===================================================================
Follow: tcp,ascii
Filter: tcp.stream eq 0
Node 0: 192.168.10.50:40000
Node 1: 192.168.10.10:80
POST /login HTTP/1.1
Host: portal.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
username=admin&password=sup3rsecret
HTTP/1.1 302 Found
Location: /dashboard
Content-Length: 0
(Measured 2026-09-09. Request and response reassembled on one screen.)
Check the bulk-transfer conversation (stream 1) too:
tshark -r scenario.pcap -Y 'tcp.stream eq 1' | head -4
tshark -r scenario.pcap -Y 'tcp.stream eq 1' | wc -l
17 0.800000 203.0.113.99 → 192.168.10.50 TCP 40 4444 → 40001 [SYN] Seq=0 Win=8192 Len=0
18 0.850000 192.168.10.50 → 203.0.113.99 TCP 40 40001 → 4444 [SYN, ACK] Seq=0 Ack=1
19 0.900000 203.0.113.99 → 192.168.10.50 TCP 40 4444 → 40001 [ACK] Seq=1 Ack=1
20 0.950000 203.0.113.99 → 192.168.10.50 TCP 2540 4444 → 40001 [PSH, ACK] Len=2500
123
(Measured 2026-09-09.)
How to read it: the decisive clue is packet 17 — the [SYN] came from the external server (203.0.113.99) to our PC. A normal download starts with us connecting first. A conversation the outside opens is a "reverse connection" — either the trace of an attacker knocking on the door directly rather than implanted malware, or a byproduct of proxy configuration. Either way, it’s an investigative subject. And right behind it, 120 chunks of 2500 bytes each flooded in:
tshark -r scenario.pcap -Y 'tcp.len > 2000' | wc -l
120
3-5. Automation — Putting It on a Shell Pipeline
Hook tshark’s output into sort | uniq -c and repeated analysis becomes a script:
tshark -r scenario.pcap -T fields -e ip.src | sort | uniq -c | sort -rn
122 203.0.113.99
10 192.168.10.50
5 8.8.8.8
2 192.168.10.10
(Measured 2026-09-09.)
How to read it: who talked the most is visible at a glance. 203.0.113.99 dominates — the conclusion of the conversation statistics (conv,tcp) reconfirmed in one line. In the field, you swap http.host, dns.qry.name, and friends into this pipeline to instantly pull "most-contacted hosts" and "most-queried domains" rankings. On a large pcap, these one-line statistics are the investigation’s first map.
3-6. The Same Work in the GUI — Screen Example
Here’s the path for the same analysis in the Wireshark GUI (Screen example — not actually executed):
# Screen example — Wireshark GUI procedure
1. Open scenario.pcap
2. Statistics → Protocol Hierarchy ← same as tshark -z io,phs
3. Statistics → Conversations → TCP ← same as tshark -z conv,tcp. Sort by the Bytes column
4. In the display-filter bar: http.request.method == "POST"
5. Right-click a packet → Follow → TCP Stream ← same as -z follow,tcp,ascii
6. File → Export Objects → HTTP ← save the big ones from the transferred-objects list
Same file, same conclusion, different tool. The GUI is strong at exploration; tshark is strong at record-keeping and repetition — the power of the CLI is that you can paste the exact commands into the investigation report.
3-7. Reconstructing the Timeline — Today’s Conclusion
Arrange the collected evidence in time order and it becomes an incident:
[0.00s~] Normal activity: DNS query for portal.example.com, page access
[0.35s] HTTP POST /login — admin/sup3rsecret sent in cleartext (evidence: stream 0)
[0.55s~] Three DNS queries with base64 chunks to exfil.evil-example.net — credential exfiltration
[0.80s] 203.0.113.99:4444 connects to the PC first (SYN) — reverse connection
[0.95s~] 2500 bytes × 120 = 300KB inbound over the same connection — payload or data bundle
The chain "login eavesdropping → credential exfiltration → external connection → bulk transfer" is complete. This single paragraph is the same skeleton as an incident report’s Executive Summary.
4. Missions & Exercises
Mission — A Set of Five Filters and an Incident Report
- Apply the five filters below to scenario.pcap and record one line of results for each:
①http.request②dns.qry.name contains "evil"③tcp.flags.syn == 1 and tcp.flags.ack == 0④tcp.len > 2000⑤ build oneip.len-based filter yourself and compare with ④ - Identify which of ①–④ flags "direct evidence of exfiltration"
- Write your own timeline in the 3-7 format — attach the supporting filter or stream number in parentheses to each entry
- (Challenge) Dump the 300KB bulk transfer with
follow,tcp,raw,1, save it to a file, and verify its size
Exercises
Problem 1. In -z conv,tcp, the 203.0.113.99:4444 ↔ our PC conversation showed 1 packet on <- and 122 packets on ->. What does this asymmetry mean?
Problem 2. Besides dns.qry.name contains "exfil", think of a condition that can select DNS-tunneling queries even when you don’t know the domain (hint: the length of the name).
Problem 3. The credentials of the cleartext HTTP login were exposed. If the same site had been HTTPS, what could we see and what could we not see?
Problem 4. Stream 1’s first packet was an external→internal [SYN]. Explain why this single fact lets you reject the "normal download" hypothesis.
5. Model Answers & Completion Criteria
Mission Model Answer
Filter results (based on 2026-09-09 measurements):
① http.request → 1 packet (POST /login)
② dns.qry.name contains "evil" → 6 packets (3 queries + 3 responses)
③ tcp.flags.syn==1 and tcp.flags.ack==0 → 2 packets (two conversation starts — one originated from outside!)
④ tcp.len > 2000 → 120 packets (all 203.0.113.99→PC)
⑤ ip.len > 2000 → 120 packets (length including the IP header — same set as ④)
The direct evidence of exfiltration is ② — the base64-encoded credentials sit inside the DNS queries. ③ flags a separate anomaly: "an externally initiated connection."
The timeline follows the 3-7 format, with evidence attached to each entry — e.g., [0.80s] reverse connection starts (evidence: filter ③'s second packet, ip.src=203.0.113.99). Challenge 4: strip the header lines from -z follow,tcp,raw,1‘s output and save it; you should get 300,000 bytes (120 × 2500).
How to verify: ① Were the packet counts of all five filters recorded? ② Does the "direct evidence" choice come with a reason? ③ Does every timeline entry carry its evidence (filter/stream)?
Exercise Answers
Answer 1. It means a bulk transfer flowed in one direction only — the peer pushed 304KB while our side sent almost no request or response traffic. In a normal request-response conversation (request a file, then download it), request packets exist on the <- side. This asymmetry is direct evidence of the anomaly "a transfer we never asked for."
Answer 2. A name-length condition — e.g., dns.qry.name.len > 40. DNS tunneling loads data into labels, so query names become abnormally long. Normal domains are names a human would type, so they’re short. Beyond length, "query frequency under the same parent domain" (check with -T fields -e dns.qry.name | sort | uniq -c) becomes the second condition.
Answer 3. Visible: the peer IP and port, the size and timing of the traffic, and (if SNI is cleartext) the domain contacted. Not visible: the URL path (/login), form fields, credentials, response body — all inside TLS. That’s why HTTPS-era analysis moved from "contents" to "behavior (with whom, how much, in what rhythm)" — the statistical techniques of 3-5 are exactly the tools of that era.
Answer 4. In a normal download, the connection initiator (SYN sender) is always our PC — we have to knock before the server opens. The [SYN] arriving from outside is evidence that this conversation did not begin with our request, so the hypothesis "the user downloaded a file" cannot stand. The remaining hypotheses are a reverse connection (a firewall-evasion technique) or a pre-compromised relay — either way, abnormal.
Completion Criteria Checklist
- [ ] I can pick a starting point for analysis with the
io,phsandconv,tcpstatistics - [ ] I can extract the fields I want as a table with the
-T fields -ecombination - [ ] I reassembled a conversation’s contents with
follow,tcp,ascii,N - [ ] I know DNS tunneling’s two faces (long subdomains, repeated queries)
- [ ] I can read an externally initiated SYN as a warning sign
- [ ] I can pipe tshark output into a
sort | uniq -cpipeline - [ ] Mission: applied all 5 filters + completed a timeline with evidence attached
6. Common Pitfalls & Fixes
Wall 1. You set a filter but nothing comes out
Symptom: tshark -r f -Y "http" returns an empty result.
Cause: either that pcap genuinely has no HTTP, or HTTP traveled over a non-standard port (something other than 80) and protocol auto-detection failed.
Fix: check the protocol map first with -z io,phs. For a non-standard port, force the interpretation with -d tcp.port==8080,http.
Wall 2. -z follow throws an error
Symptom: tshark -z follow,tcp,ascii,0 can’t find the stream.
Cause: stream numbers are reassigned from 0 in every file. You either carried a number over from another file, or the numbers shifted when combined with a filter.
Fix: look at this file’s conversation list with -z conv,tcp first and confirm the number. A stream number is "an index within this file."
Wall 3. -e does nothing because you don’t know the field name
Symptom: a guessed name like -T fields -e http.passwd yields blank columns.
Cause: Wireshark’s field names follow a fixed hierarchical naming scheme (http.request.uri, dns.qry.name …).
Fix: click a field in the GUI’s packet-details pane and the exact field name appears in the status bar at the bottom left (Screen example). Confirm names in the GUI, then move them to tshark — that’s the real-world workflow.
Wall 4. The Running as user "root" ... This could be dangerous warning bothers you
Symptom (measured 2026-09-09): this warning prints to stderr every time tshark runs.
Cause: you’re running as root in WSL. The output itself is fine.
Fix: ignoring it doesn’t hurt analysis, but when piping output, get in the habit of discarding stderr with 2>/dev/null so it doesn’t mix in.
Wall 5. The pcap is so big that tshark is slow
Symptom: every filter on a tens-of-thousands-of-packets file takes seconds.
Cause: tshark traverses the whole file every time.
Fix: as the investigation narrows, save the narrowed result with -w partial.pcap and continue analysis on that file. Moving ocean → puddle → cup is the standard approach for large-volume analysis.
7. Summary
Today’s Concepts
| Concept | One-line explanation |
|---|---|
| Three-stage system | Statistics (map) → filters (excavation) → streams (close reading) — eyeball scrolling comes last |
| TCP stream | The index of a single conversation — the unit of follow |
| The danger of cleartext | An HTTP POST is a postcard — credentials are right there to see |
| DNS tunneling | Long subdomains + repeated queries = the face of a secret letter |
| Reverse connection | A SYN sent first by the outside — "a conversation we never started" |
| Asymmetric conversation | A conversation big in only one direction is a transfer with no request |
Today’s Commands & Filters
| Command/filter | What it does |
|---|---|
tshark -r f -q -z io,phs |
Protocol map (what’s there, how much) |
tshark -r f -q -z conv,tcp |
Conversation list (who talked to whom, how much) |
http.request.method == "POST" |
Select only form submissions |
dns.qry.name contains "…" |
Queries whose names contain a given string |
tcp.stream eq N |
View only conversation N |
-T fields -e field |
Extract just the fields as a table |
-q -z follow,tcp,ascii,N |
Reassemble a conversation whole |
| sort | uniq -c | sort -rn |
Frequency ranking — the investigation’s first map |
An Instinct More Important Than Commands
Skill at packet analysis isn’t filter memorization — it’s the order of questions. "What’s there (statistics) → where’s the anomaly (asymmetry, floods, things that shouldn’t be) → what did that conversation say (streams) → does it connect to other evidence (timeline)." Just as the cleartext login and the DNS chunks proved each other in today’s scenario, a forensic conclusion always comes from the cross-confirmation of evidence.
And note once more that all of this analysis happened on "an evidence file I made myself." Applying the same techniques to a file captured from someone else’s network is an entirely different matter — the domain of law and authorization.
Once every box is checked, Step 241 is complete. Click the checkbox in the sidebar to save your progress.