Wireshark for the SOC
A working reference for reading capture files the way an analyst does — orient, filter down, read the protocol, pivot, reconstruct.
This came out of a hands-on first-capture session — a few thousand packets of ordinary browsing, and not one cleartext HTTP request: all TLS and QUIC. The loop in practice: isolate a lookup with dns.qry.name == "example.com", read the query/response pair, then pivot on the answer with ip.addr == <resolved IP>. Everything below is that same loop, generalised.
What Wireshark is
If you've never opened it — what the tool actually does, what it's for, and where it helps or gets in the way.
Wireshark is a free, open-source network protocol analyser — a “packet sniffer.” It records the individual frames going in and out of a network interface (or reads a saved capture file) and decodes every one from raw bytes into readable fields, across roughly 3,000 protocols.
It is the de facto standard for looking at network traffic. The package is really three tools: the Wireshark GUI, the tshark command-line version, and dumpcap, the small program that actually does the capturing. It captures through Npcap on Windows and libpcap on Linux and macOS.
What you can do with it
- Capture live traffic, or open a
.pcap/.pcapngsomeone hands you and work it offline. - Inspect any layer of any packet — Ethernet, IP, TCP/UDP, TLS, HTTP, DNS, SMB, Kerberos, and thousands more.
- Cut a capture of millions of packets down to the few that matter with the display-filter language.
- Reassemble whole conversations (Follow Stream) and carve transferred files back out (Export Objects).
- Summarise a file at a glance — protocol mix, top talkers, throughput over time, TCP problems.
- Decrypt TLS or WPA/WPA2 when you legitimately hold the keys.
- Script all of the above for automation and CI with
tshark.
Typically used for: Incident response and network forensics, malware traffic analysis, chasing latency / packet loss / failed handshakes, confirming what an application actually sends on the wire, learning how a protocol really works, and CTF challenges.
Strengths
- Free, open-source, cross-platform, and decodes more protocols than any commercial tool.
- Unmatched depth — you see the actual bytes, not a vendor's summary of them.
- Huge ecosystem: documentation, sample captures, courses, and a community that has already analysed almost anything you'll meet.
- The skills transfer — the filter language and the concepts carry to tcpdump, Zeek, Suricata, and every cloud packet-capture feature.
- Scriptable and repeatable through
tshark.
Limits & cautions
- It is not monitoring. Wireshark only sees what happens while you're watching — you have to already be capturing when the event occurs. Continuous coverage needs an IDS, Zeek, or a full-packet-capture appliance.
- It doesn't scale to fast links. On a busy network it drops packets, and the GUI struggles with multi-gigabyte files. Capture with
dumpcaportcpdumpinto a ring buffer, then slice the file down before opening it. - It only sees traffic that reaches your capture point. For anything beyond your own machine you need a SPAN / mirror port or a network tap — it can't see other VLANs or switches.
- Encrypted payloads stay opaque without the keys, and most of the modern web is TLS or QUIC.
- Easy to misread — TCP “errors” in a capture are often artifacts of the capture itself, not the network.
- Its protocol dissectors have a long history of security bugs. Keep it updated, don't run it as root/admin, and open untrusted captures on an isolated machine.
- Capturing traffic can breach policy or law. Only capture on networks you're authorised to.
Step by step: capture to finding
Every click from launching Wireshark to a saved, hand-off-ready finding. A–C get a capture open; D–I are the analysis. The rest of this page is the reference behind each step.
Capture your own traffic
Handed a .pcap file instead? Skip to C.
- Open Wireshark — Start menu, type
Wireshark, Enter. If it can't see any interfaces, close it and reopen as Administrator. - The welcome screen lists your network interfaces, each with a small live activity graph. The one that's moving is normally
Wi-FiorEthernet. - Optional — record only what you need: click the
…using this filterbox and type a capture filter such ashost 10.0.0.42orport 443. Leave it blank to capture everything, which is usually the safer choice in an incident. - Double-click the interface. Capture starts at once — packets scroll in the top pane and the toolbar shows a red square.
- Reproduce the activity you're investigating, or let it run for the time window you need.
- Stop the capture: click the red square in the toolbar, or press
Ctrl+E.
Save it before you touch anything
File → Save As(Ctrl+Shift+S).- Choose a folder, name it something you'll recognise later —
case1234-web.pcapng— leave the type aspcapng, click Save. - Always work on a copy.
File → Save Asonce more to make a working duplicate, so the original stays untouched while you cut packets out of the copy.
Open a capture you were given
File → Open(Ctrl+O) and pick the.pcap/.pcapng— or drag the file straight onto the Wireshark window.- Set the clock to UTC so timestamps line up with every other log:
View → Time Display Format → UTC Date and Time of Day. View → Time Display Format → Seconds Since Previous Displayed Packetis the other useful mode — it shows gaps between packets, which is how you eyeball a beacon interval.
Orient — read the whole file before you filter
Statistics → Capture File Properties: note the time span and total packet count so you know the scale of what you're looking at.Statistics → Protocol Hierarchy: scan the protocol mix. Anything you didn't expect — IRC, TFTP, SMB heading to the internet, raw TCP on an odd port — is your first lead.Statistics → Conversations, then click theBytescolumn header to sort. This is "who talked to whom, and how much." Note the largest flows and any external IP you don't recognise.- Right-click the row for an interesting flow →
Apply as Filter → Selected → A↔Bto pull just that conversation into the main window. Statistics → Endpoints → Mapplots the external IPs on a world map — quick sanity check on where traffic is going.
Filter down to the question
- Click the display-filter bar, or press
Ctrl+/to jump to it. - Type an expression and press
Enter. The bar turns green when the syntax is valid, red when it isn't, yellow when it's legal but probably not what you meant. - Build a filter without typing it: click a value in the middle (details) pane, then right-click →
Apply as Filter → Selected. Use…and Selected/…or Selectedfrom the same menu to stack conditions. - Turn a field into a permanent column: right-click it in the details pane →
Apply as Column. Do this with the TLSServer Nameso every HTTPS row shows its destination. - Clear the filter with the
Xat the right of the bar. Nothing was deleted — every packet comes straight back.
Read a single packet
- Click a row in the top (packet list) pane. The other two panes follow it.
- Middle pane is the decode, outer layer to inner:
Frame → Ethernet → IP → TCP/UDP → the application protocol. Click the>to expand one layer. - Open an entire layer at once: right-click it →
Expand Subtrees. - Bottom pane is the raw bytes. Click any field in the middle pane and its bytes highlight below — that's how you confirm a value is genuinely in the packet and not inferred.
Ctrl+.andCtrl+,step to the next / previous packet in the same conversation.
Reconstruct the whole conversation
- Right-click any packet in the flow →
Follow → TCP Stream(orHTTP/TLS/UDP Stream). - The window shows the exchange in order — one colour for client→server, another for server→client. A TLS stream shows the handshake in clear, then ciphertext.
- Change
Show data astoHex Dumpfor binary protocols, orRawto save the payload out to a file. - The dropdown at the bottom-left steps through every stream of that type in the capture.
- Close the window — the filter
tcp.stream eq <n>stays applied, so you're now looking at just that one conversation. Clear the bar to zoom back out.
Carve out transferred files
File → Export Objects → HTTP(alsoSMB,IMFfor email,TFTP,DICOM).- The list is every file seen in that protocol, with hostname, type and size. Click a row to jump to the packets that carried it.
- Select one and click
Save, orSave Allto a folder. Treat anything you pull as hostile — scan it, don't open it.
Mark it up and hand off the finding
- Click each packet that matters and press
Ctrl+M— a black bar marks it. - Right-click →
Packet Comment, write what the packet shows,OK. The note is saved inside the pcapng and travels with the file. File → Export Specified Packets→ chooseMarked packets only→ save a small evidence file (case1234-evidence.pcapng).- For each marked packet, write down: the frame number, the 5-tuple (
src IP:port → dst IP:port, protocol), and the UTC timestamp. A finding that doesn't point at specific packets can't be reproduced.
The analysis loop
The same six moves every time, whether it's your own capture or a PCAP handed to you from an incident. Don't start filtering until you've oriented.
Orient before you filter
Get the shape of the whole capture first.
- Statistics → Protocol Hierarchy — what protocols, in what proportion. Anything unexpected (IRC, TFTP, raw TCP on odd ports) stands out here.
- Statistics → Conversations, sort by Bytes — who is talking to whom, and which flows are large.
- Statistics → Capture File Properties — time span, dropped packets, capture comments.
- Note the internal hosts you'll be reasoning about.
Filter to the question
Start broad, then narrow with fields.
- Type a protocol name alone as a shortcut: dns, http, tls.
- Narrow with fields: dns.qry.name == "acme-updates.com", ip.addr == 10.0.0.42.
- Display filters never delete anything — clear the bar and every packet returns.
- Bar colour: green = valid, red = broken syntax, yellow = legal but probably not what you meant.
Read the protocol
Expand the relevant layer in the details pane and read the fields that carry meaning.
- DNS — query name, record type, reply code, answers.
- HTTP — method, Host, URI, User-Agent, status code.
- TLS — Client Hello → server_name (SNI); certificate CN / SAN.
- TCP — flags, the handshake, resets, retransmissions.
Pivot
Every fact points at the next filter.
- DNS gave you an IP → ip.addr == <that IP>.
- A suspicious host → what else did it connect to?
- A port → who else is using it?
- A JA3 / JA4 hash → every other client with the same fingerprint.
- Chain: domain → IP → conversation → that IP's other flows.
Reconstruct
Assemble whole conversations, not loose packets.
- Right-click → Follow → TCP / HTTP / TLS Stream — the exchange in order, colour-coded by direction.
- File → Export Objects → HTTP / SMB / IMF — carve out transferred files.
- Statistics → HTTP → Requests — every URL that was asked for.
Write it down
A finding has to point at specific packets.
- Ctrl+M to mark packets of interest.
- Right-click → Packet Comment to annotate inside the file.
- Record frame numbers, the 5-tuple, and timestamps — switch to UTC via View → Time Display Format.
- File → Export Specified Packets to hand off just the relevant slice.
Display-filter syntax
One shape: field.name operator value. Typing a protocol name alone (dns, tls) is a shortcut for “any packet with this layer”.
Comparison
ip.addr == 10.0.0.5 # eq
tcp.port != 443 # ne
frame.len >= 1400 # ge (gt lt le)
http.response.code > 399
Careful with ip.addr != x — it means "has an address field that isn't x", true for almost every packet. To exclude a host use !(ip.addr == x).
Combine
dns and ip.src == 10.0.0.5
http.request or tls.handshake.type == 1
not arp
(tcp.port == 80 or tcp.port == 443)
and ip.dst == 8.8.8.8
and or not — or && || ! — same thing. Parenthesise freely.
Substring & sets
http.host contains "update"
dns.qry.name matches "[a-z0-9]{20,}" # regex
frame contains "PASS " # raw bytes
tcp.port in {80 443 8080 8443}
contains is a literal byte match; matches is PCRE and case-insensitive by default.
Filters by protocol
The handful worth memorising per protocol. Everything else you can build straight from the details pane.
| Filter | Shows |
|---|---|
| DNS | |
| dns.flags.response == 0 | Queries only — what the host wanted to resolve |
| dns.flags.rcode == 3 | NXDOMAIN — the name doesn't exist. Malware hitting dead C2, DGA misses. |
| dns.qry.name contains "domain" | Every lookup touching a domain string |
| dns.qry.type == 16 | TXT queries — a common carrier for DNS tunnelling |
| dns.time > 1 | Slow responses (> 1 s) — resolver problems |
| HTTP | |
| http.request | Requests only — one row per URL fetched |
| http.request.method == "POST" | Uploads / form posts — watch for exfil |
| http.request.uri matches "\.(exe|dll|ps1|scr|zip)$" | Executable / archive downloads |
| http.user_agent contains "curl" | Non-browser clients (curl, python-requests, PowerShell) |
| http.response.code >= 400 | Errors — failed fetches, probing |
| TLS / SSL | |
| tls.handshake.type == 1 | Client Hello — one per new HTTPS connection; carries the SNI |
| tls.handshake.extensions_server_name == "host" | Connections to a specific hostname (pre-ECH) |
| tls.handshake.type == 11 | Certificate — read issuer, CN, SAN, validity |
| tls.record.version == 0x0301 | Legacy TLS 1.0 negotiated — worth a look |
| tls.alert_message | Handshake failures / aborted sessions |
| TCP | |
| tcp.flags.syn == 1 && tcp.flags.ack == 0 | Connection attempts — the basis of scan detection |
| tcp.flags.reset == 1 | RST — refused / torn-down connections |
| tcp.analysis.retransmission | Retransmits — loss, or a saturated / failing path |
| tcp.analysis.flags | Everything Wireshark thinks is wrong with TCP here |
| tcp.stream == 7 | One TCP conversation by its stream index |
| UDP / ICMP / ARP | |
| udp.length > 512 | Oversized UDP — DNS abuse, tunnelling, amplification |
| icmp.type == 8 || icmp.type == 0 | Ping echo request / reply — sweeps, keep-alives, tunnels |
| icmp && data.len > 64 | ICMP with a fat payload — possible ICMP tunnel |
| arp.duplicate-address-detected | Two MACs claiming one IP — ARP spoofing / poisoning |
| arp.opcode == 1 | Who-has requests — a storm from one host = LAN sweep |
| IP / frame / generic | |
| ip.addr == 10.0.0.0/8 | Anything touching a subnet (CIDR works) |
| ip.src == 10.0.0.5 && !(ip.dst == 10.0.0.0/8) | One internal host talking out to the internet |
| frame contains "password" | A byte string anywhere in the packet (cleartext only) |
| frame.time >= "2026-08-31 14:00:00" | Everything after a timestamp |
| !(arp || dns || icmpv6 || mdns) | Mute the background chatter |
The analyst phrasebook
You know what you want to see in words. Here's how to say it to Wireshark.
| I want to see… | Filter / action |
|---|---|
| Everything to and from one host | ip.addr == 10.0.0.42 |
| Only what that host sent outbound to the internet | ip.src == 10.0.0.42 && !(ip.dst == 10.0.0.0/8 || ip.dst == 192.168.0.0/16) |
| Every website a machine visited (HTTPS) | tls.handshake.type == 1 — then add tls.handshake.extensions_server_name as a column |
| Every DNS name a machine asked for | dns.flags.response == 0 && ip.src == 10.0.0.42 |
| Lookups that failed (dead domains) | dns.flags.rcode == 3 |
| Scans / connection attempts | tcp.flags.syn == 1 && tcp.flags.ack == 0 |
| Connections that got refused | tcp.flags.reset == 1 |
| Cleartext credentials or keywords | frame contains "password" / ftp.request.command == "PASS" / http.authorization |
| Files pulled over HTTP | http.request.uri matches "\.(exe|dll|ps1|bat|zip|rar|7z|scr)$" — then File → Export Objects → HTTP |
| Possible upload / exfil | http.request.method == "POST" / Statistics → Conversations, sort Bytes A→B |
| One conversation start to finish | Right-click a packet → Follow → TCP Stream (or tcp.stream eq N) |
| Traffic on unexpected ports | Statistics → Conversations → TCP tab, sort by Port B, scan for oddities |
| Whether a host is beaconing | Statistics → I/O Graph, one series filtered to ip.addr == <suspect>, look for even spacing |
Spot the pattern
What common activity looks like in the packet list — the visual signature, a starter filter, and the next thing to check.
One source, marching through ports
10.0.0.5 → 10.0.0.9 TCP 58xxx→22 [SYN]
10.0.0.5 → 10.0.0.9 TCP 58xxx→23 [SYN]
10.0.0.5 → 10.0.0.9 TCP 58xxx→25 [SYN]
10.0.0.9 → 10.0.0.5 TCP 25→58xxx [RST, ACK]
tcp.flags.syn == 1 && tcp.flags.ack == 0Next: Statistics → Conversations → is one pair spread across hundreds of ports in seconds?
One source, one port, many targets
10.0.0.5 → 10.0.0.11 TCP →445 [SYN]
10.0.0.5 → 10.0.0.12 TCP →445 [SYN]
10.0.0.5 → 10.0.0.13 TCP →445 [SYN]
10.0.0.5 → 10.0.0.14 ARP who has 10.0.0.14?
ip.src == 10.0.0.5 && tcp.flags.syn == 1Next: Is this host normally a client? Sweeps from a workstation are not routine.
Repeated short sessions to one service
10.0.0.8 → SRV TCP →3389 [SYN]
SRV → 10.0.0.8 TLS Application Data
10.0.0.8 → SRV TCP →3389 [SYN] (new port)
SRV → 10.0.0.8 TCP [RST, ACK]
ip.dst == SRV && tcp.port == 3389Next: Line it up against auth logs — count attempts vs. one success.
Metronome traffic to one external IP
t+0s 10.0.0.42 → 203.0.113.9 TLS ~400 B
t+60s 10.0.0.42 → 203.0.113.9 TLS ~400 B
t+120s 10.0.0.42 → 203.0.113.9 TLS ~400 B
(little else to that IP)
ip.addr == 203.0.113.9Next: How old is the domain? Any SNI? JA3/JA4 of the client? Beacon + young domain + rare fingerprint = escalate.
Large, sustained, outbound
10.0.0.42 → 198.51.100.7 TLS 1460 B
10.0.0.42 → 198.51.100.7 TLS 1460 B
10.0.0.42 → 198.51.100.7 TLS 1460 B
198.51.100.7 → 10.0.0.42 TCP [ACK] len 0
Conversations, sort "Bytes A→B" descendingNext: Is the destination known? Is the volume normal for this host at this hour?
Long random names, one domain, high volume
q k3f9a2....x7.tun.bad.com TXT
q m0p1q8....z2.tun.bad.com TXT
q a9c4e1....r5.tun.bad.com NULL
(hundreds/min, little other traffic)
dns && dns.qry.name contains "bad.com"Next: Statistics → DNS — query count and rate to that domain vs. everything else.
The Statistics menu
Where you go before filtering, and where you confirm a hunch after. Most of these can push a filter back to the main window.
| Statistics → | Use it to… |
|---|---|
| Protocol Hierarchy | Every protocol in the file and its share of bytes. First stop for "is anything weird in here". |
| Conversations | Every pair of talkers with packet/byte totals and duration. Sort by bytes; right-click → Apply as Filter. |
| Endpoints | Per-host totals. The Map button plots external IPs geographically. |
| I/O Graph | Throughput over time, multiple filtered series. Beacon spacing, transfer spikes, gaps where a link died. |
| Expert Information | Wireshark's own findings — retransmissions, resets, malformed packets — grouped by severity. |
| DNS | Query/response counts, response codes, query types, slowest responses. |
| HTTP → Requests | Every URL requested, grouped by host. Fast way to see "what did this box fetch". |
| Capture File Properties | Time span, packet/drop counts, capture interface and filter, file and packet comments. |
Right-click & Follow Stream
Most of the work is done from the right-click menu on a packet or a field — not by typing filters from scratch.
On a field (details pane)
- Apply as Filter → Selected — Replace the bar with "= this value"
- Prepare as Filter — Build the bar but don't run it yet — chain several, then Enter
- Apply as Column — Promote this field to a permanent column (do this with the TLS server_name)
- Copy → Value / as Filter — Grab the value, or a ready-made filter expression
On a packet (list)
- Follow → TCP / HTTP / TLS Stream — The whole conversation reassembled, client vs server colour-coded
- Conversation Filter → TCP — Jump to just this 5-tuple
- Colorize Conversation — Paint this flow so you can track it while scrolling everything
- Packet Comment — Annotation saved inside the pcapng
Encrypted vs cleartext
On a modern network almost everything is TLS or QUIC. Know exactly what still leaks — it's more than people assume, and it's where the investigation starts.
You can still see
- DNS query names and answers (unless DoH / DoT is in use)
- TLS SNI — the destination hostname in the Client Hello, unless Encrypted Client Hello (ECH) is negotiated
- Server certificate — CN, SAN list, issuer, validity dates (TLS 1.2; inferable in 1.3)
- TLS version and cipher suites offered and chosen
- JA3 / JA4 client and server fingerprints — identify the software regardless of IP or port
- Every IP, port, packet size, direction, and timestamp — enough for beacon and volume analysis
- Cleartext HTTP if it happens at all — rare enough now to be notable by itself
You can’t see (without keys)
- HTTP method, path, headers, cookies, body — anything inside HTTPS
- Request and response contents — all of it is Application Data
- Credentials, tokens, uploaded / downloaded file contents over TLS
- Anything inside QUIC without the keys (and QUIC hides more of the handshake than TLS 1.2 did)
To decrypt your own traffic: set SSLKEYLOGFILE before launching the browser, then Preferences → Protocols → TLS → (Pre)-Master-Secret log filename. Only works for sessions whose keys you logged — not after the fact, not someone else's.
Capture filters (BPF)
A different, simpler grammar — and permanent: a capture filter decides what gets written to disk. Default to capturing everything and filtering the display.
The ones worth knowing
host 10.0.0.5
net 10.0.0.0/24
port 443
tcp port 80
src host 10.0.0.5 and dst port 53
not arp and not broadcast
host 10.0.0.5 and not port 22 # drop your own SSH
Watch out
- Syntax is not the display-filter syntax. host x, not ip.addr == x. No .field names.
- Primitives are host, net, port, src, dst, tcp, udp — combined with and / or / not.
- Set it on the welcome screen (bar under the interface list) or Capture → Options.
- In an incident you usually want no capture filter — you can't filter for evidence you didn't know you'd need.
Keyboard & navigation
Move
- Ctrl + /
- Jump to the display-filter bar
- Ctrl + G
- Go to packet number
- Ctrl + .
- Next packet in this conversation
- Ctrl + ,
- Previous packet in this conversation
- Alt + → / ←
- Forward / back through selection history
- Ctrl + F
- Find (string, hex, or filter)
Read & mark
- → / ←
- Expand / collapse a layer in the details pane
- *
- Expand all subtrees of the selected layer
- Ctrl + M
- Mark / unmark this packet
- Ctrl + Shift + N
- Next marked packet
- Ctrl + T
- Set / unset time reference (times count from here)
Right-click any column header to add, remove, reorder or resize columns. A good analyst layout: No., Time (UTC), Source, Destination, Protocol, Length, Info — plus server_name when working TLS.
Practice & references
Cheat sheets get you moving; reps make you fast. The practice sites give you real malicious PCAPs with questions and answer keys.
Reference
- packetlife.net cheat sheetsJeremy Stretch's Wireshark Display Filters + tcpdump PDFs. The classics; print them.
- Wireshark Display Filter ReferenceEvery field of every dissector, searchable.
- Wireshark User's GuideThe official manual.
- Wireshark Sample CapturesA protocol zoo to poke at.
Practise on real traffic
- malware-traffic-analysis.netBrad Duncan's training exercises: infection PCAPs with prompts and answers. Start here.
- CyberDefendersScored blue-team PCAP / DFIR challenges.
- Active Countermeasures — Malware of the DayC2 traffic samples (Cobalt Strike, Sliver, etc.) with write-ups.
- TryHackMe — Wireshark roomsThe Basics / Packet Operations / Traffic Analysis.
Go deeper
- Practical Packet Analysis — Chris SandersNo Starch Press. The standard first book.
- Applied Network Security Monitoring — Sanders & SmithThe SOC-side framing.
- ZeekPair with Wireshark for connection logs at scale — Wireshark for the packet, Zeek for the summary.