Wireshark: Packet Capture Analysis for Beginners
Wireshark is the standard tool for capturing and inspecting network traffic at the packet level. In this guide we'll cover the things that trip up beginners most often: the very different syntaxes of capture filters versus display filters, walking through a TCP three-way handshake, reading ARP and DNS exchanges, and a handful of defensive use cases. Everything below works on Wireshark 4.x.
.pcap/.pcapng files as sensitive. Only capture on networks you own or are explicitly authorised to monitor.
Setup and First Capture
Install Wireshark from wireshark.org (Windows/macOS) or from your package manager (e.g. sudo apt-get install wireshark on Debian/Ubuntu). During installation on Linux, accept the prompt to let non-root members of the wireshark group capture; otherwise you'll need sudo every time.
Linux: let your user capture without sudo
# Add yourself to the wireshark group (you must log out / back in to pick this up). sudo usermod -aG wireshark "$USER" # Verify your interface list is populated as your normal user. dumpcap -D
Start Wireshark, double-click an interface (usually eth0, wlan0, or Wi-Fi), and you'll be staring at live traffic. Stop the capture with the red square in the toolbar.
Capture Filters vs Display Filters — Different Languages
This is the #1 thing that confuses people. Wireshark has two filter syntaxes that do similar-looking things, but they are not interchangeable.
- Capture filters use BPF (Berkeley Packet Filter) syntax. They decide which packets are captured to disk in the first place. They are evaluated by libpcap before Wireshark sees the packet.
- Display filters use Wireshark's own expression language. They decide which already-captured packets are shown in the list pane. They can use any decoded field name (e.g.
http.host,tls.handshake.type) which BPF cannot.
Quick reference:
Capture filter (BPF) Display filter (Wireshark)
------------------- --------------------------
Host host 10.0.0.5 ip.addr == 10.0.0.5
Either direction src or dst host 10.0.0.5 ip.src == 10.0.0.5 || ip.dst == 10.0.0.5
TCP port tcp port 443 tcp.port == 443
UDP only udp udp
Not ARP not arp !arp
HTTP requests (n/a — no field knowledge) http.request
DNS port 53 dns
Specific subnet net 10.0.0.0/24 ip.addr == 10.0.0.0/24
Combine tcp and port 443 tcp.port == 443 && ip.src == 10.0.0.5
Rule of thumb: use capture filters when you are capturing on a busy interface and don't want to drown in unrelated packets; use display filters for everything else. Once a packet is captured, you can change display filters as many times as you like without re-capturing.
Examples worth remembering
# Capture filter examples (set in Capture > Options > Capture filter for selected interface) host 192.168.1.50 tcp port 80 or tcp port 443 not (broadcast or multicast) ether host aa:bb:cc:dd:ee:ff # Display filter examples (type into the green bar above the packet list) ip.addr == 192.168.1.50 tcp.flags.syn == 1 and tcp.flags.ack == 0 # SYN-only packets http.request.method == "POST" dns.flags.response == 0 # DNS queries (not responses) tls.handshake.type == 1 # ClientHello tcp.analysis.retransmission # Wireshark's own anomaly detector
Reading the TCP Three-Way Handshake
TCP opens a connection with a three-packet exchange. Filter for one interesting connection (e.g. tcp.port == 443 && ip.host == example.com) and look at the first three packets. The handshake is the single most important thing to be able to read in Wireshark.
Client (C) Server (S)
── SYN seq=X ───────────────────▶
win=64240,
options: MSS, SACK, WS, ...
◀────────────── SYN, ACK seq=Y, ack=X+1 ──
win=65535,
options: MSS, SACK, WS
── ACK seq=X+1, ack=Y+1 ──────────────────▶
(connection is now ESTABLISHED, data can flow)
What to notice in Wireshark's packet detail pane:
- In the first packet, expand "Transmission Control Protocol" → "Flags" — only the
SYNbit is set, and the Acknowledgment number is 0 / undefined. - In the second packet, both
SYNandACKare set. The server's initial sequence isY; the ack number isX + 1(acknowledging the client's SYN consumes one sequence number, even though no real payload byte was sent). - By default Wireshark shows relative sequence numbers (so the first SYN shows seq=0). To see the real absolute numbers, untick Edit → Preferences → Protocols → TCP → Relative sequence numbers.
- The third packet is the client's ACK. Wireshark labels it as a "TCP ACK" — at this point the connection is fully established.
If you ever see SYN, then SYN, then SYN again with no SYN+ACK in between, the destination port is closed or filtered — that's the same pattern a stealth scan produces.
ARP — Who Has What MAC?
ARP resolves an IPv4 address to a MAC address on the local segment. The exchange is two packets: broadcast request and unicast reply.
Display filter to isolate ARP
arp
Request (Broadcast) : "Who has 192.168.1.1? Tell 192.168.1.50"
eth.dst = ff:ff:ff:ff:ff:ff eth.src = (sender)
arp.opcode = 1 (request)
Reply (Unicast) : "192.168.1.1 is at aa:bb:cc:dd:ee:01"
eth.dst = (original requester)
arp.opcode = 2 (reply)
Defensive note: if Wireshark reports "duplicate use of IP address detected" in the Expert Info pane, that means two different MACs are claiming the same IP. That is the signature of ARP spoofing (a man-in-the-middle attempt) or simply two devices misconfigured with the same static IP.
DNS — Query and Response
DNS over UDP/53 is one of the easiest protocols to read in Wireshark.
Display filter for a domain you care about
dns and dns.qry.name contains "example.com"
You'll see two packets per lookup (one query, one response). Expand the Domain Name System tree in the detail pane:
- Transaction ID matches request to response.
- Flags: bit 0 of the second byte (
QR) is 0 for query, 1 for response. - Questions section shows the QNAME and QTYPE (A, AAAA, MX, TXT, ...).
- Answers section appears in the response with the resolved records and their TTLs.
Defensive note: long, high-entropy, random-looking subdomain queries (x7p9d2.tunnel.evil.example) that occur repeatedly are a hallmark of DNS tunneling — exfiltration over DNS. Sort by dns.qry.name length, or filter on it, when hunting.
HTTP and Follow TCP Stream
Plain HTTP is rare on the public internet now but still very common on internal services, IoT devices, and management ports.
Common HTTP filters
http # all HTTP traffic http.request # only requests http.request.method == "POST" # just POSTs http.response.code >= 400 # errors http.host == "internal.example.com" # by Host header
Right-click any HTTP packet → Follow → HTTP Stream to see the full request/response as plaintext. For TCP-level reconstruction, right-click → Follow → TCP Stream; for TLS, Follow → TLS Stream shows the cleartext if you have the keys (next section).
A Note on TLS
Wireshark can decrypt TLS in two situations:
- Pre-master keys (the modern, recommended approach): set
SSLKEYLOGFILEin the environment of the client process (e.g. Chrome, Firefox, curl) before it starts. Then point Wireshark at the resulting file in Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filename. This works for TLS 1.3. - Private key (legacy): only works for TLS 1.2 with an RSA key exchange (not ECDHE) — which is rare today. Most modern handshakes use ephemeral key exchange and cannot be decrypted from the server key alone, which is the entire point of forward secrecy.
Statistics: Conversations, Endpoints, IO Graph
The Statistics menu is the most under-used part of Wireshark.
- Statistics → Conversations: every pair of hosts that exchanged packets, sortable by byte/packet count. Spot "top talkers" instantly.
- Statistics → Endpoints: individual hosts (IPv4, IPv6, TCP, UDP tabs). Right-click a row → Apply as Filter to focus on it.
- Statistics → I/O Graphs: traffic over time. Add multiple display-filter-based graphs (e.g. one for
tcp.analysis.retransmission, one fordns) to correlate visually. - Statistics → Protocol Hierarchy: what percentage of your capture is each protocol. Quick way to spot something unusual on the wire.
Exporting Objects
File → Export Objects → HTTP lists every HTTP-served file in the capture and lets you save them. Similar entries exist for SMB, FTP-DATA, and others. Useful in incident response when you want to recover a payload an attacker dropped over a plaintext channel.
Coloring Rules
View → Coloring Rules. The default ruleset already highlights TCP retransmissions, errors, and bad checksums. You can add your own — for example, paint any packet matching dns.qry.name matches "[a-z0-9]{20,}" bright red to catch suspicious DNS at a glance.
tshark — Wireshark in the Terminal
tshark is Wireshark's CLI. Use it when you need to capture on a remote/headless box, or when you want to script analysis.
Common tshark recipes
# List interfaces
tshark -D
# Capture on eth0 for 30s, write to file
sudo tshark -i eth0 -a duration:30 -w /tmp/capture.pcapng
# Capture only DNS, print summary lines
sudo tshark -i eth0 -f "port 53" -Y "dns"
# Read a file with a display filter and print specific fields
tshark -r /tmp/capture.pcapng \
-Y "http.request" \
-T fields -e ip.src -e http.host -e http.request.uri
# Top 10 DNS query names by frequency
tshark -r /tmp/capture.pcapng -Y "dns.flags.response == 0" \
-T fields -e dns.qry.name | sort | uniq -c | sort -rn | head -10
Note the -f flag is a capture filter (BPF) and -Y is a display filter — same distinction as the GUI.
Defensive Use Cases
To finish, a handful of small investigations you can run against any capture:
- Suspect ARP spoofing? Use the Expert Info pane (Analyze → Expert Information) and look for "duplicate IP address detected" entries. Compare ARP replies to your DHCP lease records.
- Suspect data exfil over DNS? Filter
dns.qry.nameand look at the length distribution. Healthy traffic is a long tail of short names; tunneling produces a flat distribution of long random-looking labels. - Suspect port-scan? Filter
tcp.flags.syn == 1 and tcp.flags.ack == 0and group by destination port via Statistics → Conversations. A single source talking to many destination ports in seconds is a giveaway. - Suspect slow application?
tcp.analysis.retransmission || tcp.analysis.duplicate_ack || tcp.analysis.zero_windowisolates almost every "the network feels slow" cause. - Suspect plaintext credentials? Wireshark's Statistics → Protocol Hierarchy + Follow → TCP Stream on any FTP, Telnet, HTTP basic-auth, or non-TLS SMTP/IMAP flow will show them in seconds. Disable those protocols where you find them.
Next Steps
Once you're comfortable with the above, look into:
- Display filter macros (Edit → Preferences → Macros) for filters you use constantly.
- Lua dissectors for custom or undocumented protocols you encounter on internal networks.
- Zeek (formerly Bro) for protocol-level logging at scale — Wireshark is great for single captures; Zeek is what you reach for when you need to process a continuous network feed.
And remember: the most powerful thing Wireshark gives you is ground truth. When two systems disagree about who said what to whom, the wire doesn't lie.