Internet scanners (Shodan, Censys, botnets) constantly sweep public IPs for open ports and vulnerable services. By analysing their source IP ranges and probe payloads, you can fingerprint them automatically. Instead of blocking them outright, redirect them transparently to a fake honeypot that looks vulnerable — gathering intelligence on what they're looking for while hiding your real services.
Internet scanners (Shodan, Censys, Infrawatch, Shadowserver) continuously probe every public IP on every port. For production infrastructure running non-standard services, this creates noise that obscures legitimate connection attempts and exposes service fingerprints. This writeup covers a detection and redirection system that identifies scanner probes, redirects them transparently to a honeypot listener, and alerts with enriched WHOIS context — while allowing legitimate connections through unchanged.
Shodan scans the entire IPv4 address space in roughly 5 minutes. Censys completes a full scan in under an hour. These services are commercially operated, have static IP ranges, and do not honor robots.txt equivalents for network services. Firewall rules that block their ranges are effective but brittle — ranges change, and blocking by IP is reactive.
A more useful approach: let scanner probes connect, identify them, serve a response from a honeypot, and log everything — while simultaneously ensuring legitimate connections reach the real service unmodified.
Scanner IP (Infrawatch, Shodan, Censys, etc.)
│
▼
iptables PREROUTING REDIRECT
│ (IP range match → redirect to honeypot port)
▼
Honeypot Listener
│ Sends custom response, logs connection details
▼
Discord/Webhook Alert
│ IP, scanner type, org name (WHOIS), timestamp
▼
Connection dropped
────────────────────────────
Legitimate IP (whitelisted)
│
▼
Direct to service (unchanged)
│
▼
Normal operation
Two methods are used in combination. IP range matching is fast and catches known scanners. Payload signature matching catches unknown scanners that share characteristics with their vendor.
Each major scanner operates from a fixed or semi-fixed CIDR range. These ranges are publicly documented (Shodan publishes its own IP list; others are identified from scan data and WHOIS records).
| Scanner | Published Ranges (sample) | Detection Reliability |
|---|---|---|
| Shodan | 66.240.192.0/24, 71.6.135.0/24, 71.6.165.0/24, 185.142.236.0/24 | High — publishes ranges |
| Censys | 162.142.125.0/24, 167.94.138.0/24, 167.94.145.0/24, 167.94.146.0/24 | High — publishes ranges |
| Infrawatch | 5.226.140.0/24, 69.5.169.0/24, 89.37.172.128/27, 194.88.98.0/24 | Medium — ranges change |
| Shadowserver | 74.82.47.0/24, 184.105.139.0/24, 184.105.247.0/24 | High — nonprofit, documented |
| BinaryEdge | 37.19.221.0/24, 143.42.56.0/24 | Medium |
| Stretchoid | 198.96.95.0/24 | Medium |
| Palo Alto Networks | 198.235.24.0/24 | High — security research |
Many scanners send identifiable strings in their initial probe or banner-grab payload. Matching on payload content catches scanners from IP ranges not yet in the blocklist.
# Known payload signatures (partial list) SCANNER_SIGNATURES = { "Censys": b"Censys", "Shodan": b"masscan", "Stretchoid": b"MGLNDD", # Stretchoid fingerprint "zgrab": b"zgrab", "nmap": b"Nmap", } def identify_scanner(src_ip, payload): # Check IP ranges first (faster, no payload parse needed) for scanner, ranges in SCANNER_RANGES.items(): for cidr in ranges: if ipaddress.ip_address(src_ip) in ipaddress.ip_network(cidr): return scanner # Fallback: payload signature match for scanner, sig in SCANNER_SIGNATURES.items(): if sig in payload: return scanner return None
When a scanner IP is identified, iptables PREROUTING REDIRECT transparently redirects all further connections from that IP to a honeypot port. The scanner receives a complete TCP handshake and a custom response — it appears to be a real service. The redirect happens before the packet reaches userspace, so the main service never sees it.
# Add scanner to redirect: map service port to honeypot port # Real service on :8080 → honeypot on :18080 iptables -t nat -A PREROUTING \ -s [SCANNER_IP] \ -p tcp --dport [SERVICE_PORT] \ -j REDIRECT --to-port [HONEYPOT_PORT] # Verify rule applied iptables -t nat -L PREROUTING -n | grep REDIRECT # Existing TCP connections survive — redirect applies to new connections only # Use --syn to restrict to SYN packets if needed
Whitelist rules are inserted at higher priority (-I instead of -A) and use ACCEPT to ensure legitimate IPs bypass the redirect. The whitelist check runs before any redirect rule.
The honeypot listens on redirect ports and handles every incoming connection. For each connection: receive the probe payload, identify the scanner type, send a tailored response (a plausible service banner, an error, or a static response), log the full interaction, and alert.
import socket, threading, ipaddress, subprocess, requests HONEYPOT_RESPONSES = { "Shodan": b"SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.6\r\n", "Censys": b"HTTP/1.1 200 OK\r\nServer: nginx/1.24.0\r\n\r\n", "default": b"Connection refused\r\n", } def handle_scanner(conn, addr): try: payload = conn.recv(1024) scanner_type = identify_scanner(addr[0], payload) response = HONEYPOT_RESPONSES.get(scanner_type, HONEYPOT_RESPONSES["default"]) conn.send(response) whois_info = get_whois(addr[0]) log_and_alert(addr[0], scanner_type, payload, whois_info) finally: conn.close() def get_whois(ip): try: result = subprocess.run(["whois", ip], capture_output=True, text=True, timeout=5) for line in result.stdout.splitlines(): if "OrgName" in line or "org-name" in line: return line.split(":")[1].strip() except: pass return "Unknown"
Known scanner ranges cover most probes, but new scanners and new IP ranges appear continuously. The auto-detector monitors incoming connections and applies heuristics to identify unknown scanners, automatically adding them to the redirect list.
Heuristics for auto-detection:
masscan, zgrab, nmap) in initial probeWhen the auto-detector identifies a probable scanner, it adds the IP to the redirect list and applies iptables rules immediately — before further probes reach the main service.
Each detected scanner generates a webhook alert with full context for triage. The alert includes the source IP, scanner classification, target port, WHOIS organization name, country, and the first 128 bytes of the probe payload.
# Alert payload (example — all fields populated from live detection) { "scanner_ip": "[SOURCE_IP]", "target_port": 8080, "scanner_type": "Censys", "org": "Censys, Inc.", "country": "US", "action": "redirected to honeypot", "probe_payload": "[first 128 bytes, hex-escaped]", "timestamp": "2026-08-29T14:32:11Z" }
iptables-save / iptables-restore or netfilter-persistent.Service fingerprinting: Scanners mapping exposed services by banner never reach the real service — they receive a controlled honeypot response. Your real service version and configuration stay unknown to scanner databases.
Reconnaissance visibility: Every probe is logged with timestamp, source IP, probe payload, and organization. This data is intelligence: attack surface exposure, which organizations are actively scanning, and whether specific IPs return repeatedly (targeted vs. broad scan).
Log noise reduction: Application-level logs stop being polluted by scanner traffic. Legitimate connection anomalies become visible against a clean baseline.
ipset for large blocklists instead of individual rulesip6tables rules — many scanner operators now scan IPv6 spaceICMP packet capture: IP header decode showing scanner probe structure. NFQUEUE intercept classifying by source IP range.
MITRE ATT&CK (Defender): Detect T1595 (Active Scanning), T1592 (Gather Victim Host Information), T1046 (Network Service Discovery)