A stateless packet filter checks each packet in isolation against an ACL of (protocol, src/dst IP, src/dst port, TCP flags). A stateful firewall instead builds a live connection table keyed by the 5-tuple (protocol, src IP, src port, dst IP, dst port) and walks every packet through a real TCP state machine:
CLOSED --SYN(out)--> SYN_SENT --SYN/ACK(in)--> ESTABLISHED
ESTABLISHED --FIN--> FIN_WAIT --FIN/ACK--> TIME_WAIT --timeout--> (evicted)
Inbound packets are only let through if they match an entry created by traffic that originated inside the network — an unsolicited inbound packet with no matching entry is dropped, full stop, regardless of what its header flags claim.
A naive stateless filter often approximates "this is return traffic" by just checking whether the ACK flag is set (the classic allow tcp any any established rule of early packet filters). That heuristic is exactly what Nmap's ACK scan exploits: an attacker sends a bare ACK packet with no real session behind it. The stateless filter sees ACK=1 and waves it through; the stateful firewall looks the 5-tuple up in its table, finds nothing, and drops it — the same technique that makes stateful inspection the baseline for every modern firewall (iptables --state ESTABLISHED,RELATED, Cisco ASA, pfSense, cloud security groups).
- New Outbound Connection — spawns a real SYN → SYN/ACK → ACK handshake, a couple of DATA round-trips, then a FIN → FIN/ACK teardown into TIME_WAIT.
- Send Spoofed Bare-ACK Packet — injects an inbound ACK-flagged packet that belongs to no tracked session; watch it bounce off a stateful firewall and sail through a stateless one.
- TIME_WAIT idle timeout — how long a closed connection's entry lingers in the table before eviction, mirroring a firewall's idle-connection timeout setting.