Skip to content

Instantly share code, notes, and snippets.

@aayla-secura
Last active May 24, 2021 22:40
Show Gist options
  • Save aayla-secura/10b8cf9b779b6edec35ed135378789f5 to your computer and use it in GitHub Desktop.
Save aayla-secura/10b8cf9b779b6edec35ed135378789f5 to your computer and use it in GitHub Desktop.
Parse nmap output to print host port
#!/bin/bash
# TODO
# - Consolidate ports for the same host from different files
ONE_PER_LINE=0
AS_URLS=0
AS_IPS=0
NO_VERSION=0
SEP=" "
SUBSEP=","
FILES=()
HTTPS_RE="https|http.*(ssl|tls)|(ssl|tls).*http"
HTTP_RE="http|web"
HTTP_PORTS="80,8080,8000,8001"
HTTPS_PORTS="443,8443"
usage() {
cat <<EOF
Print open ports in nmap scan.
Usage: $(basename ${0}) [<options>] <.gnmap file> ...
Option:
-1 Display one open port per line. Default is one host per line with a comma-separated list of ports.
-s|--sep <sep> Separator to use between host and port. Default is '${SEP}'.
-S|--subsep <sep> Separator to use between ports. Default is '${SUBSEP}'. Ignored if -1 is given.
-u|--url Display HTTP services as URLs (this enables -1). Non-matching ports will not be displayed.
-n|--ip Do not use the domain name, only IP address.
-N|--no-version Do not show the version info.
--http-re <regex> Regex to use to detect HTTP services (checked last). Set to empty to disable and check only port. Default is '${HTTP_RE}'.
--https-re <regex> Regex to use to detect HTTPS services (checked first). Set to empty to disable and check only port. Default is '${HTTPS_RE}'.
--http-ports <ports> Always treat those ports (comma-separated) as HTTP services. Default is '${HTTP_PORTS}'.
--https-ports <ports> Always treat those ports (comma-separated) as HTTPS services. Default is '${HTTPS_PORTS}'.
Multiple files can be given. Comparison is case-insensitive.
EOF
exit 1
}
while [[ $# -gt 0 ]] ; do
case "${1}" in
-1)
ONE_PER_LINE=1
;;
-u|--url)
AS_URLS=1
ONE_PER_LINE=1
;;
-n|--ip)
AS_IPS=1
;;
-N|--no-version)
NO_VERSION=1
;;
-s|--sep)
SEP="${2}"
shift
;;
-S|--subsep)
SUBSEP="${2}"
shift
;;
--http-re)
HTTP_RE="${2}"
shift
;;
--https-re)
HTTPS_RE="${2}"
shift
;;
--http-ports)
HTTP_PORTS="${2}"
shift
;;
--https-ports)
HTTPS_PORTS="${2}"
shift
;;
-*)
usage
;;
*)
FILES+=("${1}")
;;
esac
shift
done
awk -F '\t' \
-v one_per_line="${ONE_PER_LINE}" \
-v as_urls="${AS_URLS}" \
-v as_ips="${AS_IPS}" \
-v no_version="${NO_VERSION}" \
-v http_re="${HTTP_RE}" \
-v https_re="${HTTPS_RE}" \
-v http_ports="${HTTP_PORTS}" \
-v https_ports="${HTTPS_PORTS}" \
-v sep="${SEP}" \
-v subsep="${SUBSEP}" '
function show_host_compact(host, ports, _p, _first, _pnum, _proto, _srv) {
_first = 1
for (_p in ports) {
if (! is_open(ports[_p])) { continue }
if (_first) {
printf "%s%s", hostname(host, 1), sep
}
_pnum = port_num(ports[_p])
_proto = port_proto(ports[_p])
_srv = port_srv(ports[_p])
printf "%s%s/%s", (_first ? "" : subsep), _pnum, _proto
if (! no_version) { printf " (%s)", _srv }
_first = 0
}
print ""
}
function show_host(host, ports, _p, _http, _pnum, _proto, _srv) {
for (_p in ports) {
if (! is_open(ports[_p])) { continue }
_pnum = port_num(ports[_p])
_proto = port_proto(ports[_p])
_srv = port_srv(ports[_p])
if (as_urls) {
_http = http_proto(ports[_p])
if (!_http) { continue }
printf "%s://%s:%s", _http, hostname(host), _pnum
} else {
printf "%s%s%s/%s", hostname(host, 1), sep, _pnum, _proto
}
if (! no_version) { printf " (%s)", _srv }
printf "\n"
}
}
function hostname(host, full, _f) {
if (full) { _f = 0 }
else { _f = 2 }
match(host, /([0-9.]+) \((.*)\)$/, m)
if (m[2] && ! as_ips) { return m[_f] }
return m[1]
}
function http_proto(port, _pnum, _srv) {
_pnum = port_num(port)
_srv = port_srv(port)
if (_port_or_service_matches(_srv, _pnum, https_re, https_ports)) {
return "https"
}
if (_port_or_service_matches(_srv, _pnum, http_re, http_ports)) {
return "http"
}
}
function _port_or_service_matches(srv, port, srv_re, port_list, _tmp, _i) {
if (srv_re && srv ~ srv_re) { return 1 }
split(port_list, _tmp, / *, */)
for (_i in _tmp) {
if (port == _tmp[_i]) { return 1 }
}
}
function is_open(port) {
return (port_field(port, 2) == "open")
}
function port_num(port) {
return port_field(port, 1)
}
function port_proto(port) {
return port_field(port, 3)
}
function port_srv(port, _srv) {
_srv = port_field(port, 5) " " port_field(port, 7)
return (_srv == " " ? "unknown" : _srv)
}
function port_field(port, i, _tmp) {
split(port, _tmp, "/")
return _tmp[i]
}
BEGIN { IGNORE_CASE = 1 }
# ($0, /^Ports: *(.*)/, m) {
($2 ~ /^Ports:/) {
host = gensub(/Host: /, "", 1, $1)
split(gensub(/Ports: /, "", 1, $2), ports, /, /)
if (one_per_line) {
show_host(host, ports)
} else {
show_host_compact(host, ports)
}
}
' "${FILES[@]}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment