Learning Goals
3 minBy the end of this lesson you can:
- Explain what packet sniffing is and the legal boundaries (your own traffic only).
- Capture and inspect packets with
scapyon your own loopback/network. - See why plaintext HTTP leaks everything and HTTPS shows only ciphertext.
- Name the defences: TLS everywhere, switched networks, VPNs.
Warm-Up · The Wire Doesn't Keep Secrets
5 minData on a network is just electrical/radio signals anyone on the path can copy. On the same wifi or LAN segment, a sniffer can capture packets not addressed to it. If those packets are plaintext (HTTP, old FTP, Telnet), the contents — including passwords — are right there to read.
A packet sniffer captures raw frames as they cross a network interface. It's a legitimate, essential tool for defenders (debugging, intrusion detection, your own traffic analysis) — and the very threat that makes encryption mandatory. This lesson is a teaser: enough to understand the danger and appreciate HTTPS, captured strictly on traffic you generate. scapy is the Swiss-army-knife library; tcpdump/Wireshark are the standard tools.
New Concept · Sniffing, Safely & Legally
14 min⚠️ The legal line (same as always, sharper here)
Sniffing other people's traffic is wiretapping — illegal under laws like the US Wiretap Act and equivalents worldwide, even on a shared network. Capturing on a network you don't own/administer, or others' communications, is a serious crime. In this lesson you only capture your own machine's traffic (loopback or your own interface, traffic you generate) — in your lab, on a network you control. Sniffing also typically needs admin/root privileges, which is itself a signal of how privileged the capability is.
Capturing packets with scapy
from scapy.all import sniff, IP, TCP # pip install scapy (run as admin/root) # capture 10 packets on the loopback interface (your OWN local traffic) def show(pkt): if pkt.haslayer(IP): ip = pkt[IP] proto = "TCP" if pkt.haslayer(TCP) else "other" print(f"{ip.src} → {ip.dst} {proto} len={len(pkt)}") sniff(iface="lo", count=10, prn=show) # 'lo'/'lo0' = loopback = your own machine
sniff captures packets and calls your prn function on each. Filtering to your loopback (lo) keeps you watching only local, self-generated traffic. You can add a BPF filter="tcp port 8000" to focus.
The crux: plaintext vs. encrypted payloads
from scapy.all import sniff, Raw def peek_payload(pkt): if pkt.haslayer(Raw): data = bytes(pkt[Raw].load) # HTTP is readable text; HTTPS/TLS is random-looking bytes try: text = data.decode("utf-8") print("PLAINTEXT:", text[:80]) # e.g. "POST /login ... password=hunter2" except UnicodeDecodeError: print("ENCRYPTED:", data[:24], "...(unreadable ciphertext)") # sniff your own request to a LOCAL test server: sniff(iface="lo", filter="tcp", count=20, prn=peek_payload)
This is the whole point. Send a request to a local plain-HTTP test server and the payload — including any form data — is readable text in the capture. Send it over HTTPS and the payload is unintelligible ciphertext. The sniffer sees the same packets; only encryption changes what it learns.
Defences against sniffing
- Encrypt everything in transit — TLS/HTTPS (Lesson 9), SSH instead of Telnet, SFTP instead of FTP. Then a sniffer gets only ciphertext.
- Switched networks — modern switches send frames only to the intended port (vs. old hubs that broadcast to all). But ARP spoofing (Lesson 4) can defeat this — so don't rely on it alone.
- VPNs — encrypt all traffic over untrusted networks (public wifi).
- Network monitoring — IDS that watches for the sniffing's precursors (e.g. ARP anomalies).
The legitimate defender uses
Sniffing isn't only an attack: defenders use it constantly — debugging why a connection fails, capturing traffic for an IDS, analysing malware's network behaviour in a sandbox, and verifying that your own app actually encrypts what it should. The skill is dual-use; the ethics decide which side you're on.
Worked Example · Prove HTTPS Protects You (locally)
12 minGoal: a controlled, local demonstration. Run a plain-HTTP and an HTTPS test server on localhost, send a request with a "password" to each, and sniff your own loopback to see one leak and one stay secret. Everything is your own traffic on your own machine.
# capture.py — run as admin/root; sniffs ONLY your loopback (your own traffic) from scapy.all import sniff, Raw SECRET_MARKER = b"password=" def inspect(pkt): if pkt.haslayer(Raw): data = bytes(pkt[Raw].load) if SECRET_MARKER in data: print("🔴 LEAKED on the wire:", data[:120].decode(errors="replace")) else: # likely TLS-encrypted application data head = data[:1] label = "TLS app-data" if head in (b"\x17",) else "binary" print(f"🔒 unreadable ({label}):", data[:16]) print("Sniffing loopback for 'password=' (your own traffic only)…") # capture a small number of packets on loopback while you make the requests sniff(iface="lo", filter="tcp", count=40, prn=inspect, store=False)
# in another terminal, send the SAME secret over HTTP then HTTPS to LOCAL servers import requests # (1) plain HTTP local server on :8000 → the sniffer will see the password requests.post("http://127.0.0.1:8000/login", data={"password": "demo-pw"}) # (2) HTTPS local server on :8443 (self-signed lab cert, L8-09) requests.post("https://127.0.0.1:8443/login", data={"password": "demo-pw"}, verify="cert.pem")
Sniffing loopback for 'password=' (your own traffic only)… 🔴 LEAKED on the wire: POST /login HTTP/1.1 ... password=demo-pw 🔒 unreadable (TLS app-data): b'\x17\x03\x03\x00@\x9a...'
Read the result
Side by side, the lesson is undeniable: the HTTP request's body — including password=demo-pw — appears in plaintext in the capture, while the HTTPS request shows only TLS application-data bytes (\\x17 records) that reveal nothing. The sniffer captured both equally; encryption is the only thing that protected the second. This is exactly why every login, every API, every page should be HTTPS — and why "it's just internal traffic" is a dangerous excuse. (Demo strictly on your own loopback; never sniff others.)
Try It Yourself
13 minAll capture is on your own loopback, on your own machine. If you can't run scapy (it needs privileges), Wireshark on a loopback capture demonstrates the same thing.
Capture 10 packets on loopback while you hit your local dev server, and print src → dst, protocol, and length for each. Confirm you only see your own traffic.
Reproduce the worked example: send a marked string over local HTTP and local HTTPS and confirm the sniffer reads the first and not the second. Write down what this proves about "internal" plaintext traffic.
Capture a batch of your own packets and tally them by protocol/port (how many TCP:443, TCP:80, UDP:53, etc.). Note how much of your normal traffic is already encrypted (443) — a sign of how far HTTPS adoption has come.
Hint
from scapy.all import sniff, TCP, UDP from collections import Counter tally = Counter() def count(pkt): if pkt.haslayer(TCP): tally[f"tcp:{pkt[TCP].dport}"] += 1 elif pkt.haslayer(UDP): tally[f"udp:{pkt[UDP].dport}"] += 1 sniff(count=100, prn=count, store=False) print(tally.most_common(10))
Mini-Challenge · A Plaintext-Credential Detector (for your own app)
8 minBuild a defensive tool: sniff your own traffic and alert if it ever sees credential-like patterns (password=, token=, Authorization: Basic) in plaintext — i.e. your app accidentally sent something over HTTP. This is a real way to catch a misconfiguration where TLS wasn't enforced.
Show a sample solution
from scapy.all import sniff, Raw import re PATTERNS = [re.compile(p, re.I) for p in [rb"password=", rb"passwd=", rb"token=", rb"api[_-]?key=", rb"authorization:\s*basic"]] def detect(pkt): if pkt.haslayer(Raw): data = bytes(pkt[Raw].load) for pat in PATTERNS: if pat.search(data): print("⚠️ PLAINTEXT CREDENTIAL on the wire — this should be " "over TLS! Snippet:", data[:80]) break print("Auditing OWN traffic for plaintext credentials (loopback)…") sniff(iface="lo", filter="tcp", prn=detect, store=False)
Non-negotiables: own traffic only, detects credential-like patterns in plaintext, framed as catching a missing-TLS misconfiguration.
Recap
3 minA packet sniffer captures raw frames crossing an interface — a legitimate defender's tool and the threat that justifies encryption. Sniffing others' traffic is wiretapping and illegal; you only ever capture your own traffic on a network you control (loopback in your lab). The decisive demo: plaintext HTTP leaks everything (passwords included) into the capture, while HTTPS shows only TLS ciphertext — proving why everything must be encrypted in transit. Defences: TLS/HTTPS everywhere, SSH/SFTP over Telnet/FTP, VPNs on untrusted networks, switched networks (with ARP-spoofing caveats). scapy/Wireshark/tcpdump are the tools; the ethics decide the side.
Vocabulary Card
- packet sniffer
- A tool that captures raw network frames crossing an interface.
- plaintext protocol
- HTTP/FTP/Telnet — contents (incl. passwords) are readable on the wire.
- wiretapping
- Capturing others' communications — illegal without authorization.
- encryption in transit
- TLS/SSH protecting data on the network so sniffers see only ciphertext.
Homework
4 minOn your own machine only, reproduce the HTTP-leaks / HTTPS-protects demo and capture the evidence (the plaintext password vs. the ciphertext). Build the plaintext-credential detector for your own traffic. Write a paragraph: why sniffing makes "internal-only, no HTTPS" a dangerous choice, and the exact legal boundary on whose traffic you may capture.
Sample · why "internal HTTP" is dangerous + the legal line
My capture showed it plainly: over HTTP, "password=demo-pw" appeared as readable text in the packet; over HTTPS, the same request was just TLS records (\x17...) — unreadable. Why "internal-only, no HTTPS" is dangerous: "internal" networks are not safe. Anyone who gets onto the LAN/wifi (a compromised laptop, a rogue device, an ARP-spoofing attacker) can sniff plaintext and harvest credentials and data. Insider threats and lateral movement make the internal network a hostile place too. Encrypt everywhere. The legal line: I may capture ONLY my own traffic, on a network I own/administer. Capturing other people's communications is wiretapping — illegal even on a shared/public network, even if I "don't use" what I see. All my testing was on 127.0.0.1 (loopback), my own machine.
Non-negotiables: captured HTTP-leak vs HTTPS-protect evidence (own traffic), the credential detector, and a clear statement of the wiretapping legal boundary.