🧰 03-02: Sniffing Tools — tcpdump, Wireshark & Scapy¶
📌 Overview¶
Now that we know why sniffing works (03-01: Packet Sniffing Basics), let's take a practical tour of the tools people actually use to do it. Three tools dominate this space, each suited to a different job:
| Tool | Best for |
|---|---|
| tcpdump | Fast, scriptable, command-line capture — great for servers with no GUI |
| Wireshark | Deep, visual, point-and-click packet inspection |
| Scapy | Programmatic packet crafting and sniffing — a building block for custom tools |
💡 All three ultimately sit on top of the same underlying capture library on Linux/macOS: libpcap (on Windows, Npcap, the modern successor to WinPcap). That's why capture filter syntax is shared between tcpdump and Wireshark.
🖥️ tcpdump: The Command-Line Classic¶
tcpdump is a lightweight, text-based sniffer available on virtually every Unix-like system by default. It's the tool you reach for when you're SSH'd into a remote server with no graphical interface.
Basic Usage¶
# Capture on a specific interface, printing to the terminal
sudo tcpdump -i eth0
# Show more detail (verbose) and don't resolve hostnames/ports (faster, cleaner output)
sudo tcpdump -i eth0 -v -n
# Capture only 20 packets, then stop
sudo tcpdump -i eth0 -c 20
# Write the capture to a file for later analysis in Wireshark
sudo tcpdump -i eth0 -w capture.pcap
# Read a previously saved capture file
tcpdump -r capture.pcap
Filtering with BPF Syntax¶
tcpdump uses BPF (Berkeley Packet Filter) syntax to narrow down what gets captured:
# Only ICMP (ping) traffic
sudo tcpdump -i eth0 icmp
# Only traffic to/from a specific host
sudo tcpdump -i eth0 host 192.168.1.10
# Only TCP traffic on port 23 (telnet) from a given source
sudo tcpdump -i eth0 'tcp and src host 192.168.1.10 and dst port 23'
# Only traffic to/from an entire subnet
sudo tcpdump -i eth0 net 128.230.0.0/16
💡 Tip: Combine conditions with and/or/not — this same BPF syntax works identically inside Wireshark's capture filter box and inside Scapy's sniff(filter=...) argument.
🦈 Wireshark: Capture Filters vs. Display Filters¶
Wireshark is the most popular graphical sniffer, and it's worth understanding that it actually has two separate filtering systems that beginners often confuse:
| Capture Filter | Display Filter | |
|---|---|---|
| When it applies | Before a packet is captured/saved | After capture, only affects what's shown |
| Syntax | BPF syntax (same as tcpdump) | Wireshark's own rich syntax |
| Can you change it later? | No — packets not matching are gone forever | Yes — change it anytime, nothing is lost |
| Example | tcp port 80 |
http.request.method == "POST" |
Why the Distinction Matters¶
👉 A capture filter is a permanent decision made before packets ever hit disk/memory — useful when you know exactly what you want and need to keep file sizes small on a busy link.
👉 A display filter is applied to packets already captured — the safer default, since you can capture broadly (or everything) and then slice-and-dice the view as many times as you like without re-capturing.
Common Display Filter Examples¶
ip.addr == 192.168.1.10 # traffic to or from this IP
tcp.port == 443 # any TCP traffic on port 443
http # only HTTP traffic
dns.qry.name contains "bank" # DNS queries mentioning "bank"
tcp.flags.syn == 1 and tcp.flags.ack == 0 # SYN packets only (handshake starts)
💡 A very common workflow: capture everything with a broad or empty capture filter, then use display filters to investigate — this avoids the risk of missing something you didn't think to filter for up front.
🐍 Scapy: Sniffing and Crafting Packets in Python¶
Scapy is a Python library that goes beyond fixed-function tools like tcpdump — it lets you sniff, dissect, and construct arbitrary packets programmatically. This makes it the tool of choice for building custom sniffing/spoofing programs rather than just observing traffic. We'll use it heavily in the ARP spoofing and TCP attack lessons ahead.
Installing and Entering Interactive Mode¶
pip install scapy
sudo python3 # root/admin privileges are required for sniffing & sending raw packets
A Minimal Sniffer¶
from scapy.all import sniff
def print_pkt(pkt):
pkt.show() # pretty-print all the layers of the packet
# Sniff on a given interface, filtering only ICMP traffic
sniff(iface="eth0", filter="icmp", prn=print_pkt)
👉 What's happening here:
iface— which network interface to listen on (use a list, e.g.["eth0", "wlan0"], to sniff multiple interfaces at once)filter— a BPF filter string, exactly like tcpdump'sprn— a callback function that Scapy invokes once for every packet it capturesstore=0can be added to avoid keeping packets in memory (useful for long-running sniffers)
Crafting a Custom Packet¶
This is where Scapy really shines — building packets layer by layer using the / operator to stack protocol headers:
from scapy.all import IP, TCP, send
# Build an IP layer
ip = IP(dst="10.0.0.5")
# Build a TCP layer — a SYN packet aimed at port 80
tcp = TCP(dport=80, flags="S")
# Stack the layers: IP header, then TCP header
pkt = ip / tcp
pkt.show() # inspect before sending
send(pkt) # transmit it onto the network
👉 Every field you don't explicitly set gets a sensible default (e.g., IP() defaults src to the machine's own address, ttl to 64). You can override any field, including ones that are normally auto-generated — which is exactly how spoofing tools set an arbitrary source IP address:
💡 This single line is the technical seed of IP spoofing — nothing stops a program from lying about the source address field, because IP has no built-in authentication of who is allowed to claim a given source address. We'll build on this directly in 03-04: ARP Spoofing Attacks and all of Module 04.
Sniff-Then-Respond Pattern¶
Scapy also supports combining sniffing and spoofing in one program — sniff for a trigger packet, then immediately craft and send a forged response. This pattern reappears throughout later attack lessons:
from scapy.all import sniff, IP, ICMP, send
def respond(pkt):
if ICMP in pkt and pkt[ICMP].type == 8: # type 8 = echo request
print(f"Got a ping from {pkt[IP].src}, spoofing a reply...")
# (attack logic would go here)
sniff(filter="icmp", prn=respond)
🧭 Choosing the Right Tool¶
| Scenario | Tool |
|---|---|
| Quick check on a headless server | tcpdump |
| Deep-dive investigation with lots of protocol context | Wireshark |
| Automating an attack, building a custom tool, or repeatable testing | Scapy |
| Need to both observe and forge traffic | Scapy |
📌 Key Takeaways¶
- tcpdump is a fast, ubiquitous command-line sniffer using BPF filter syntax.
- Wireshark has two distinct filtering systems: capture filters (BPF syntax, applied before capture, irreversible) and display filters (Wireshark syntax, applied after capture, freely changeable).
- A safe default workflow is to capture broadly and refine with display filters afterward.
- Scapy is a Python library that can both sniff traffic and construct arbitrary packets field-by-field.
- Scapy's
sniff(iface=..., filter=..., prn=...)mirrors tcpdump's filtering but lets you run custom Python logic per packet. - Building packets with
IP()/TCP()and the/operator lets you override any field — including the source IP address, which is the root mechanism behind IP spoofing. - Sniffing and spoofing can be combined in a single Scapy program: sniff for a trigger, then craft and send a forged response — a pattern used throughout later ARP and TCP attack lessons.