Lucene search
+L

Nmap (NASL wrapper)

🗓️ 03 Nov 2005 00:00:00Reported by Copyright (C) 2005 Michel ArboiType 
openvas
 openvas
🔗 plugins.openvas.org👁 1014 Views

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

Refs
Code
# 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 );

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation