netrecon.

Concurrent multi-engine network reconnaissance. nmap, masscan, rustscan, arp-scan, Shodan, gobuster, nikto, whatweb, searchsploit — all in one pass. Built-in IoT/camera/ICS device detection, SSL cert domain extraction, country/ASN-based targeting, GeoIP2 offline lookup, and full-stack firewall evasion.

VANTA Module Network Recon IoT Detection ICS/SCADA FW Evasion SSL Extraction Country/ASN GeoIP2 Python 3.8+ v0.0.1
CategoryNetwork Recon
LanguagePython 3
Locationtools/network/netrecon/
Author0xb0rn3
Version1.0.0
LicenseMIT
Beginner Startup
New to VANTA or netrecon? Start here.
Foundations guide →
What you need first
  • ▸ nmap installed: pacman -S nmap
  • ▸ A target IP or range you own or have permission to scan
  • ▸ Run as root for SYN scanning: sudo ./vanta
Your first safe command
set target 127.0.0.1    # your own machine
set operation scan
run
Operations by difficulty
  • Easy: scan, host_discover, port_scan
  • Medium: service_detect, vuln_scan, iot_probe
  • Advanced: cve_chain, scada_probe, full_enum
Key terms: IP Address — The numeric address of a device on a network  ·  Port — A number (0–65535) identifying a service on a host  ·  nmap — The network scanner that drives most netrecon operations  ·  CVE — An identifier for a known vulnerability (CVE-YEAR-NUMBER)  — Full glossary →

Overview

What netrecon does

netrecon is a concurrent multi-engine network reconnaissance module inside VANTA. It runs nmap, masscan, rustscan, arp-scan, Shodan, gobuster, ffuf, whatweb, and searchsploit all in a single scan pass, building a full profile for every host discovered. Every engine runs in parallel — scan time scales with host count, not tool count.

On top of standard port scanning, the module adds automatic device category classification for IoT hardware, IP cameras, routers, NAS devices, ICS/SCADA systems, and Flock Safety LPR surveillance cameras. SSL certificate domains (CN + all SANs) are extracted from every TLS port. Country and ASN-based targeting lets you scope scans to entire countries or BGP autonomous systems. GeoIP2 local DB support provides offline, rate-limit-free ASN and country lookups.

Engines
Multi-Engine
nmap + masscan + rustscan + arp-scan all concurrent. First result wins.
Intelligence
CVE Correlation
100+ services + version-specific CVE lookup from VERSIONED_CVE_DB. NVD live enrichment optional.
Detection
Device Classify
Auto-detects camera, router, NAS, IoT, ICS/SCADA, surveillance. Protocol probes included.
TLS
SSL Domains
Extracts CN + all SANs from TLS certs on every host. CDN noise filtered. Stored in ssl_domains[].
Targeting
Country / ASN
country:de / asn:AS15169 targets fetch live CIDRs from ipdeny.com and RIPE stat API.
GeoIP
GeoIP2 Offline
MaxMind GeoLite2 local DB for ASN + country. No rate limits, works offline. Falls back to ipinfo.io.
Evasion
FW Bypass
Packet frags, random decoys, source-port spoof, data padding, proxychains wrapping.
Web
Web Profiling
gobuster/ffuf dir brute-force + whatweb + TLS cert + security header audit + cookie analysis.
Output
Multi-Format
JSON + self-contained HTML report (no xsltproc needed) + nmap XML + Metasploit RC file.

Quick Start

Running a scan

netrecon reads JSON from stdin and writes JSON to stdout. The input structure takes a target and a params object.

# Quick scan of a single host
echo '{"target": "192.168.1.1", "params": {"mode": "normal"}}' \
  | python3 netrecon.py | jq .

# Full network sweep - deep mode with output directory
echo '{
  "target": "192.168.1.0/24",
  "params": {
    "mode": "deep",
    "threads": 30,
    "output_dir": "/tmp/scan",
    "web_enum": true,
    "searchsploit": true
  }
}' | python3 netrecon.py

# IoT sweep - MQTT, RTSP, camera, ICS protocols
echo '{"target": "10.0.0.0/24", "params": {"ports": "device"}}' \
  | python3 netrecon.py | jq '.data.hosts[] | {ip, device_category}'

# Country-wide scan — all German IP space (ipdeny.com CIDRs, 1024 sample IPs)
echo '{"target": "country:de", "params": {"max_hosts": 1024}}' \
  | python3 netrecon.py

# ASN scan — all Google-announced prefixes (RIPE stat API)
echo '{"target": "asn:AS15169", "params": {"max_hosts": 200}}' \
  | python3 netrecon.py

# Evasion mode through proxychains
echo '{
  "target": "10.10.10.5",
  "params": {
    "mode": "evasion",
    "proxychains": true,
    "decoys": 10,
    "source_port": 53
  }
}' | python3 netrecon.py
Run as root for SYN scans (faster, stealthier). TCP connect fallback works without root but is noisier and slower.

Reference

Scan modes

The mode parameter controls the overall behaviour - timeouts, rate limits, which scripts run, and whether evasion flags are active.

quick fast
Top ports only. No vuln scripts. Minimal service detection. Use for initial triage on large ranges.
normal default
top-1000 ports. Full service/version detection. HTTP profiling. CVE correlation. Balanced speed.
deep thorough
All ports. vuln NSE scripts. OS fingerprinting. Deeper timeouts. Use for single hosts.
stealth quiet
Low rate (≤200pps). Extended timeouts. Slower but less likely to trip rate-based IDS.
evasion bypass
Full IDS/FW bypass: fragmented packets, RND decoys, source-port spoof, padding, banner/firewalk NSE.

Parameters

All parameters go inside the params object of the JSON input.

Core

ParameterValuesDefaultDescription
modequick | normal | deep | stealth | evasionnormalScan profile - controls speed, depth, and evasion posture
portspreset name or customtop-1000Ports to scan. See Port Presets section.
threadsinteger20Max concurrent host threads
rateinteger1000Masscan packets/sec
timeoutinteger (seconds)5Per-host base timeout
os_detectiontrue | falsefalseEnable OS fingerprinting via nmap -O
vuln_scriptstrue | falsefalseRun nmap default,vuln NSE scripts
nse_profilestring (e.g. vuln,exploit,auth)""Custom NSE script list
passive_onlytrue | falsefalseDNS/WHOIS/Shodan only - no active scanning
interfacestring (e.g. eth0)""NIC for ARP scan
excludecomma-separated IPs""IPs to skip entirely
shodan_keystring""Shodan API key for external intel
nvd_api_keystringenv: NVD_API_KEYNVD API key for live CVE enrichment
max_hostsinteger1024Max representative IPs sampled from country: / asn: targets

Evasion / Anonymity

ParameterValuesDefaultDescription
evasiontrue | falsefalseEnable all IDS/FW bypass flags on any mode
decoysinteger5Number of random decoy IPs in nmap -D RND:N
mtuinteger (8, 16, 24…)8Packet fragment MTU - smaller = harder to reassemble
source_portinteger53Spoof source port (53=DNS, 80=HTTP, 443=HTTPS)
data_lengthinteger (bytes)32Append random bytes to packets to confuse signatures
proxychainstrue | falsefalseWrap nmap/gobuster through proxychains4

Web Enumeration & Output

ParameterValuesDefaultDescription
web_enumtrue | falsetrueRun gobuster/ffuf + nikto + whatweb on discovered web ports
web_wordlistpath stringauto-detectCustom wordlist path for gobuster/ffuf
searchsploittrue | falsefalseRun searchsploit against each discovered service
output_dirpath string""Save nmap XML, HTML report, MSF RC, gobuster results here

Port presets

Pass any preset name as the ports parameter, or provide a custom comma-separated list or range like 80,443,8080-8090.

PresetPorts coveredUse case
quick21,22,23,25,53,80,443,445,3389,62078Fast triage - critical ports only
top-20Top 20 common service portsRapid sweep of standard services
top-100–top-ports 100Broader quick sweep
top-1000–top-ports 1000 (default)Standard full scan
web80,443,8000–8091,8443,8888,9000,9090,9200Web application focused
database1433,1521,3306,5432,6379,9200,11211,27017Database exposure audit
commonAll standard service ports including auth, mail, SMBBroad enterprise sweep
ios62078,5000,7000,548,3689,49152,88,5353Apple iOS/macOS device discovery
iot80,443,1883,8883,5683,5353,8080,8443,5000MQTT, MQTT-TLS, CoAP, mDNS IoT sweep
camera80,443,554,8000,8080,8443,37777,34567,3702IP cameras - RTSP, Hikvision, Dahua, ONVIF
router80,443,8080,8443,22,23,7547,8291Routers - TR-069, MikroTik Winbox, management
nas80,443,5000,5001,9000,445,139,22NAS - Synology DSM, SMB, management
ics80,443,502,20000,102,47808,1911,161,22ICS/SCADA - Modbus, DNP3, S7, BACnet, Niagara Fox
deviceAll IoT + camera + router + NAS + ICS ports combinedFull embedded device sweep
all1–65535Complete port space - use with care on large nets

Target formats

Pass any of these formats as the target field in the JSON input. Multiple targets can be comma-separated.

FormatExampleHow it works
Single IP192.168.1.1Direct host scan
CIDR192.168.1.0/24All hosts in subnet (capped at 1024 unless deep mode)
Range192.168.1.1-50Expanded to individual IPs
Hostnameexample.comResolved via DNS then scanned
Multi10.0.0.1,10.0.0.5Each target parsed independently
country:XXcountry:deFetches all CIDRs for the country from ipdeny.com, samples one IP per CIDR up to max_hosts. Passes full CIDR list to masscan natively.
asn:AS####asn:AS15169Fetches all announced prefixes for the ASN via RIPE stat API. Samples one IP per prefix up to max_hosts.
country: and asn: targets require requests to be installed for the CIDR/prefix fetch. The sampled IPs represent the range for per-host profiling; masscan receives the full CIDR list for port discovery.

SSL cert domain extraction

After port scanning, netrecon automatically probes every TLS port on each host (443, 8443, 9443, 465, 636, 993, 995) and extracts the certificate's Common Name and all Subject Alternative Names. The result is stored in host.ssl_domains[] and shown inline in the terminal output.

Extraction method

With cryptography installed, the full DER certificate is parsed to extract every SAN. Without it, the stdlib ssl module's decoded cert dict is used as a fallback — both CN and DNS SANs are captured.

Noise filtering

Raw domains from the cert are passed through _clean_domains() before being stored. This strips:

  • Wildcard prefixes (*.example.comexample.com)
  • CDN-assigned default hostnames (cloudfront.net, akamaiedge.net, fastly.net, cloudflare.net, amazonaws.com, azure*.net, trafficmanager.net)
  • Hash hostnames — long hex strings before the first dot
  • Bare labels with no dot (internal-only names)
# Example: host with TLS on 443
{
  "ip": "93.184.216.34",
  "ssl_domains": ["example.com", "www.example.com"]
}
Install pip3 install cryptography for full SAN extraction. Without it, only the CN and any SANs exposed via the stdlib decoded dict are captured.

GeoIP2 offline lookup

By default, ASN and country lookups use the ipinfo.io API (requires requests). If MaxMind GeoLite2 databases are present locally, the module uses them instead — no rate limits, no API key, fully offline.

DB search paths

The module checks these locations in order for each DB type:

# GeoLite2-ASN.mmdb — ASN number + organisation
~/.vanta/GeoLite2-ASN.mmdb
/var/lib/vanta/GeoLite2-ASN.mmdb
/usr/share/GeoIP/GeoLite2-ASN.mmdb

# GeoLite2-City.mmdb — country ISO code + city name
~/.vanta/GeoLite2-City.mmdb
/var/lib/vanta/GeoLite2-City.mmdb
/usr/share/GeoIP/GeoLite2-City.mmdb

Setup

pip3 install geoip2 --break-system-packages

# Download free DBs (account required) from:
# https://dev.maxmind.com/geoip/geolite2-free-geolocation-data

mkdir -p ~/.vanta
mv GeoLite2-ASN.mmdb GeoLite2-City.mmdb ~/.vanta/
If no local DB is found and requests is available, the module falls back to ipinfo.io automatically. If neither is available, ASN/country fields are left empty.

Device detection & IoT profiling

netrecon automatically classifies each host into a device category based on open ports, HTTP server headers, page content, favicon hashes, and active protocol probes. No manual configuration required - it runs on every scan.

Category
camera
RTSP 554 · Hikvision 8000 · Dahua 37777/34567 · ONVIF 3702 · Favicon hash (17 models)
Category
iot
MQTT 1883/8883 · CoAP 5683 · mDNS 5353 · MQTT CONNECT probe
Category
ics
Modbus 502 · DNP3 20000 · Siemens S7 102 · BACnet 47808 · Niagara Fox 1911
Category
router
TR-069 7547 · MikroTik Winbox 8291 · Admin panel detection
Category
nas
Synology DSM 5000/5001 · Shared storage port 9000 · SMB 445/139
Category
surveillance
Flock Safety & LPR camera MAC OUI prefixes from flock-back signatures (20 prefixes)

Camera fingerprinting

When a host is identified as a camera, netrecon fetches /favicon.ico from each web port and computes an mmh3 hash against 17 known camera vendor hashes. Identified models include: Axis, Hikvision, D-Link, Panasonic, Foscam, Vivotek, TP-Link, Logitech Circle, NETGEAR Arlo, Samsung SmartCam, Sony, Dahua, Amcrest, Toshiba, Sricam, Reolink, and Zmodo.

Server header signatures (boa, goahead, uc-httpd, dahua, hikvision-webs) and page title keywords are also checked as a fallback when mmh3 is not installed.

MQTT probe

On port 1883, netrecon sends a real MQTT v0.0.1 CONNECT packet and reads the CONNACK. A return code of 0x00 in the CONNACK means the broker accepted the connection without credentials - flagged as MQTT-NO-AUTH CRITICAL severity.

RTSP probe

On port 554, an RTSP OPTIONS request is sent. A 200 OK response without a 401 challenge indicates the stream is unauthenticated - flagged as RTSP-NO-AUTH HIGH severity.

ICS/SCADA

ICS ports have the highest detection priority. Any host with an open ICS port immediately gets device_category: "ics" and a CRITICAL-severity vulnerability finding for each exposed protocol. The known absence of authentication in Modbus, DNP3, and BACnet is flagged directly without any further probing needed.

! ICS exposure findings are always CRITICAL regardless of other scan results. The presence of Modbus (502), DNP3 (20000), Siemens S7 (102), BACnet (47808), or Niagara Fox (1911) on a reachable host is itself a critical finding. The module adds +40 to the risk score automatically.

Firewall & IDS evasion

Use mode: "evasion" or set evasion: true on any mode to activate all bypass flags. The following nmap flags are applied:

-f               # Fragment packets into 8-byte chunks
--mtu 8          # Force small MTU - stateless FW can't reassemble
-n               # Skip DNS resolution (no PTR lookups that alert SOC)
-Pn              # Treat all hosts as up - skip ICMP ping
-D RND:5         # Inject 5 random decoy IPs in each probe
-g 53            # Spoof source port as 53 (DNS) - passes some FW rules
--data-length 32 # Pad each packet with 32 random bytes

When proxychains: true, every nmap and gobuster call is prefixed with proxychains4 -q. Make sure your /etc/proxychains4.conf is configured before enabling this.

Evasion mode significantly increases scan time. The evasion timeout profile allows 480s for port scans and 720s for full host profiling. Combine with a low rate (rate: 50) for maximum stealth against deep packet inspection.

Recommended evasion configs

ScenarioConfig
Basic IDS bypassmode: "evasion"
Full anonymitymode: "evasion", proxychains: true, decoys: 15, source_port: 443
Rate-limit awaremode: "stealth", rate: 50, timeout: 20
Custom fragmentsevasion: true, mtu: 16, data_length: 64

Web enumeration

Web enumeration runs automatically after host profiling when web_enum: true (the default). It runs on all discovered HTTP/HTTPS ports.

Tools used

gobuster is tried first for directory brute-force. If not available, ffuf is used as fallback. Found paths are stored in svc.web_paths[] in the JSON output.

nikto runs a full vulnerability scan on each web port after directory brute-force completes. Findings are stored in svc.nikto_findings[] and the nikto source tag is added to the service entry. Nikto detects outdated server versions, dangerous methods, default files, misconfigurations, and known CVEs.

whatweb identifies CMS, frameworks, server software, and JavaScript libraries per-host. Results are merged into svc.http_technologies[].

Evasion in web tools

When evasion: true, all web scanning tools receive anti-detection measures automatically:

ToolEvasion applied
gobusterThread cap 5, random browser User-Agent, 500ms inter-request delay
ffufThread cap 5, random browser User-Agent, 0.5s delay between probes
niktoTechniques 1,2,5,7 (URI encoding, self-reference, fake param, random case), random browser UA, 1s pause between probes
whatwebRandom browser User-Agent, 500ms throttle
RTSP probeRandom browser User-Agent instead of vanta-netrecon

HTTP analysis

For each web port, http_probe captures:

  • HTTP status code, page title, server header
  • Technology fingerprints (20+ patterns including WordPress, Joomla, Django, Flask, Spring)
  • Security header audit - HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, CORP
  • Missing headers list (actionable finding)
  • Cookie security flags - Secure, HttpOnly, SameSite per cookie
  • Full redirect chain
  • TLS: subject, issuer, expiry, not-before, signature algorithm, Subject Alternative Names (SANs)

Wordlist discovery

If no web_wordlist is specified, the module searches these paths in order:

/usr/share/wordlists/dirb/common.txt
/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
/usr/share/seclists/Discovery/Web-Content/common.txt
/usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt

Output format

All output is JSON to stdout. The top-level structure is:

{
  "success": true,
  "data": {
    "target": "192.168.1.0/24",
    "scan_start": "2025-01-01T00:00:00",
    "scan_duration": 42.3,
    "hosts_discovered": 12,
    "engines_used": ["nmap", "masscan", "arp"],
    "capabilities": { ... },
    "hosts": [ ... ],       // per-host profiles (includes ssl_domains[])
    "summary": { ... },     // aggregate stats
    "outputs": { ... }      // html_report, nmap_xml, msf_rc paths (if output_dir set)
  },
  "errors": []
}

Host profile

{
  "ip": "192.168.1.100",
  "hostname": "camera-01.local",
  "mac": "ec:1b:bd:aa:bb:cc",
  "mac_vendor": "Flock Safety",
  "os_family": "Linux",
  "device_type": "Hikvision",       // specific model if identified
  "device_category": "camera",      // camera|router|nas|iot|ics|surveillance
  "risk_score": 85,
  "risk_level": "CRITICAL",
  "ssl_domains": ["camera-01.example.com", "www.example.com"],  // CN + SANs, noise-filtered
  "services": [ ... ],
  "vulnerabilities": [
    {
      "id": "RTSP-NO-AUTH",
      "severity": "HIGH",
      "desc": "RTSP stream accessible without authentication"
    },
    {
      "id": "ICS-EXPOSED-MODBUS",
      "severity": "CRITICAL",
      "desc": "MODBUS port 502 is exposed - unauthenticated ICS/SCADA access possible"
    }
  ],
  "asn": "AS12345",
  "country": "US",
  "shodan": { ... }
}

Service entry

{
  "port": 1883,
  "service": "mqtt",
  "version": "Mosquitto 2.0.15",
  "banner": "MQTT CONNACK received",
  "mqtt_no_auth": true,             // present if broker has no auth
  "snmp_community": "public",       // present if SNMP probed
  "smb_shares": ["ADMIN$", "C$"],  // present if SMB enumerated
  "http_title": "",
  "http_server": "",
  "http_technologies": [],
  "http_security_headers": {},
  "http_missing_headers": ["Strict-Transport-Security", "CSP"],
  "tls_subject": "",
  "tls_expiry": "",
  "tls_sans": [],
  "web_paths": ["/admin", "/config"],
  "cves": [
    {
      "id": "MQTT-NO-AUTH",
      "cvss": 9.1,
      "desc": "MQTT broker allows unauthenticated connections"
    }
  ],
  "sources": ["nmap", "banner_grab"]
}

Summary block

{
  "hosts_alive": 12,
  "total_open_ports": 87,
  "top_services": [["http", 8], ["ssh", 6], ["mqtt", 3]],
  "os_distribution": {"Linux": 9, "Windows": 2, "Unknown": 1},
  "risk_distribution": {"CRITICAL": 2, "HIGH": 3, "MEDIUM": 5, "LOW": 2},
  "vulnerabilities": {
    "critical": 4, "high": 11, "medium": 8, "total": 23,
    "top_cves": ["CVE-2021-36260", "CVE-2019-0708", "MQTT-NO-AUTH"]
  },
  "high_risk_hosts": ["192.168.1.100", "192.168.1.142"],
  "engines_used": ["nmap", "masscan", "arp", "tcp_connect"]
}

Examples

Standard network recon

echo '{"target": "192.168.1.0/24", "params": {}}' | python3 netrecon.py

Deep single host with Shodan + output

echo '{
  "target": "10.0.0.1",
  "params": {
    "mode": "deep",
    "shodan_key": "YOUR_KEY",
    "searchsploit": true,
    "output_dir": "/tmp/scan"
  }
}' | python3 netrecon.py

IoT sweep - MQTT, CoAP, cameras

echo '{"target": "192.168.1.0/24", "params": {"ports": "device"}}' \
  | python3 netrecon.py \
  | jq '.data.hosts[] | select(.device_category != "")
        | {ip, device_category, device_type, risk_level}'

ICS/SCADA exposure audit

echo '{"target": "10.0.0.0/16", "params": {"ports": "ics", "threads": 50}}' \
  | python3 netrecon.py \
  | jq '.data.hosts[] | select(.device_category == "ics") | {ip, vulnerabilities}'

Evasion - fragmented packets through proxy

echo '{
  "target": "10.10.10.5",
  "params": {
    "mode": "evasion",
    "proxychains": true,
    "decoys": 15,
    "source_port": 443,
    "mtu": 16,
    "data_length": 64
  }
}' | python3 netrecon.py

Country-wide scan

# Sample 500 hosts from German IP space (fetches all .de CIDRs from ipdeny.com)
echo '{"target": "country:de", "params": {"max_hosts": 500, "ports": "top-100"}}' \
  | python3 netrecon.py | jq '.data.hosts[] | {ip, country, asn, ssl_domains}'

ASN recon

# Probe 200 hosts across Google's announced prefixes (RIPE stat API)
echo '{"target": "asn:AS15169", "params": {"max_hosts": 200}}' \
  | python3 netrecon.py | jq '.data.hosts[] | {ip, asn, ssl_domains, risk_level}'

Passive intel only

echo '{"target": "example.com", "params": {"passive_only": true, "shodan_key": "KEY"}}' \
  | python3 netrecon.py

Camera sweep with HTML report

echo '{
  "target": "192.168.0.0/24",
  "params": { "ports": "camera", "output_dir": "/tmp/cam_scan" }
}' | python3 netrecon.py && open /tmp/cam_scan/netrecon_*.html

Requirements

TierWhat to installAdds
MinimumPython 3.8+TCP connect fallback - always works
StandardnmapService/version/OS detection, NSE scripts
Fullmasscan nmap arp-scan whatweb gobuster nikto + rootSYN scans, rapid discovery, web tech, directory brute, vuln scan
Python libspip3 install requests scapy dnspython shodanHTTP probing, ARP, DNS, Shodan
IoT/Camerapip3 install mmh3Favicon hash fingerprinting (17 camera models)
SSL extractionpip3 install cryptographyFull DER cert parsing — CN + all SANs from TLS certs
GeoIP2 offlinepip3 install geoip2 + GeoLite2 .mmdb filesOffline ASN + country lookup, no rate limits, no API key
SMB/SNMPapt install nmblookup smbclient snmpwalkNetBIOS names, share enum, SNMP community probe
Evasionproxychains4 + configured proxyRoute scans through SOCKS proxy
Full outputapt install ffuf xsltproc exploitdbffuf fallback, searchsploit (HTML report built-in, no xsltproc needed)
NVD enrichmentNVD API key in NVD_API_KEY env varLive CVSS scores and full CVE descriptions
The module uses capability detection at startup. If a tool is missing, that engine is silently skipped. The scan still runs with whatever is available - you never get a hard failure from a missing optional dependency.

Internals

Architecture Deep Dive

This chapter dissects every layer of netrecon — from nmap's raw TCP probes to how XML output is parsed and enriched, how service fingerprinting works at the byte level, and how to extend the module with custom detection logic.

Internals

nmap XML Output — Parsing Internals

netrecon does not parse nmap's human-readable terminal output (which changes between versions). It invokes nmap with -oX - to receive structured XML on stdout, then uses Python's xml.etree.ElementTree to extract port states, service versions, and OS fingerprints.

## nmap invocation by netrecon (simplified)
nmap -sV -sC -O --open -T4 -oX - <targets>
     ↑     ↑   ↑  ↑    ↑   ↑
     │     │   │  │    │   └─ output XML to stdout
     │     │   │  │    └───── timing template 4 (aggressive)
     │     │   │  └────────── only show open ports
     │     │   └───────────── OS detection
     │     └───────────────── run default NSE scripts (-sC)
     └─────────────────────── service/version detection

## nmap XML structure (abbreviated)
<nmaprun scanner="nmap" args="..." start="1748000000">
  <host starttime="..." endtime="...">
    <status state="up" reason="echo-reply"/>
    <address addr="192.168.1.10" addrtype="ipv4"/>
    <address addr="aa:bb:cc:dd:ee:ff" addrtype="mac" vendor="Apple"/>
    <hostnames>
      <hostname name="target.local" type="PTR"/>
    </hostnames>
    <os>
      <osmatch name="Linux 4.15 - 5.6" accuracy="96">
        <osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="4.X|5.X"/>
      </osmatch>
    </os>
    <ports>
      <port protocol="tcp" portid="22">
        <state state="open" reason="syn-ack"/>
        <service name="ssh" product="OpenSSH" version="8.9" extrainfo="Ubuntu Linux" method="probed" conf="10"/>
        <script id="ssh-hostkey" output="...">...</script>
      </port>
      <port protocol="tcp" portid="80">
        <state state="open" reason="syn-ack"/>
        <service name="http" product="nginx" version="1.24.0"/>
      </port>
    </ports>
  </host>
</nmaprun>

How netrecon Parses the XML

# Simplified parser matching what netrecon does internally
import xml.etree.ElementTree as ET

def parse_nmap_xml(xml_str: str) -> list[dict]:
    root = ET.fromstring(xml_str)
    hosts = []

    for host in root.findall("host"):
        if host.find("status").get("state") != "up":
            continue

        addr  = host.find("address[@addrtype='ipv4']").get("addr")
        mac_e = host.find("address[@addrtype='mac']")
        mac   = mac_e.get("addr") if mac_e is not None else None
        vendor = mac_e.get("vendor", "") if mac_e is not None else ""

        hostname_e = host.find("hostnames/hostname[@type='PTR']")
        hostname   = hostname_e.get("name") if hostname_e is not None else ""

        ports = []
        for port in host.findall("ports/port"):
            state_e = port.find("state")
            if state_e.get("state") != "open":
                continue
            svc    = port.find("service")
            scripts = {s.get("id"): s.get("output","") for s in port.findall("script")}
            ports.append({
                "port":    int(port.get("portid")),
                "proto":   port.get("protocol"),
                "service": svc.get("name","?") if svc is not None else "?",
                "product": svc.get("product","") if svc is not None else "",
                "version": svc.get("version","") if svc is not None else "",
                "scripts": scripts,
            })

        hosts.append({"ip": addr, "mac": mac, "vendor": vendor,
                      "hostname": hostname, "ports": ports})
    return hosts

Internals

Service Fingerprinting — Byte-Level Probes

nmap's -sV flag performs service version detection by sending carefully crafted probe strings to open ports and matching the responses against the nmap-service-probes database (usually at /usr/share/nmap/nmap-service-probes).

## nmap-service-probes format (excerpt — HTTP probe)
Probe TCP GetRequest q|GET / HTTP/1.0\r\n\r\n|
  # nmap sends these exact bytes to TCP port:
  47 45 54 20 2F 20 48 54 54 50 2F 31 2E 30 0D 0A 0D 0A
  G  E  T     /     H  T  T  P  /  1  .  0  \r \n \r \n

match http m|^HTTP/1\.[01] \d\d\d| p/HTTP title server/ v/$1/ cpe:/a:...../
  # Pattern: look for "HTTP/1.x NNN" at start of response
  # p/ = product name, v/ = version capture group, cpe/ = CVE lookup key

## SSH fingerprint probe — nmap reads the SSH banner first
Probe TCP NULL q||
match ssh m|^SSH-([.\d]+)-(.+)$|s p/OpenSSH/ v/$2/ i/protocol $1/
  # SSH servers send their banner without any probe:
  # "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.6\r\n"
  # nmap reads it, matches the regex, extracts version "8.9p1"

ADB TCP Fingerprint (Custom — netrecon)

netrecon's Android preset includes a custom ADB fingerprinting probe that's not in stock nmap. It sends the ADB CNXN handshake and reads the device banner:

## ADB CNXN packet structure (sent by netrecon to port 5555)
struct adb_message {
  uint32_t command;    // 0x4e584e43 = 'CNXN' (little-endian)
  uint32_t arg0;       // ADB protocol version: 0x01000001
  uint32_t arg1;       // max payload size: 0x00100000 (1MB)
  uint32_t data_length;
  uint32_t data_check; // CRC32 of data field
  uint32_t magic;      // command ^ 0xFFFFFFFF
  char data[];         // "host::features=..." string
};

## Hex dump of ADB CNXN probe (24 header bytes + data):
43 4E 58 4E  01 00 00 01  00 00 10 00  <len> <crc> <magic>
C  N  X  N   version      max_sz

data: "host::features=shell_v2,cmd,stat_v2,ls_v2,fixed_push_mkdir\0"

## If port 5555 is ADB-open, device responds with CNXN containing:
data: "device::ro.product.name=Pixel7;ro.product.model=Pixel 7;..."
## netrecon parses this to extract model, Android version, serial

Internals

Scan Evasion — How netrecon Bypasses Firewalls

The mode parameter controls how netrecon evades IDS/IPS and firewall filtering. Each technique manipulates different layers of the TCP/IP stack.

Fragmented Packets (mode: fragment)

## Normal TCP SYN to port 80:
[ IP Header ][ TCP Header (SYN) ]
  20 bytes      20 bytes min

## Fragmented (nmap -f flag — 8-byte IP fragments):
Fragment 1: [ IP Header (MF=1, offset=0) ][ first 8 bytes of TCP header ]
Fragment 2: [ IP Header (MF=0, offset=1) ][ remaining TCP header bytes ]

## Effect: IDS that reassembles TCP before inspecting sees the full SYN
## IDS that inspects individual fragments misses the TCP header structure
## Most modern IDS reassemble — use decoy or timing for better evasion

nmap flag: -f (8-byte frags), -ff (16-byte frags), --mtu <N> (custom)

Decoy Scan (mode: decoy)

## Decoy scan: SYN packets appear to come from multiple sources
nmap -D RND:10 <target>
# nmap sends SYN from 10 random IPs + your real IP, interspersed
# Target's firewall log shows 11 sources — analyst can't tell which is real

## Packet level: each "decoy" SYN has a spoofed source IP in the IP header
## nmap crafts raw IP packets (requires root) with the spoofed src fields
## The actual SYN-ACK replies go to the decoy IPs — real IP's SYN is buried

Idle Scan (mode: idle)

## Idle scan: your IP never appears in target's logs at all
nmap -sI <zombie_ip> <target>

## Mechanism (3 steps per port):
# 1. Query zombie's IP ID (IPID) sequence number:
      attacker → zombie: SYN/ACK → zombie responds RST with IPID=X
# 2. Send spoofed SYN to target, pretending to be the zombie:
      attacker (spoofed=zombie) → target: SYN
# 3. If port open: target sends SYN-ACK to zombie → zombie sends RST → zombie IPID increments
      attacker → zombie: SYN/ACK → zombie RST with IPID=X+2  ← port OPEN
# If port closed: target sends RST to zombie → zombie ignores → IPID=X+1  ← port CLOSED

## Works only if zombie has predictable IPID (older Linux/Windows hosts)
## Modern hosts use random IPID — useless as zombies

Extend

Customization & Extension

Adding a Custom Service Detector

# In netrecon.py — SERVICE_DETECTORS dict
# Each entry: port → function(banner: bytes) → dict | None

def detect_mqtt(banner: bytes) -> dict | None:
    # MQTT CONNECT packet response: starts with 0x20 (CONNACK)
    if len(banner) >= 4 and banner[0] == 0x20 and banner[1] == 0x02:
        rc = banner[3]  # 0=accepted, 1-5=errors
        return {
            "service": "mqtt",
            "auth_required": rc == 4,   # 0x04 = bad credentials
            "anonymous":     rc == 0,   # 0x00 = accepted without auth
            "severity": "CRITICAL" if rc == 0 else "INFO",
            "note": "Anonymous MQTT broker — subscribe to all topics: mosquitto_sub -h HOST -t '#'"
        }
    return None

SERVICE_DETECTORS = {
    1883: detect_mqtt,
    8883: detect_mqtt,   # MQTT over TLS
    # ... existing detectors ...
}

Adding a Custom Report Format

# netrecon.py — REPORT_FORMATS dict
def format_splunk(findings: list[dict]) -> str:
    """Output findings as Splunk-compatible key=value events."""
    lines = []
    for f in findings:
        kv = " ".join(f'{k}="{v}"' for k, v in f.items() if isinstance(v, (str, int)))
        lines.append(f"source=vanta_netrecon {kv}")
    return "\n".join(lines)

REPORT_FORMATS = {
    "json":    format_json,     # existing
    "html":    format_html,     # existing
    "splunk":  format_splunk,   # your addition
}

# Usage: set output_format splunk

Adding an NSE Script

netrecon passes --script arguments to nmap. You can bundle a custom NSE script with the VANTA repo and reference it directly:

-- tools/netrecon/scripts/vanta-banner-grab.nse
description = [[Grab raw banner from any open port for VANTA enrichment.]]
author = "0xb0rn3"
categories = {"discovery", "safe"}

local nmap    = require "nmap"
local shortport = require "shortport"

portrule = shortport.port_or_service({1-65535}, nil, "tcp")

action = function(host, port)
  local sock = nmap.new_socket()
  sock:set_timeout(3000)
  local status, err = sock:connect(host, port)
  if not status then return nil end
  local banner
  status, banner = sock:receive_lines(3)
  sock:close()
  if status then
    return banner:sub(1, 256)   -- trim to 256 chars
  end
end

-- Reference in netrecon:
-- set extra_scripts /path/to/vanta-banner-grab.nse
-- netrecon appends: --script /path/to/vanta-banner-grab.nse to nmap args

Study

Learning Path & Resources

Network reconnaissance is the first phase of every engagement. Build your knowledge from TCP/IP basics through advanced evasion techniques.

Step 1: Networking Fundamentals

ResourceTypeWhy
Professor Messer's CompTIA Network+ (free on YouTube)Free video courseCovers TCP/IP, subnets, ports, protocols — everything netrecon operates on. Watch before touching nmap.
TCP/IP Illustrated Vol 1 (Stevens)BookThe authoritative TCP/IP reference. Chapter 1-4 explain packets, routing, ports — the foundation for understanding nmap output.
Wireshark for beginners (free YouTube)FreeWatch actual packets as nmap scans. Run Wireshark alongside netrecon to see every probe at the byte level.

Step 2: nmap Mastery

ResourceFocus
Nmap Network Scanning (Fyodor, nmap.org/book) — free onlineThe official nmap book by its creator. Chapter 5 (port scanning) and Chapter 8 (OS detection) are essential. netrecon is a wrapper around nmap — knowing nmap deeply means you know netrecon deeply.
nmap NSE scripting guide (nmap.org/book/nse.html)Writing custom NSE scripts. Directly applicable to the netrecon customization guide above.
nmap cheat sheet (StationX)Quick reference for flags. netrecon sets most flags automatically — this helps you understand which ones and why.

Step 3: Practice Labs

PlatformCostFocus
TryHackMe — "Nmap" room (free)FreeGuided nmap exercises. Run netrecon alongside to compare outputs and understand what VANTA automates.
TryHackMe — "Passive Recon" and "Active Recon" roomsFreeCover DNS enumeration, WHOIS, Shodan — all integrated into netrecon's web enumeration mode.
HackTheBox — Any machine (scan phase)Free tierEvery HackTheBox machine starts with a reconnaissance phase. Use netrecon instead of running nmap manually — compare findings.
Vulnhub machinesFreeDownload VMs and scan them with netrecon. The "android" preset is great for local Android lab machines.

Step 4: Deep Reference

ResourceUse For
Shodan (shodan.io)Internet-wide passive reconnaissance — search for devices by banner, port, OS. Complements netrecon's active scanning.
Censys (censys.io)Similar to Shodan. Excellent for finding SSL certs, open ports across IP ranges before active scanning.
HackTricks — Network Services Pentesting (book.hacktricks.xyz)Port-by-port exploitation guide. Use it alongside netrecon's findings — when netrecon finds port 21 (FTP), HackTricks tells you how to test it.
PTES Technical Guidelines (pentest-standard.org)Penetration Testing Execution Standard — professional recon methodology that netrecon implements.

Practice Path

## Progression: from first scan to professional recon

Week 1: Your First Scan
  Setup: lab-setup.html — Kali + Metasploitable on host-only network
  Run: netrecon with default settings against 192.168.56.0/24
  Learn: read every finding — look up every open port in HackTricks

Week 2: Service Fingerprinting
  Target: Metasploitable 2
  Run: netrecon with service_scan preset
  Manual: replicate what netrecon does with nmap -sV -sC -O directly
  Compare: outputs — understand what each nmap flag adds

Week 3: Evasion Modes
  Setup: a pfSense firewall VM between Kali and targets
  Test: each evasion mode (fragment, decoy, idle)
  Monitor: pfSense logs — which techniques get logged vs evade

Week 4: Android Recon
  Run: netrecon with android preset against your lab network
  Connect: an Android device with ADB-WiFi enabled (port 5555)
  Observe: how netrecon detects and fingerprints the ADB service

Module Author

Who built this

netrecon is a community contribution to VANTA by 0xb0rn3, a senior security researcher and tool developer. The module consolidates features from several prior projects: s3rp (evasion/FW bypass), r3cond0g (MAC/CVE/TLS enrichment), and flock-back (IoT/surveillance detection).

0x
0xb0rn3
Senior Module Developer - VANTA
Security researcher focused on network reconnaissance, IoT security, and offensive tooling. Built and maintains netrecon, the VANTA framework's network module. Background in both red team operations and ICS/OT security.

A1 How Port Scanning Works

TCP SYN Scanning Under the Hood

Before you can use netrecon intelligently, you need to understand what is actually happening on the wire when a port scan runs. A port is just a number (0-65535) that the operating system uses to route incoming network traffic to the right program. When you scan a host, you are asking: which of these numbered doors are open, and what is standing behind them?

Full TCP Connect Scan
The scanner completes the full three-way handshake: SYN, then SYN-ACK from the server, then ACK from you. The OS logs this as a real connection. Reliable but loud - every connection attempt appears in server logs. Used by nmap with -sT. Does not require root privileges.
SYN (Half-Open) Scan
The scanner sends a SYN packet. If the port is open the server replies SYN-ACK. The scanner immediately sends RST (reset) to tear down the connection before it fully forms. The OS never logs it as a complete connection. Much stealthier. Requires root. This is what nmap uses with -sS (the default).
UDP Scan
UDP has no handshake. The scanner sends a UDP packet. If the port is closed, the OS sends back an ICMP "port unreachable" message. If nothing comes back, the port is either open or filtered. Much slower because you have to wait for timeouts. Used with -sU.
Port States
open - something is listening and accepted the probe.
closed - host responded but nothing is listening there.
filtered - no response at all (firewall dropped the packet).
open|filtered - UDP scan got no response, could be either.

SYN Scan Packet Flow

Scanner                    Target Host
   |                            |
   |------ SYN (port 80) -----> |   Scanner asks: is port 80 open?
   |                            |
   |  IF PORT IS OPEN:          |
   | <----- SYN-ACK ----------- |   Host says: yes, connect
   | ------ RST --------------> |   Scanner tears it down immediately
   |                            |   (never fully logged as a session)
   |  IF PORT IS CLOSED:        |
   | <----- RST --------------- |   Host says: nothing here, go away
   |                            |
   |  IF PORT IS FILTERED:      |
   |   (silence - no response)  |   Firewall dropped the packet silently
   |                            |

Why Scan Speed Matters

Timing controls how many packets per second you send. Faster scans finish quickly but are easy to detect and may trigger rate-limiting that causes you to miss open ports. Slower scans look like normal background traffic. nmap has six timing templates from T0 (paranoid, one packet every 5 minutes) to T5 (insane, flood speed). netrecon uses T3 by default and drops to T2 when evasion mode is active.

nmap -sS -T2 --scan-delay 500ms 192.168.1.1
  # T2 = polite timing; 500ms minimum delay between probes
  # reduces chance of triggering IDS rate-based alerts

A2 Reading Scan Output

Interpreting What netrecon Finds

Raw scan output can be overwhelming when you first see it. This section teaches you to read each column, understand what service version information reveals, and recognize which findings deserve immediate attention.

PORT column
Format is number/protocol. Example: 22/tcp means TCP port 22. 53/udp means UDP port 53. Well-known port assignments are defined by IANA - 80 is HTTP, 443 is HTTPS, 22 is SSH, 21 is FTP, 3306 is MySQL.
STATE column
open means a service answered. closed means the host is reachable but nothing listens there. filtered means a firewall or ACL is silently dropping your probes - you cannot determine if a service exists behind the filter.
SERVICE column
nmap looks up the port number in its service database and guesses what runs there. This is just a guess based on the port number alone, not verified. The VERSION column is what confirms it with a real banner grab.
VERSION column
nmap sends service-specific probes and reads the response to identify the actual software and version. Example: OpenSSH 7.4p1 tells you the exact SSH version, which you can then search for known CVEs with searchsploit.
OS Detection
nmap analyzes TCP/IP stack behavior (TTL values, window sizes, TCP options) to fingerprint the operating system. It is probabilistic - you get a percentage confidence score. High confidence OS detection helps narrow down which exploits to try.
Script Output
Lines starting with | are output from nmap NSE (Nmap Scripting Engine) scripts. These run automated checks - for example http-title fetches the page title, ssl-cert extracts certificate details, smb-vuln-ms17-010 checks for EternalBlue.

Example Output Walk-through

PORT     STATE  SERVICE    VERSION
22/tcp   open   ssh        OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0)
| ssh-hostkey:
|   2048 aa:bb:cc:dd (RSA)
80/tcp   open   http       Apache httpd 2.4.25
|_http-title: Welcome to Company Intranet
443/tcp  closed https
3306/tcp filtered mysql

  # Port 22 open: SSH is running. Note the version - 7.4p1 is old,
  # searchsploit openssh 7.4 may show relevant issues.
  # Port 80 open: Apache 2.4.25 is from 2017 - very outdated.
  # Port 443 closed: HTTPS not running at all.
  # Port 3306 filtered: MySQL port exists but a firewall is blocking it.
Key insight: Outdated service versions are your most reliable signal. When you see Apache 2.4.25 or OpenSSH 7.4, those are years-old versions with documented CVEs. Run searchsploit apache 2.4.25 immediately. netrecon does this automatically and includes the results in its report.

A3 Scan Evasion Techniques

Why Firewalls Block Scans and How to Get Around Them

A firewall or IDS (Intrusion Detection System) watches network traffic for patterns that look like scanning. The most obvious pattern is: one IP address sending SYN packets to dozens of different ports in rapid succession. Evasion techniques break up or disguise that pattern.

Packet Fragmentation
Split each probe packet into tiny fragments (8 bytes each). Old or misconfigured firewalls inspect only the first fragment and pass the rest without reassembly. The target OS reassembles them and processes the probe normally. Use nmap -f or --mtu 8. Modern stateful firewalls reassemble all fragments before inspection - fragmentation is less effective against them but still works against legacy gear.
Decoy Scanning
Generate fake probe packets from spoofed source IPs alongside your real probes. The firewall logs show 10 different source IPs all scanning at the same time - the defender cannot easily identify which one is the real attacker. Use nmap -D RND:10 for 10 random decoys. Your real IP must still be included so responses route back to you.
Slow Timing
IDS systems look for bursts. If you send one probe every 5 minutes it looks like background Internet noise, not a scan. nmap -T0 (paranoid) waits 5 minutes between each probe. A full 65535-port scan at T0 takes days - only practical against a handful of ports. netrecon uses --scan-delay and --max-rate to tune this.
Idle Scan (Zombie)
The most advanced evasion: use a third machine (the "zombie") whose IP ID counter you can observe. Forge probe packets as if they come from the zombie's IP. Responses from the target go to the zombie. By watching the zombie's IP ID increment, you can infer which ports are open without your real IP ever appearing in target logs. Use nmap -sI zombie_ip target.
nmap -sS -f --mtu 8 -D RND:5 -T2 --source-port 53 192.168.1.1 -p 22,80,443
  # -f        = fragment packets (8-byte chunks)
  # -D RND:5  = add 5 random decoy source IPs
  # -T2       = polite timing, slower probes
  # --source-port 53 = spoof source port as DNS (port 53 often passes FW rules)

A4 What To Do With Results

Turning Scan Data Into Attack Paths

A scan result is not a finding on its own - it is raw data. Your job is to turn that data into a ranked list of potential entry points. This section teaches a practical triage process.

Step 1 - Flag Old Versions
Every service version in the scan output should be checked against CVE databases. Run searchsploit <service> <version> for each result. netrecon does this automatically. Old versions with public exploits are your highest-priority targets.
Step 2 - Spot Default Services
Ports like 23 (Telnet), 21 (FTP), 2323 (alt-Telnet), 8080 (alt-HTTP admin panels) often run with default credentials. Always try admin/admin, root/root, admin/password against web panels before spending time on exploits.
Step 3 - Map Attack Surface
Each open port is a potential attack surface. HTTP/HTTPS means run web enumeration (gobuster, nikto). SSH means try credential stuffing or look for weak key algorithms. SMB (445) means check for EternalBlue or null sessions. Group findings by attack type.
Step 4 - Export for Tools
Save nmap output in XML format with -oX results.xml. Metasploit can import this directly with db_import results.xml and populate its internal host/service database. netrecon outputs JSON which you can feed to custom automation scripts.
Low-Hanging Fruit
Prioritize: (1) services with known RCE exploits and no auth, (2) default credentials on admin panels, (3) services running as root, (4) open databases (MongoDB, Redis, Elasticsearch) with no authentication configured. These are the fastest paths to initial access.
Export Formats
nmap supports -oN (normal text), -oX (XML for tool import), -oG (grepable format for shell scripting), and -oA (all three at once). netrecon produces JSON and Markdown by default for easy reading and automation.
nmap -sV -sC -O -oA full_scan 192.168.1.0/24
  # -sV  = detect service versions
  # -sC  = run default NSE scripts
  # -O   = OS detection
  # -oA  = save results in all 3 formats (normal, XML, grepable)

msfconsole -x "db_import full_scan.xml; hosts; services"
  # import scan results into Metasploit database
  # then list discovered hosts and services