Nmap (NASL wrapper) is a plugin that runs nmap to find open ports. It includes various preferences for different scanning techniques, service scan, RPC port scan, timing policies, and defeating RST/ICMP ratelimit
# SPDX-FileCopyrightText: 2005 Michel Arboi
# Some text descriptions might be excerpted from (a) referenced
# source(s), and are Copyright (C) by the respective right holder(s).
#
# SPDX-License-Identifier: GPL-2.0-only
if(description)
{
script_oid("1.3.6.1.4.1.25623.1.0.14259");
script_version("2023-08-01T13:29:10+0000");
script_tag(name:"last_modification", value:"2023-08-01 13:29:10 +0000 (Tue, 01 Aug 2023)");
script_tag(name:"creation_date", value:"2005-11-03 14:08:04 +0100 (Thu, 03 Nov 2005)");
script_tag(name:"cvss_base", value:"0.0");
script_tag(name:"cvss_base_vector", value:"AV:N/AC:L/Au:N/C:N/I:N/A:N");
script_name("Nmap (NASL wrapper)");
script_category(ACT_SCANNER);
script_copyright("Copyright (C) 2005 Michel Arboi");
script_family("Port scanners");
script_dependencies("toolcheck.nasl", "host_alive_detection.nasl");
script_mandatory_keys("Tools/Present/nmap");
script_xref(name:"URL", value:"https://nmap.org/");
script_xref(name:"URL", value:"https://nmap.org/book/performance-timing-templates.html");
script_xref(name:"URL", value:"https://nmap.org/book/man-performance.html");
script_add_preference(name:"TCP scanning technique :", type:"radio", value:"connect();SYN scan;FIN scan;Xmas Tree scan;SYN FIN scan;FIN SYN scan;Null scan;No TCP scan", id:1);
script_add_preference(name:"Service scan", type:"checkbox", value:"no", id:2);
script_add_preference(name:"RPC port scan", type:"checkbox", value:"no", id:3);
script_add_preference(name:"Fragment IP packets (bypasses firewalls)", type:"checkbox", value:"no", id:4);
script_add_preference(name:"Do not randomize the order in which ports are scanned", type:"checkbox", value:"no", id:5);
script_add_preference(name:"Source port :", type:"entry", value:"", id:6);
script_add_preference(name:"Timing policy :", type:"radio", value:"Aggressive;Insane;Normal;Polite;Sneaky;Paranoid;Custom", id:7);
script_add_preference(name:"Max Retries :", type:"entry", value:"", id:8);
script_add_preference(name:"Host Timeout (ms) :", type:"entry", value:"", id:9);
script_add_preference(name:"Min RTT Timeout (ms) :", type:"entry", value:"", id:10);
script_add_preference(name:"Max RTT Timeout (ms) :", type:"entry", value:"", id:11);
# nb: Note the comment on this preference in the script_get_preference() below
script_add_preference(name:"Initial RTT timeout (ms) :", type:"entry", value:"", id:12);
script_add_preference(name:"Ports scanned in parallel (max)", type:"entry", value:"", id:13);
script_add_preference(name:"Ports scanned in parallel (min)", type:"entry", value:"", id:14);
script_add_preference(name:"Minimum wait between probes (ms)", type:"entry", value:"", id:15);
script_add_preference(name:"Maximum wait between probes (ms)", type:"entry", value:"", id:16);
script_add_preference(name:"File containing grepable results : ", type:"file", value:"", id:17);
script_add_preference(name:"Do not scan targets not in the file", type:"checkbox", value:"no", id:18);
script_add_preference(name:"Data length : ", type:"entry", value:"", id:19);
script_add_preference(name:"Run dangerous port scans even if safe checks are set", type:"checkbox", value:"no", id:20);
script_add_preference(name:"Log nmap output", type:"checkbox", value:"no", id:21);
script_add_preference(name:"Defeat RST ratelimit", type:"checkbox", value:"no", id:22);
script_add_preference(name:"Defeat ICMP ratelimit", type:"checkbox", value:"no", id:23);
script_add_preference(name:"Send using IP packets", type:"checkbox", value:"no", id:24);
script_add_preference(name:"No ARP or ND Ping", type:"checkbox", value:"no", id:25);
script_tag(name:"summary", value:"This plugin runs nmap to find open ports.");
script_tag(name:"qod_type", value:"remote_banner");
exit(0);
}
include("host_details.inc");
include("list_array_func.inc");
phase = 0;
if( defined_func( "scan_phase" ) ) {
phase = scan_phase();
}
# Phases:
# 0: No network level scan requested or scanner does not support scan phases.
# 1: Network level scan requested, we need to scan network_targets instead
# of the "real" target and need to store the results prefixed with IP.
# 2: Network level scan was requested and completed.
# If we already did a network level scan, the results are in the DB and
# there is no point in scanning this individual host again.
# Just read and report the results for this host.
if( phase == 2 ) {
ports = get_kb_list( "Ports/tcp/*" );
foreach portstr( keys( ports ) ) {
port = split( portstr, sep:"/", keep:FALSE );
scanner_add_port( proto:"tcp", port:port[2] );
}
exit( 0 );
}
tmpfile = NULL;
function on_exit() {
if( tmpfile && file_stat( tmpfile ) )
unlink( tmpfile );
}
safe_opt = script_get_preference( "Run dangerous port scans even if safe checks are set", id:20 );
if( safe_opt && "yes" >< safe_opt )
safe = 0;
else
safe = safe_checks();
if( phase == 0 ) {
ip = get_host_ip();
esc_ip = "";
l = strlen( ip );
for( i = 0; i < l; i++ ) {
if( ip[i] == "." )
esc_ip = strcat( esc_ip, "\." );
else
esc_ip = strcat( esc_ip, ip[i] );
}
} else {
ip = "network";
esc_ip = "network";
}
res = script_get_preference_file_content( "File containing grepable results : ", id:17 );
res = egrep( pattern:"Host: +" + esc_ip + " ", string:res );
if( ! res ) {
opt = script_get_preference( "Do not scan targets not in the file", id:18 );
if( "yes" >< opt )
exit( 0 );
i = 0;
argv[i++] = "nmap";
if( TARGET_IS_IPV6() )
argv[i++] = "-6";
argv[i++] = "-n";
argv[i++] = "-Pn"; # Nmap ping is not reliable
argv[i++] = "-oG";
tmpdir = get_tmp_dir();
if( tmpdir && strlen( tmpdir ) ) {
tmpfile = strcat( tmpdir, "nmap-", ip, "-", rand() );
fwrite( data:" ", file:tmpfile ); # make sure that tmpfile could be created. Then we can check that tmpfile exist with file_stat().
}
if( tmpfile && file_stat( tmpfile ) )
argv[i++] = tmpfile;
else
argv[i++] = "-";
port_range = get_preference( "port_range" );
if( "T:" >< port_range ) {
p = script_get_preference( "TCP scanning technique :", id:1 );
if( p != "No TCP scan" ) {
if( p == "SYN scan" || p == "SYN FIN scan" )
argv[i++] = "-sS";
else if( p == "FIN scan" || p == "FIN SYN scan" )
argv[i++] = "-sF";
else if( p == "Xmas Tree scan" )
argv[i++] = "-sX";
else if( p == "Null scan" )
argv[i++] = "-sN";
else
argv[i++] = "-sT";
if( p == "FIN SYN scan" || p == "SYN FIN scan" ) {
argv[i++] = "--scanflags";
argv[i++] = "SYNFIN";
}
}
p = script_get_preference( "Defeat RST ratelimit", id:22 );
if( "yes" >< p )
argv[i++] = "--defeat-rst-ratelimit";
}
# UDP & RPC scans or fingerprinting may kill a buggy IP stack
if( ! safe ) {
p = script_get_preference( "Service scan", id:2 );
if( "yes" >< p )
argv[i++] = "-sV";
p = script_get_preference( "RPC port scan", id:3 );
if( "yes" >< p )
argv[i++] = "-sR";
p = script_get_preference( "Fragment IP packets (bypasses firewalls)", id:4 );
if( "yes" >< p )
argv[i++] = "-f";
}
if( port_range ) { # Null for command line tests only
if( "U:" >< port_range ) {
argv[i++] = "-sU";
p = script_get_preference( "Defeat ICMP ratelimit", id:23 );
if( "yes" >< p )
argv[i++] = "--defeat-icmp-ratelimit";
}
argv[i++] = "-p";
argv[i++] = port_range;
}
p = script_get_preference( "Do not randomize the order in which ports are scanned", id:5 );
if( "yes" >< p )
argv[i++] = "-r";
p = script_get_preference( "Source port :", id:6 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "-g";
argv[i++] = p;
}
p = get_preference( "source_iface" );
if( p =~ "^[0-9a-zA-Z:_]+$" ) {
argv[i++] = "-e";
argv[i++] = p;
}
# We should check the values when running in "safe checks".
custom_policy = FALSE;
p = script_get_preference( "Max Retries :", id:8 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--max-retries";
argv[i++] = p;
custom_policy = TRUE;
}
p = script_get_preference( "Host Timeout (ms) :", id:9 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--host-timeout";
argv[i++] = p + "ms";
custom_policy = TRUE;
}
p = script_get_preference( "Min RTT Timeout (ms) :", id:10 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--min-rtt-timeout";
argv[i++] = p + "ms";
custom_policy = TRUE;
}
p = script_get_preference( "Max RTT Timeout (ms) :", id:11 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--max-rtt-timeout";
argv[i++] = p + "ms";
custom_policy = TRUE;
}
# nb: The lowercase t in timeout is expected here as it was used in the initial script_add_preference and can't be changed there.
p = script_get_preference( "Initial RTT timeout (ms) :", id:12 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--initial-rtt-timeout";
argv[i++] = p + "ms";
custom_policy = TRUE;
}
min = 1;
p = script_get_preference( "Ports scanned in parallel (min)", id:14 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--min-parallelism";
argv[i++] = p;
min = p;
custom_policy = TRUE;
}
p = script_get_preference( "Ports scanned in parallel (max)", id:13 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--max-parallelism";
if( p < min )
p = min;
argv[i++] = p;
custom_policy = TRUE;
}
p = script_get_preference( "Minimum wait between probes (ms)", id:15 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--scan-delay";
argv[i++] = p + "ms";
custom_policy = TRUE;
}
p = script_get_preference( "Maximum wait between probes (ms)", id:16 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--max-scan-delay";
argv[i++] = p + "ms";
custom_policy = TRUE;
}
if( ! custom_policy ) {
timing_templates = make_array( "Paranoid", 0,
"Sneaky", 1,
"Polite", 2,
"Normal", 3,
"Aggressive", 4,
"Insane", 5 );
p = script_get_preference( "Timing policy :", id:7 );
if( isnull( p ) )
p = "Aggressive";
timing = timing_templates[p];
if( ! isnull( timing ) ) {
_timing = "-T" + timing;
argv[i++] = _timing;
# For passing it to gb_nmap_os_detection, find_service_nmap.nasl and nmap_mac.nasl
replace_kb_item( name:"Tools/nmap/timing_policy", value:_timing );
}
}
p = script_get_preference( "Data length : ", id:19 );
if( p =~ "^[0-9]+$" ) {
argv[i++] = "--data-length";
argv[i++] = p;
custom_policy = TRUE; # This is expected to be here as it doesn't affect the timing policies above
}
p = script_get_preference( "Log nmap output", id:21 );
if( "yes" >< p ) {
argv[i++] = "-vv";
log_output = TRUE;
}
p = script_get_preference( "Send using IP packets", id:24 );
if( "yes" >< p )
argv[i++] = "--send-ip";
p = script_get_preference( "No ARP or ND Ping", id:25 );
if( "yes" >< p )
argv[i++] = "--disable-arp-ping";
if( phase == 1 ) {
if( defined_func( "network_targets" ) ) {
argv[i++] = network_targets();
} else {
log_message( port:0, data:"ERROR: 'network_scan' mode requested but 'network_targets' function not available.");
exit( 0 );
}
} else {
argv[i++] = ip;
}
scanner_status( current:0, total:65535 );
res = pread( cmd:"nmap", argv:argv, cd:1 );
if( "You requested a scan type which requires root privileges" >< res ) {
log_message( port:0, data:"ERROR: You requested a Nmap scan type which requires root privileges but scanner is running under an unprivileged user. Start scanner as root or use a different portrange to get this scan working.");
exit( 0 );
}
if( log_output ) {
log_message( port:0, data:"nmap command: " + join( list:argv ) + '\n\n' + res );
}
if( tmpfile && file_stat( tmpfile ) )
res = fread( tmpfile );
if( ! res ) {
if( tmpfile )
report = "ERROR: Failed to read the file '" + tmpfile + "' containing the results of nmap.";
else
report = "ERROR: Failed to read the response of nmap. Maybe the nmap process timed out or was aborted?";
log_message( port:0, data:report );
exit( 0 ); # error
}
}
if( phase == 0 ) {
# TODO: Verify this pattern against newer and older nmap versions.
# It seems at least in nmap 7.70 this text isn't included in the -oG output.
if( egrep( string:res, pattern:"^# +Ports scanned: +TCP\(65535;" ) )
full_scan = TRUE;
else
full_scan = FALSE;
res = egrep( pattern:"Host: +" + esc_ip + " ", string:res );
if( ! res ) {
mark_dead = get_kb_item( "/ping_host/mark_dead" );
if( "yes" >< mark_dead )
set_kb_item( name:"Host/dead", value:TRUE );
exit( 0 ); # Host isn't alive / no ports open. Just exit here...
}
res = ereg_replace( pattern:'Host: +[0-9.]+ .*[ \t]+Ports: +', string:res, replace:"" );
# Fields:
# port_nb/state/protocol/owner/port_name/rpc_name/service/
# Example:
# Host: 127.0.0.1 () Ports: 111/open/tcp/bin/rpcbind (rpcbind V2)/(rpcbind:100000*2-2)/2 (rpc #100000)/, 111/open/udp//rpcbind (rpcbind V2)/(rpcbind:100000*2-2)/2 (rpc #100000)/, 113/open/tcp/root/ident //Linux-identd/, 119/open/tcp/root/nntp //Leafnode NNTPd 1.9.49.rel/, 123/open/udp//ntp ///, 137/open/udp//netbios-ns //Samba nmbd (host: CASSEROLE workgroup: MAISON)/, 138/open/udp//netbios-dgm ///, 139/open/tcp/root/netbios-ssn //Samba smbd 3.X (workgroup: MAISON)/ Ignored State: closed (194)
scanned = FALSE;
udp_scanned = FALSE;
ident_scanned = FALSE;
foreach blob( split( res, sep:",", keep:FALSE ) ) {
v = eregmatch( string:blob, icase:TRUE, pattern:"^(Host: .*:)? *([0-9]+)/([^/]+)/([^/]+)/([^/]*)/([^/]*)/([^/]*)/([^/]*)/?" );
if( ! isnull( v ) ) {
port = v[2];
status = v[3];
proto = v[4];
owner = v[5];
svc = v[6];
rpc = v[7];
ver = v[8];
if( "open" >< status ) # nmap 3.70 says "open|filtered" on UDP
scanner_add_port( proto:proto, port:port );
if( owner ) {
log_message( port:port, proto:proto, data:"This service is owned by user " + owner );
set_kb_item( name:"Ident/" + proto + "/" + port, value:owner );
ident_scanned = TRUE;
}
scanned = TRUE;
if( proto == "udp" )
udp_scanned = TRUE;
if( strlen( rpc ) > 1 ) {
r = ereg_replace( string:rpc, pattern:"\(([^:]+):.+\)", replace:"\1" );
if( ! r )
r = rpc;
log_message( port:port, proto:proto, data:"The RPC service " + r + " is running on this port. If you do not use it, disable it, as it is a potential security risk" );
}
if( ver ) {
ver = ereg_replace( pattern:"^([0-9-]+) +\((.+)\)$", string:ver, replace:"\2 V\1" );
log_message( port:port, proto:proto, data:"Nmap has identified this service as " + ver );
}
}
}
v = eregmatch( string:res, pattern:'Seq Index: ([^\t]+)' );
if( ! isnull( v ) ) {
idx = int( v[1] );
if( idx == 9999999 ) {
log_message( port:0, data:"The TCP initial sequence number of the remote host look truly random. Excellent!" );
set_kb_item( name:"Host/tcp_seq", value:"random" );
} else if( idx == 0 ) {
set_kb_item( name:"Host/tcp_seq", value:"constant" );
} else if( idx == 1 ) {
set_kb_item( name:"Host/tcp_seq", value:"64000" );
} else if( idx == 10 ) {
set_kb_item( name:"Host/tcp_seq", value:"800" );
} else if( idx < 75 ) {
set_kb_item( name:"Host/tcp_seq", value:"time" );
} else {
log_message( port:0, data:"The TCP initial sequence number of the remote host are incremented by random positive values. Good!" );
set_kb_item( name:"Host/tcp_seq", value:"random" );
}
set_kb_item( name:"Host/tcp_seq_idx", value:v[1] );
}
v = eregmatch( string:res, pattern:'IPID Seq: ([^\t]+)' );
if( ! isnull( v ) )
log_message( port:0, data:"The IP ID sequence generation is: " + v[1] );
if( scanned ) {
set_kb_item( name:"Host/scanned", value: TRUE );
set_kb_item( name:"Host/scanners/nmap", value:TRUE );
}
if( udp_scanned )
set_kb_item( name:"Host/udp_scanned", value:TRUE );
if( full_scan ) {
if( ident_scanned )
set_kb_item( name:"Host/ident_scanned", value:TRUE );
set_kb_item( name:"Host/full_scan", value:TRUE );
}
} else if( phase == 1 ) {
lines = split( res, sep:'\n', keep:FALSE );
foreach blob( lines ) {
c = split( blob, sep:"Ports: ", keep:FALSE );
d = split( c[0], sep:" ", keep:FALSE );
e = split( c[1], sep:", ", keep:FALSE );
if( ! isnull( e ) ) {
foreach f( e ) {
g = split( f, sep:"/", keep:FALSE );
set_kb_item( name:d[1] + "/Ports/tcp/" + g[0], value:1 );
}
}
}
}
scanner_status( current:65535, total:65535 );
Transform Your Security Services
Elevate your offerings with Vulners' advanced Vulnerability Intelligence. Contact us for a demo and discover the difference comprehensive, actionable intelligence can make in your security strategy.
Book a live demo