Skip to main content
  1. Posts/

Network protocol analysis: Wireshark and tcpdump

··1804 words·9 mins·
Table of Contents
Capture packets only on networks you own or have written authorization to test. Passive capture on a network you don’t have permission to monitor is illegal in most jurisdictions, regardless of whether the traffic is encrypted.

Wireshark and tcpdump split the packet analysis work between them. Tcpdump is the small, portable, always-available capture tool that runs on anything with libpcap. Wireshark is the analysis tool where you actually make sense of what tcpdump collected. Most real workflows use both.

What packet analysis actually shows you today
#

Before the API details, one calibration point that shapes everything else: the modern internet is almost entirely TLS-encrypted. Casual “sniff the wire and read passwords” scenarios stopped working for public-web traffic around 2015 and are mostly gone from internal networks now too. What passive capture reliably reveals in 2026:

  • Traffic metadata: source, destination, protocol, ports, timing, packet sizes
  • Unencrypted internal-protocol traffic: internal DNS, ARP, DHCP, LLMNR, mDNS, NetBIOS, some management protocols
  • TLS handshakes and SNI (Server Name Indication) hostnames, even without decryption
  • TCP-level anomalies: retransmissions, window size issues, connection resets
  • Anything you deliberately captured with keys in place (see the TLS decryption section)

For anything TLS, you either need the keys (via SSLKEYLOGFILE from a browser or client you control), a TLS intercepting proxy on the endpoint, or you’re looking at metadata only. Plan the capture accordingly.

Installing without running as root
#

Both Wireshark and tcpdump need raw packet access, which historically meant running them as root. Both now support privilege separation: a small helper (dumpcap for Wireshark, capabilities on the tcpdump binary) handles the capture, and the GUI or CLI runs as your normal user.

On Debian and Ubuntu, the Wireshark install script prompts you to allow non-root capture. If it didn’t or you want to reconfigure:

sudo apt install wireshark
sudo dpkg-reconfigure wireshark-common
sudo usermod -aG wireshark $USER
newgrp wireshark

Log out and back in after adding yourself to the group. Now wireshark runs as your user with dumpcap handling the privileged capture step.

For tcpdump, most distributions ship with cap_net_raw and cap_net_admin capabilities on the binary, so tcpdump works without sudo for many use cases. If not, sudo setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /usr/sbin/tcpdump grants the same capabilities.

Wireshark
#

Wireshark 4.x is the current line as of 2026. The window layout hasn’t changed meaningfully in a decade: packet list at the top, dissected packet details in the middle, raw bytes at the bottom.

Capture and display filters
#

Wireshark has two filter systems that people mix up constantly:

Capture filters use BPF syntax (same as tcpdump) and decide what gets recorded. Set them before starting a capture. Cannot be changed mid-capture without restarting.

Display filters use Wireshark’s own syntax and decide what gets shown. Apply them any time after capture. Way more expressive than BPF.

Use capture filters when you know you don’t care about certain traffic and want to keep the capture file small. Use display filters for everything else. The most common mistake is trying to use display filter syntax in the capture filter field.

Common display filters:

# Protocol filters
http
dns
tls
arp
icmp

# Address filters
ip.addr == 192.168.1.100
ip.src == 10.0.0.5 and ip.dst == 8.8.8.8

# Port filters
tcp.port == 443
tcp.dstport == 22

# Content filters (much more powerful than BPF)
http.request.method == "POST"
dns.qry.name contains "malware"
tcp.flags.syn == 1 and tcp.flags.ack == 0

# Boolean combinations
(ip.src == 10.0.0.5 or ip.src == 10.0.0.6) and tcp.port == 443

Note: the DHCP dissector was renamed from bootp to dhcp in Wireshark 3.0. Older tutorials still show bootp.type filters that won’t work in current builds.

Following streams
#

Right-click any packet and select Follow, then TCP Stream, UDP Stream, HTTP Stream, HTTP/2 Stream, or QUIC Stream. Wireshark reassembles the full conversation and shows it in a readable format, with client and server data color-coded.

For HTTP over TLS, follow the QUIC or TLS stream to see the handshake and encrypted payload. To read the payload, you need the keys.

Decrypting TLS
#

The reliable way to decrypt TLS traffic during analysis is via SSLKEYLOGFILE, an environment variable that most browsers and many other clients honor. When set, the client logs its TLS session keys to that file. Wireshark reads the log and decrypts matching sessions.

On Linux or macOS:

export SSLKEYLOGFILE=~/tls-keys.log
firefox &   # or chromium, curl, etc.

In Wireshark: Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filename → point at ~/tls-keys.log.

This works for TLS 1.2 and TLS 1.3. It requires you to control the client (or the endpoint the client is running on). You cannot decrypt arbitrary third-party TLS by watching the wire.

Saving and exporting
#

File → Save As writes the current capture. Prefer pcapng over pcap; pcapng carries per-packet comments, capture metadata, and multiple interfaces in one file. pcap is fine for compatibility with older tools.

File → Export Objects extracts HTTP objects, DICOM files, SMB files, and a few other reassembled payloads. Useful when you’re recovering artifacts from a malware capture.

tcpdump
#

tcpdump is the CLI-first sibling. Same libpcap underneath, same BPF filter language, but built for shell pipelines and remote systems where Wireshark isn’t available.

Basic capture
#

# Basic capture on interface eth0
sudo tcpdump -i eth0

# Any interface (Linux)
sudo tcpdump -i any

# List available interfaces
tcpdump -D

BPF filters
#

The same BPF syntax Wireshark uses for capture filters:

# Protocol filters
sudo tcpdump -i eth0 icmp
sudo tcpdump -i eth0 arp

# Host filters
sudo tcpdump -i eth0 host 192.168.1.100
sudo tcpdump -i eth0 src 10.0.0.5 and dst 8.8.8.8

# Port filters
sudo tcpdump -i eth0 'tcp port 443'
sudo tcpdump -i eth0 'udp port 53'

# Combining
sudo tcpdump -i eth0 'host 192.168.1.100 and (tcp port 80 or tcp port 443)'

Quote filter expressions when they contain shell metacharacters (parentheses, or, and as separate words, redirections).

Writing and reading pcap files
#

The -w and -r flags write and read pcap files respectively:

# Capture to file
sudo tcpdump -i eth0 -w capture.pcap

# Read a saved file
tcpdump -r capture.pcap

# Filter while reading
tcpdump -r capture.pcap 'tcp port 443 and host 10.0.0.5'

Options worth knowing
#

# Print packet contents in hex and ASCII
sudo tcpdump -i eth0 -X

# Show more detail (verbose, use -vv or -vvv for more)
sudo tcpdump -i eth0 -v

# Don't resolve hostnames or port names (much faster on busy links)
sudo tcpdump -i eth0 -nn

# Stop after N packets
sudo tcpdump -i eth0 -c 100

# Capture full packet contents (default snaplen is unlimited in current tcpdump, but older builds capped at 65535)
sudo tcpdump -i eth0 -s 0

# Rotate capture files (5 files, 100MB each)
sudo tcpdump -i eth0 -w capture.pcap -C 100 -W 5

Long-running captures should always use file rotation, otherwise you’ll fill the disk and lose captures when the tool crashes.

Remote capture via SSH
#

The most useful capture pattern once you’re past basic single-host work: capture on a remote machine, pipe the pcap stream over SSH, analyze locally in Wireshark. Nothing gets installed on the remote host that isn’t already there.

# Capture on remote host, analyze locally in Wireshark
ssh user@remote-host "sudo tcpdump -i eth0 -U -w - not port 22" \
  | wireshark -k -i -

Breaking that down:

  • tcpdump -U (unbuffered) flushes each packet immediately so Wireshark doesn’t wait for buffer fills
  • -w - writes pcap to stdout
  • not port 22 excludes the SSH session itself; without this, you capture the packets carrying the capture, which is a fun feedback loop
  • wireshark -k -i - starts Wireshark capturing from stdin

You can save to a local file instead:

ssh user@remote-host "sudo tcpdump -i eth0 -U -w - not port 22" > capture.pcap

Or split into rotating local files:

ssh user@remote-host "sudo tcpdump -i eth0 -U -w - not port 22" \
  | tcpdump -r - -w capture.pcap -C 100 -W 5

Common blue team workflows
#

Finding rogue devices
#

Look at broadcast and multicast traffic on a segment. ARP, DHCP, LLMNR, mDNS, and NBNS all announce presence. A device asking for the DHCP server with a hostname that doesn’t match your naming convention is worth investigating.

# Wireshark
arp.opcode == 1 or dhcp
mdns
nbns

# tcpdump
sudo tcpdump -i eth0 -nn 'arp or (udp port 67 or udp port 68) or udp port 5353'

Investigating suspected malware traffic
#

Capture from the suspect host, then correlate with threat intel:

sudo tcpdump -i eth0 -w suspect.pcap host 10.0.0.42

In Wireshark, look for:

  • Beaconing: repeated connections to the same destination at regular intervals
  • DNS queries to newly-registered domains or DGA-looking names
  • HTTPS to IPs without SNI, or with SNI mismatched from the certificate CN
  • Data volumes that don’t match the claimed application (megabytes of “keepalive”)

For a systematic version of this, tools like Zeek , Suricata , and RITA do beaconing detection and metadata extraction at scale from pcap files.

Troubleshooting slow network transfers
#

TCP retransmissions, duplicate ACKs, and shrinking receive windows all show up cleanly in Wireshark’s expert info panel (Analyze → Expert Information). The Time-Sequence Graph (Statistics → TCP Stream Graphs → Time-Sequence) visualizes the transfer, which usually makes the problem obvious.

tshark for the middle ground
#

tshark is Wireshark’s CLI companion. Same dissectors and display filter syntax as the GUI, but scriptable. Useful for extracting specific fields from a pcap in a pipeline:

# Extract all HTTP request URLs from a pcap
tshark -r capture.pcap -Y http.request -T fields -e http.host -e http.request.uri

# Count DNS queries by name
tshark -r capture.pcap -Y "dns.flags.response == 0" -T fields -e dns.qry.name | sort | uniq -c | sort -rn

# Extract TLS SNI names (even from encrypted traffic)
tshark -r capture.pcap -Y "tls.handshake.type == 1" -T fields -e tls.handshake.extensions_server_name

The -Y flag applies a display filter, -T fields selects field output, and -e picks individual fields.

Extending Wireshark
#

Wireshark’s Lua scripting API lets you write custom dissectors for proprietary protocols and post-dissectors that add computed columns or annotations. Documentation is at wireshark.org/docs/wsdg_html_chunked/wsluarm.html .

Custom dissectors are the answer when you’re analyzing an internal protocol that Wireshark doesn’t understand. Write the dissector once, and every packet of that protocol in your captures becomes structured data instead of an opaque byte string.

Where the toolchain fits
#

Wireshark and tcpdump are the interactive-analysis end of network telemetry. For continuous capture and pattern matching at scale you want Zeek (which produces structured logs from every connection), Suricata (which does signature-based detection and alerting), or a commercial network detection and response product. For long-term storage of raw captures, look at Moloch/Arkime or Stenographer .

The pattern in most mature environments: continuous background capture with Arkime or Stenographer, real-time detection with Zeek/Suricata, and Wireshark/tcpdump for deep dives when something looks interesting.

UncleSp1d3r
Author
UncleSp1d3r
As a computer security professional, I’m passionate about building secure systems and exploring new technologies to enhance threat detection and response capabilities. My experience with Rails development has enabled me to create efficient and scalable web applications. At the same time, my passion for learning Rust has allowed me to develop more secure and high-performance software. I’m also interested in Nim and love creating custom security tools.