Skip to content

Module 06 — Network Programming

Type 9 · Tool-Build — build a port scanner and banner grabber on raw Python socket, parse a packet trace with scapy, and prove the scanner with a test pinning OPEN/CLOSED verdicts plus a SYN-ACK/RST check. Test only systems you own or have written permission to. (Secondary: Concept Autopsy — understand what happens at each layer of the wire, not just the tool output.) Go to the hands-on lab →

Last reviewed: 2026-06

Python for Securityunderstanding the wire is what separates a script from a tool and a tool from an audit.

Difficulty: Beginner  ·  Estimated time: ~4–5 hrs (study + lab)  ·  Prerequisites: Foundations

In 60 seconds

Every network security tool — scanner, banner grabber, sniffer — sits on raw sockets. A TCP port scanner is thirty lines (connect_ex, a short timeout, record the result); banner grabbing reads the first bytes a service announces; scapy lets you forge, send, and parse packets layer by layer. The code is the easy part. The discipline is operational: scan only what you own or have written permission to test, and understand that filteredclosedopen — each response is information.

Why this matters

Security tools that manipulate the network — scanners, banner grabbers, sniffers, honeypot detectors — are all built on raw socket operations. Understanding how socket and scapy work at this level means you can build your own, read the source of existing tools, and understand exactly what a scanner is doing on the wire when you fire it at a target.

Objective

Write a port scanner and banner grabber in raw Python socket, then use scapy to capture and parse a small packet trace — and prove the scanner with a test you wrote: an assertion that it returns OPEN/CLOSED correctly for the fixed target port set, plus at least one SYN-ACK/RST check from the capture. Building the scanner and committing a test that pins its verdicts are equal halves — understanding what is happening at each layer, not just reading tool output.

The core idea

A TCP port scanner is thirty lines of Python. socket.connect_ex(host, port) returns 0 on success and an error code on failure; iterate the port list, record the result, close the connection. The hard part is not the code — it is the operational discipline: always scan only targets you own or have written permission to test, set a short timeout (socket.settimeout(1.0)) so the scan doesn't hang indefinitely, and understand that a filtered port (firewall drops the packet) behaves differently from a closed port (RST returned) — which is information in itself.

The mental model

The scanner code is trivial; the value is reading the responses. A RST means closed, a SYN-ACK means open, and silence (the packet dropped) means filtered — and that silence is itself intel about a firewall. You're not running a tool, you're interrogating a host one packet at a time.

Banner grabbing is the step after port enumeration: once you know a port is open, send a probe and read the first bytes the service returns. Most services announce themselves — SSH says SSH-2.0-OpenSSH_8.9, HTTP says the server header, SMTP says 220 mail.example.com. You do not need nmap for this; socket.recv(1024) and a .decode("utf-8", errors="replace") gets you there. Be aware that some services send nothing until you speak first (FTP is prompt-first; raw recv on an HTTPS port returns TLS hello bytes, not plaintext).

scapy is the Swiss Army knife of Python networking: it can forge arbitrary packets at any layer, send them, capture responses, and parse captures. The core mental model is that every packet is a stack of layers — Ether / IP / TCP / Raw — and you can read or set any field at any layer. sniff(count=10, filter="tcp port 22") captures 10 packets matching a BPF filter. wrpcap() and rdpcap() read and write .pcap files, so scapy can post-process captures from Wireshark or tcpdump. The power and the risk are the same: scapy will send what you tell it to send, including malformed packets — there is no safety net.

The gotcha

A port scanner pointed at the wrong subnet is an incident, and scapy will send whatever you tell it — malformed packets included — with no guardrail. Scan only systems you own or have written permission to test; every exercise here runs against the compose echo server or loopback, never an external host. Outside an authorized scope, scanning is illegal regardless of intent.

Go deeper: the scapy layer model and recv-first services

Every packet is a stack of layers — Ether / IP / TCP / Raw — and you read or set any field at any layer with the / operator; sniff(filter="tcp port 22") and rdpcap()/wrpcap() let you capture and post-process traces from Wireshark or tcpdump. Note that some services don't announce until you speak first: FTP is prompt-first, and raw recv on an HTTPS port returns TLS hello bytes, not plaintext.

Learn (~3 hrs)

Socket programming (~1 hr) - Socket Programming in Python — Real Python — the definitive walkthrough; read through "TCP Sockets" and "Multi-Connection Client and Server"; skip the async section for now. - socket — Low-level networking interface (Python docs) — reference; focus on socket(), connect_ex(), settimeout(), recv(), sendall().

Scapy (~2 hrs) - Scapy — Official documentation, "Building packets" — the "Quick start" and "Building packets" sections; focus on the layer stack model (/ operator).

Key concepts

  • socket.connect_ex() for non-throwing port probes; settimeout() to prevent hangs
  • Banner grabbing: recv-first vs probe-first services
  • Filtered vs closed vs open: what each TCP response means
  • Scapy layer model: Ether / IP / TCP / Raw
  • BPF filters in scapy.sniff() — same syntax as tcpdump
  • Authorization: scan only what you own or have written permission for
  • Verify by test, not by eye: a learner-written test that asserts OPEN/CLOSED per the fixed target ports (plus a SYN-ACK/RST check) — the ownership half, not a diff against make demo

AI acceleration

Ask a model to write a port scanner. It will produce a working one. Then ask it to add a --timeout argument and handle the ConnectionRefusedError, TimeoutError, and OSError cases explicitly. The error handling is where the model's first draft usually has gaps — and a scanner that crashes on a filtered port is not a scanner.

AI caveat

A model produces a working scanner immediately, but its first draft typically skips the error cases — ConnectionRefusedError, TimeoutError, OSError on a filtered port. Push it to handle each explicitly; a scanner that crashes on the first filtered port can't survive a real subnet.

Check yourself

  • On the wire, how do open, closed, and filtered ports differ — and why is "filtered" still useful intel?
  • Why set socket.settimeout() on a scan, and what breaks if you don't?
  • In scapy, what does the / operator do when you write IP()/TCP()?

Comments

Sign in with GitHub to comment. Choose the type: Feedback (errors or suggestions on this page) · Hints (help for fellow learners — no spoilers) · General (anything else).