Back to All Modules

Network and Service Scanning

#Scan Strategy

  1. Discover responsive hosts without expanding beyond the supplied target files.
  2. Run a bounded common-port scan.
  3. Run all TCP ports only against reviewed live hosts.
  4. Run a top UDP scan separately.
  5. Fingerprint discovered services and use targeted, non-destructive NSE scripts.
  6. Preserve XML output for parsing and reporting.

#Paste-Ready Nmap Orchestrator

Save as network-scan.sh. It reads only the approved ips.txt and cidrs.txt.

#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

[[ -n "${ENGAGEMENT_ROOT:-}" ]] || { echo "Load engagement.env first"; exit 1; }
for tool in nmap python3; do
  command -v "$tool" >/dev/null 2>&1 || { echo "Missing dependency: $tool"; exit 1; }
done

TARGETS="$NORMALIZED_DIR/approved-network-targets.txt"
OUT="$RAW_DIR/nmap-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$OUT"
cat "$SCOPE_DIR/ips.txt" "$SCOPE_DIR/cidrs.txt" 2>/dev/null \
  | sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' | sort -u > "$TARGETS"
[[ -s "$TARGETS" ]] || { echo "No approved IP or CIDR targets"; exit 1; }

RATE="${SCAN_RATE:-500}"
echo "[*] Discovery against explicit scope"
nmap -sn -PE -PS22,80,443,445,3389 -PA80,443 -iL "$TARGETS" \
  --excludefile "$SCOPE_DIR/exclude.txt" -oA "$OUT/01-discovery"

awk '/Status: Up/{print $2}' "$OUT/01-discovery.gnmap" | sort -u > "$OUT/live-hosts.txt"
[[ -s "$OUT/live-hosts.txt" ]] || { echo "No responsive hosts detected"; exit 0; }

echo "[*] Common TCP ports"
nmap -sS -Pn --top-ports 1000 --open --min-rate "$RATE" -T3 \
  -iL "$OUT/live-hosts.txt" -oA "$OUT/02-top-tcp"

echo "[*] Full TCP ports"
nmap -sS -Pn -p- --open --min-rate "$RATE" -T3 \
  -iL "$OUT/live-hosts.txt" -oA "$OUT/03-full-tcp"

echo "[*] Top UDP ports"
nmap -sU -Pn --top-ports 100 --open -T3 --max-retries 2 \
  -iL "$OUT/live-hosts.txt" -oA "$OUT/04-top-udp"

TCP_PORTS="$(
  python3 - "$OUT/03-full-tcp.xml" <<'PY'
import sys
import xml.etree.ElementTree as ET

ports = {
    int(port.get("portid"))
    for port in ET.parse(sys.argv[1]).getroot().findall("./host/ports/port")
    if port.get("protocol") == "tcp"
    and port.find("state") is not None
    and port.find("state").get("state") == "open"
}
print(",".join(str(port) for port in sorted(ports)))
PY
)"
if [[ -n "$TCP_PORTS" ]]; then
  echo "[*] Service fingerprinting on discovered TCP ports: $TCP_PORTS"
  nmap -sV -sC -Pn -T3 --version-light -p "$TCP_PORTS" \
    -iL "$OUT/live-hosts.txt" -oA "$OUT/05-services"
fi

echo "Raw and XML output: $OUT"
BASH

#Parse XML into CSV

python3 - "$OUT/05-services.xml" <<'PY'
import csv
import sys
import xml.etree.ElementTree as ET

root = ET.parse(sys.argv[1]).getroot()
writer = csv.writer(sys.stdout)
writer.writerow(["address", "protocol", "port", "state", "service", "product", "version"])
for host in root.findall("host"):
    address = host.find("address").get("addr", "")
    for port in host.findall("./ports/port"):
        state = port.find("state")
        service = port.find("service")
        writer.writerow([
            address,
            port.get("protocol", ""),
            port.get("portid", ""),
            state.get("state", "") if state is not None else "",
            service.get("name", "") if service is not None else "",
            service.get("product", "") if service is not None else "",
            service.get("version", "") if service is not None else "",
        ])
PY
BASH

#Targeted Validation Examples

Run only scripts that match a discovered service. Avoid broad --script vuln sweeps against fragile production systems.

nmap -Pn -p 443 --script ssl-cert,ssl-enum-ciphers <approved-ip>
nmap -Pn -p 445 --script smb-protocols,smb2-security-mode,smb2-time <approved-ip>
nmap -Pn -p 161 -sU --script snmp-info <approved-ip>
nmap -Pn -p 631 --script http-title,http-headers <approved-ip>
BASH

See Port Scanning with Nmap for scan flags, timing tradeoffs, and service-specific follow-up.