Skip to content

Instantly share code, notes, and snippets.

@dreizehnutters
Last active April 27, 2026 10:18
Show Gist options
  • Select an option

  • Save dreizehnutters/c235ffeb2b4b8e915908e335738381de to your computer and use it in GitHub Desktop.

Select an option

Save dreizehnutters/c235ffeb2b4b8e915908e335738381de to your computer and use it in GitHub Desktop.
staggered nmap scan
#!/usr/bin/env bash
set -uo pipefail
usage() {
echo "$0 <NET_IN_CIDR>|<FILE> [--check]"
echo "$0 <RESULTS_DIR> --ports"
}
if [ $# -lt 1 ] || [ -z "${1:-}" ]; then
usage
exit 1
fi
NET=$1
bold=$(tput bold)
normal=$(tput sgr0)
error="${bold}[!]${normal}"
XMLS="$(command -v xmlstarlet)" || {
echo "${error} Required command 'xmlstarlet' not found"
exit 1
}
NMAP_BIN="$(command -v nmap)" || {
echo "${error} Required command 'nmap' not found"
exit 1
}
PROFILE_NMAP_OPTIONS=(
-T5
--min-rate=500
--min-hostgroup=16
)
SLOW_PROFILE_NMAP_OPTIONS=(
-T2
--min-rate=200
--min-hostgroup=4
)
COMMON_NMAP_OPTIONS=(
--privileged
-v
-d1
-Pn
--reason
--stats-every=10
--open
--script-timeout=90s
)
NSE_SCRIPTS=(
default version banner vulners
http-title http-server-header http-headers http-methods https-redirect
http-enum http-auth http-cookie-flags http-security-headers http-robots.txt
http-config-backup http-default-accounts http-git http-open-proxy
http-passwd http-webdav-scan http-userdir-enum http-errors http-devframework
ssl-cert ssl-enum-ciphers ssl-date ssl-known-key
smb-os-discovery smb-security-mode smb2-security-mode
smb-enum-shares smb-enum-users smb-enum-domains smb-protocols
smb-enum-groups smb-enum-sessions
snmp-info snmp-interfaces snmp-netstat
snmp-hh3c-logins snmp-win32-users snmp-win32-services snmp-win32-software
rpcinfo nfs-showmount
mysql-info mongodb-info ms-sql-info ms-sql-ntlm-info
smtp-commands smtp-ntlm-info imap-capabilities pop3-capabilities
ldap-rootdse rdp-ntlm-info rdp-enum-encryption
ldap-search ldap-novell-getpass
ssh2-enum-algos ssh-hostkey ssh-auth-methods ssh-publickey-acceptance
ntp-info dns-recursion dns-nsid dns-service-discovery
ftp-anon ftp-syst telnet-encryption
vnc-info vnc-title
rtsp-methods
modbus-discover
sip-methods sip-enum-users
ipmi-version ipmi-cipher-zero
redis-info memcached-info docker-version
s7-info enip-info bacnet-info knx-gateway-discover knx-gateway-info
ajp-methods ajp-request rsync-list-modules x11-access mqtt-subscribe auth-owners amqp-info
openflow-info rmi-dumpregistry nntp-ntlm-info openlookup-info ip-geolocation-geoplugin
)
NSE_SCRIPT_STRING=$(IFS=,; echo "${NSE_SCRIPTS[*]}")
NMAP_OPTIONS=(
"${COMMON_NMAP_OPTIONS[@]}"
"${PROFILE_NMAP_OPTIONS[@]}"
)
get_ports_from_XML() {
local nmap_path="${1}"
local result
result="$(
"$XMLS" sel -t \
-m '//port/state[@state="open"]/parent::port' \
-v './@portid' -n \
"${nmap_path}"/*.xml 2>/dev/null \
| sort -u -V \
| paste -sd, -
)"
if [ -z "$result" ]; then
return 1
fi
echo "$result"
}
is_valid_cidr() {
local cidr="${1}"
local cidr_pattern='^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$'
if ! [[ $cidr =~ $cidr_pattern ]]; then
echo "${error} Invalid CIDR notation: ${cidr}"
exit 1
fi
}
exit_fun() {
local rc="${1:-0}"
local msg="${2:-}"
if [ -n "${SUDO_UID:-}" ] && [ -n "${SUDO_GID:-}" ] && [ -n "${NET_PATH:-}" ]; then
chown -hR "${SUDO_UID}:${SUDO_GID}" "$NET_PATH" 2>/dev/null || true
fi
[ -n "$msg" ] && echo "$msg"
exit "$rc"
}
if [[ -f "${NET}" ]]; then
INPUT=(-iL "${NET}")
VERBOSE="$(tr '\n' ',' < "${NET}")"
elif [[ -d "${NET}" ]]; then
if [ "${2:-}" != "--ports" ]; then
echo "${error} Directory input is only valid with --ports"
exit 1
fi
INPUT=()
VERBOSE="${NET}"
else
is_valid_cidr "$NET"
INPUT=("${NET}")
VERBOSE="${NET}"
fi
if [ "${2:-}" = "--ports" ]; then
echo "${bold}[[[[ grepping open ports ]]]]${normal}"
get_ports_from_XML "$1" || exit 1
exit 0
fi
if [ "$EUID" -ne 0 ]; then
echo "${error} Please run as root (or set capabilities)"
echo "sudo setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip ${NMAP_BIN}"
exit 1
fi
NET_PATH="${PWD}/nmap-$(echo "${NET}" | tr '.' '_' | tr '\/' '-')-$(date +%s)"
mkdir -p "${NET_PATH}"
PF=$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c4)
if [ "${2:-}" = "--check" ]; then
echo "${bold}[[[[ max-coverage host discovery on ${VERBOSE} -> hosts_${PF}.xml ]]]]${normal}"
"$NMAP_BIN" \
-sn \
-PR \
-PE -PP \
-PS22,80,443 \
-PA80,443 \
-PU53,161 \
-v -d1 \
--stats-every=10 \
-oA "${NET_PATH}/hosts_${PF}" \
"${INPUT[@]}"
rc=$?
[ $rc -ne 0 ] && exit_fun "$rc" "${error} host discovery FAILED"
echo "${bold}[[[[ the following hosts are reachable ]]]]${normal}"
"$XMLS" sel -t \
-m "//host[status/@state='up']" \
-v "concat(address[@addrtype='ipv4']/@addr, ' ', hostnames/hostname/@name)" -n \
"${NET_PATH}/hosts_${PF}.xml" \
| sed '/^[[:space:]]*$/d' \
| tee "${NET_PATH}/up_hosts_${PF}.txt"
echo "${bold}[[[[ the following hosts did not respond ]]]]${normal}"
"$XMLS" sel -t \
-m "//host[status/@state='down']" \
-v "concat(address[@addrtype='ipv4']/@addr, ' ', hostnames/hostname/@name)" -n \
"${NET_PATH}/hosts_${PF}.xml" \
| sed '/^[[:space:]]*$/d' \
| tee "${NET_PATH}/down_hosts_${PF}.txt"
UP_XML_COUNT=$("$XMLS" sel -t -v "count(//host[status/@state='up'])" \
"${NET_PATH}/hosts_${PF}.xml")
UP_TXT_COUNT=$(wc -l < "${NET_PATH}/up_hosts_${PF}.txt")
if [ "$UP_XML_COUNT" -ne "$UP_TXT_COUNT" ]; then
echo "${error} Host count mismatch! XML=${UP_XML_COUNT}, TXT=${UP_TXT_COUNT}"
fi
exit_fun 0 "${bold}[[[[ finished ${NET_PATH} ]]]]${normal}"
fi
echo "${bold}[[[[ working in ${NET_PATH} ]]]]${normal}"
echo "${bold}[[[[ min tcp scan for ${VERBOSE} ]]]]${normal}"
"$NMAP_BIN" \
"${NMAP_OPTIONS[@]}" \
-p- -sS \
-oA "${NET_PATH}/init_${PF}" \
"${INPUT[@]}"
rc=$?
[ $rc -ne 0 ] && exit_fun "$rc" "${error} min tcp scan FAILED"
TCP_PORTS="$(get_ports_from_XML "${NET_PATH}")" || exit_fun 1 "${error} No open TCP ports found after pass 1"
echo "${bold}[[[ script='default++' scan on: ${TCP_PORTS} on ${VERBOSE} ]]]${normal}"
"$NMAP_BIN" \
"${NMAP_OPTIONS[@]}" \
-p"${TCP_PORTS}" \
-sV \
--script "$NSE_SCRIPT_STRING" \
-O \
--version-intensity 7 \
-oA "${NET_PATH}/version_${PF}" \
"${INPUT[@]}"
rc=$?
[ $rc -ne 0 ] && exit_fun "$rc" "${error} version scan FAILED"
echo "${bold}[[[[ nmap min udp for ${VERBOSE} ]]]]${normal}"
"$NMAP_BIN" \
"${NMAP_OPTIONS[@]}" \
--top-ports=100 \
-sUV \
--version-intensity 7 \
-oA "${NET_PATH}/uinit_${PF}" \
"${INPUT[@]}"
rc=$?
[ $rc -ne 0 ] && exit_fun "$rc" "${error} UDP scan FAILED"
exit_fun 0 "${bold}[[[[ finished ${NET_PATH} ]]]]${normal}"
@dreizehnutters
Copy link
Copy Markdown
Author

Automates network scanning tasks using Nmap, extracting information such as open ports and host statuses. Supports scanning a specified CIDR range or input file. Provides options for TCP, UDP, and version detection scans with configurable parameters. Outputs scan results to organized directories for further analysis.

Features

  • CIDR Range or File Input: The script supports scanning either a specified CIDR range or an input file containing a list of hosts.

  • Staggered Approach to Scanning: The script employs a staggered approach to scanning, minimizing overhead by setting appropriate options for scanning speed and host group size.

  • Optional Host Scan: Includes an option to perform a subnet scan to check the reachability of hosts within a specified CIDR range. This generates hosts.xml and provides insights into the status of hosts.

  • Grepping of Open Ports: Provides an option to extract and list all open ports from existing Nmap XML files, allowing for targeted analysis and exploration of network services.

  • Automated Directory Organization: Scan results are automatically organized into directories based on the provided input, facilitating easy access and analysis.

Usage

  1. Input Specification:

    • To scan a CIDR range: ./network_scan.sh <CIDR>
    • To scan hosts listed in a file: ./network_scan.sh <file>
  2. Options:

    • --check: Performs a subnet scan to generate hosts.xml and checks the reachability of hosts.
    • --ports: Extracts and lists open ports from existing Nmap XML files.
  3. Prerequisites:

    • Ensure Nmap and xmlstarlet are installed (sudo apt install nmap xmlstarlet).
  4. Execution:

    • Run the script with appropriate permissions (root or with capabilities).

Example Usage

# Scan a CIDR range
./network_scan.sh 192.168.1.0/24

# Scan hosts listed in a file
./network_scan.sh hosts.txt

# Check reachability of hosts in a CIDR range
./network_scan.sh 192.168.1.0/24 --check

# Extract and list open ports from existing Nmap XML files
./network_scan.sh results/ --ports

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment