Lucene search
K

Netstat Portscanner (SSH)

🗓️ 15 Aug 2004 00:00:00Reported by TenableType 
nessus
 nessus
🔗 www.tenable.com👁 955 Views

Netstat Portscanner for SSH includes security checks with trusted keys and signatures.

Refs
Code
#TRUSTED 7e8408cad61be01e234823b4edfc0e2d9f0a3089d5d4288d609c0a97101358b85a55f1ae766f1246fa44c2b115e9a807e7b7b85882601e09b65adf77c44293d9cafe5bef05d09a39d9610e0f70e5a54eaf4063a171fa822694030c7c885a7fcac01462cf9b25a4165e2b52d355a7cf16d45218d164988722908634023288467a56322b53420838588299c51beea033c18df8456c8b689b3197a12572992214ced6dced7dd217b6815457839c315fac16fff90e695699e489be027cec61c5496a8ac0a1c3dd8d2406cbd1a5bd33e6fd724ba0cb4dff82bb07fa4715b1785566c65c7f794427bf6f17472e065256b2d7e6f181d821a47d10353d24529d1d7b1c1e7c4e84298fe46ce124a6fdf6d8e4df4b93a86c1741ea8745111ee909f48230903c64109a5a70f9ea35fca73957293704f74889bbbfaa6d30bfe6d4e40998a6200319bc0355278eecafc8bc04e4c52eabf1d086056181b4e1082bc973e4d44870e880b4da135c5cdcda2610381848be9cb1a9a515f88e99b91041272d8a84825a3283ed8fa218403176cab3b74090bd73b1308d2169db108ec3f2854640fed0bb733796f2a9566bcaac630df73e84625290ac6de6dc3ac8305cc64d12f7d5c10bcd3295cdda36bff3e23256f8c32d702b51f80b812ae25412492e58511cb39237a6b77ad985b155b3bc3de29a6d0833e6c7f49710c785f5657f295eaeb95c931d
#TRUST-RSA-SHA256 a7bee60d4249fcbb894e24b16bbf08a2c9387e255a1e5c599422ab45d259796ac76465beedcae136b106655e33155ac2baa393a5cda63d4b3604064bb6d041056cdc9821f5b00487b5c357441d9c2d6ded791a9fdf46cf73ba3e309d1cd03ff15e1ccf51ea755c1a4cb2a4d7ef7d45928286a937ce1637cfc48c1a43c38739f120fb9843b3cf7edb889f5569391abece55975d152cf585d6d879455bffadf9c79cedc600069655043fde40260e49f0782fe25bc45d2c37b228f4485d78d7286756e8046a4cf6669b2b83263de4a05ca986b8dc2da9dc128da4b27e84479eaf653a96ec7ab25643870c7c0c9507dcb50e0e280e30303f002474ba85e9d2d61a3dbd309fb1121ab8660fc2d8b0587633ba02c763ff76eabd4d611216fb59efab2906fe55b86bdd6dd35bb870d2fd25fb6f59b8177de82347dd49722a91dabc526a91c676216fa410bb8b01510b0e57ee960b79cb95def3b95d5a8414eb5e5ae3151faef07802bf03a0a9cefed763af344a5d488591880fc945a1685213faa5d83dc9ee8d40a9c76ef601a8b167ac533facb96ae93f4ac4ada9a572430213ed2e338fe405697e12e5c3476a62fa4ca00ae24e2257aa2648fdf88178e574403705bd59e6cb3939548f7769028bb077cdce53513f57a9fc4826db4126cbda3f0ce950adda72675849f297217ef31a4a677772cac02f8419cf36bc802abc64c8bf2bab
#
# (C) Tenable, Inc.
#

include('compat.inc');

if (description)
{
  script_id(14272);
  script_version("1.111");
  script_set_attribute(attribute:"plugin_modification_date", value:"2025/10/29");

  script_name(english:"Netstat Portscanner (SSH)");

  script_set_attribute(attribute:'synopsis', value:
"Remote open ports can be enumerated via SSH.");
  script_set_attribute(attribute:'description', value:
"Nessus was able to run 'netstat' on the remote host to enumerate the
open ports. If 'netstat' is not available, the plugin will attempt to use 'ss'.

See the section 'plugins options' about configuring this plugin.

Note: This plugin runs on Windows using netstat.exe if the target is localhost (scanner scanning itself) or a Windows host authenticated via SSH with the ability to run netstat.exe.");
  script_set_attribute(attribute:"see_also", value:"https://en.wikipedia.org/wiki/Netstat");
  script_set_attribute(attribute:"solution", value:"n/a");
  script_set_attribute(attribute:"risk_factor", value:"None");

  script_set_attribute(attribute:"plugin_publication_date", value:"2004/08/15");

  script_set_attribute(attribute:"plugin_type", value:"local");
  script_set_attribute(attribute:"agent", value:"unix");
  script_end_attributes();

  script_category(ACT_SCANNER);
  script_family(english:"Port scanners");

  script_copyright(english:"This script is Copyright (C) 2004-2025 and is owned by Tenable, Inc. or an Affiliate thereof.");

  script_dependencies("ping_host.nasl", "ssh_settings.nasl", "portscanners_settings.nasl", "ssh_rate_limiting.nasl", 'nessus_product_setup.nasl');
  script_exclude_keys("Host/OS/ratelimited_sonicwall", "Host/OS/ratelimited_junos", "Host/OS/ratelimited_omniswitch");
  script_timeout(600);
  exit(0);
}

include("ports.inc");
include("lcx.inc");
include("agent.inc");
include("ssh_lib.inc");
include("ssh_func.inc");
include("netstat.inc");

function run_cmd_by_sshlib(cmd)
{
  local_var session, channel, login_res, escl_method, escl_extra;

  var buf = NULL;
  session = new("sshlib::session");
  login_res = sshlib::try_ssh_kb_settings_login(session:session, accept_none_auth:TRUE);
  if(!login_res)
  {
    session.close_connection();

    # If it failed, remove the failure so that plugins down the chain can verify after
    # service detection.
    rm_kb_item(name:sshlib::SSH_LIB_KB_PREFIX + "try_ssh_kb_settings_login_failed");
    return NULL;
  }

  session.set_recv_timeout(60);
  escl_method = get_kb_item(sshlib::SSH_LIB_KB_PREFIX + session.get_kb_connection_id() + "/escalation_type");
  if(!escl_method || "Nothing" >< escl_method)
  {
    buf = session.run_exec_command(command:cmd, cmd_timeout_min:120);
    if(empty_or_null(buf))
    {
      channel = session.open_shell(shell_handler:new("sshlib::sh_shell_handler"));
      if(!isnull(channel))
        buf = session.run_shell_command(channel:channel, command:cmd);
    }
  }
  else
  {
    channel = session.open_shell(shell_handler:new("sshlib::sh_shell_handler"));
    if(!isnull(channel))
    {
      escl_extra = sshlib::get_kb_args(kb_prefix:("Secret/" + sshlib::SSH_LIB_KB_PREFIX + session.get_kb_connection_id() + "/escalation_extra"));
      channel.shell_handler.set_priv_escalation(type:escl_method, extra:escl_extra);
      buf = session.run_shell_command(channel:channel, command:cmd, force_priv_escl:TRUE);
    }
    if(empty_or_null(buf))
    {
      buf = session.run_exec_command(command:cmd, cmd_timeout_min:120);
    }
    if(empty_or_null(buf))
    {
      channel.shell_handler.unset_priv_escalation();
      if(!isnull(channel))
        buf = session.run_shell_command(channel:channel, command:cmd);
    }
  }

  session.close_connection();
  return buf;
}

var netstat_ssh = get_preference('local_portscan.netstat_ssh');

if (netstat_ssh == 'no')
  exit(0, 'SSH Netstat option is disabled, and must be enabled for this plugin to run.');

if(isnull(get_kb_item("/tmp_start_time")))
  replace_kb_item(name: "/tmp/start_time", value: unixtime());

if ( get_kb_item("PortscannersSettings/run_only_if_needed") &&
     get_kb_item("Host/full_scan") )
  exit(0, "The remote host has already been port-scanned.");

if (get_kb_item("Host/OS/ratelimited_sonicwall") ||
    get_kb_item("Host/OS/ratelimited_junos") ||
    get_kb_item("Host/OS/ratelimited_omniswitch"))
  exit(1,"This plugin does not run against rate limited devices.");

# If plugin debugging is enabled, enable packet logging
if(get_kb_item("global_settings/enable_plugin_debugging"))
  SSH_LOG_PACKETS = TRUE;

var buf = "";
var ssh_banner = "";
var n_tcp = 0;
var n_udp = 0;

var port22, timeout, cmd, agent_ip, ret, i, msg, res;

# On the local machine, just run the command
if (lcx::check_localhost())
{
  buf = netstat::run_localhost_netstat();
  if ( buf )
  {
    set_kb_item(name:"Host/netstat", value:buf);
    set_kb_item(name:"Host/netstat/method", value:"local");
    if (agent())
    {
      agent_ip = agent_get_ip();
      if(!isnull(agent_ip))
        report_xml_tag(tag:"host-ip", value:agent_ip);
    }
  }
  else exit(1, "Failed to run the command 'netstat -a -n' on localhost.");
}
else if ( get_kb_item("Secret/SSH/login") )
{
  port22 = sshlib::kb_ssh_transport();
  if ( port22 && get_port_state(port22) )
  {
    res = info_connect();
    if ( res )
    {
      ssh_banner = ssh_exchange_identification();

      if (info_t == INFO_SSH)
        ssh_close_connection();

      if (
         "-cisco-" >< tolower(ssh_banner) ||
         "-cisco_" >< tolower(ssh_banner)
      ) exit(0, 'The netstat portscanner doesn\'t run against Cisco devices.');
    }
  }

  # Need to set try none for Sonicwall
  set_kb_item(name:"/tmp/ssh/try_none", value:TRUE);
  timeout = get_ssh_read_timeout();
  if (timeout <= 5) set_ssh_read_timeout(10);

  if ("force10networks.com" >< ssh_banner) sleep(1);

  ret = info_connect();

  # nb: Sonicwall needs a delay between the initial banner grab
  #     and  calling 'ssh_open_connection()'.
  if (
    !ret &&
    "please try again" >< get_ssh_error()
  )
  {
    for (i=0; i<5 && !ret; i++)
    {
      # We need to unset login failure if we are going to try again
      if(get_kb_item("SSH/login/failed")) rm_kb_item(name:"SSH/login/failed");
      sleep(i*2);
      ret = info_connect();
    }
  }

  cmd = "cmd /c netstat -an";
  if (ret)
  {
    buf = info_send_cmd(cmd:cmd, nosudo:TRUE, timeout:60);
  }
  else
  {
    if (info_t == INFO_SSH)
      ssh_close_connection();
  }

  if (get_kb_item(sshlib::SSH_LIB_KB_PREFIX + "try_ssh_kb_settings_login_failed") &&
      get_kb_item("SSH/login/failed"))
  {
    exit(1, "Failed to open an SSH connection.");
  }

  if('Command Line Interface is starting up, please wait' >< buf)
  {
    if (info_t == INFO_SSH)
      ssh_close_connection();
    exit(0, 'The netstat portscanner doesn\'t run against Cisco devices.');
  }

  if ("LISTENING" >!< buf && "0.0.0.0:0" >!< buf && "*.*" >!< buf)
  {
    # Brocade
    if (
      !buf &&
      'rbash: sh: command not found' >< ssh_cmd_error()
    )
    {
      if(!ret)
      {
        res = info_connect();
        if (!res) exit(1, "Failed to reopen an SSH connection.");
      }

      cmd = "netstat -an";
      buf = info_send_cmd(cmd:cmd, timeout:60);
    }
    # NetApp Data ONTAP
    else if (
      !buf &&
      "cmd not found.  Type '?' for a list of commands" >< ssh_cmd_error()
    )
    {
      if (info_t == INFO_SSH)
        ssh_close_connection();
      res = info_connect();
      if (!res) exit(1, "Failed to reopen an SSH connection.");
      sleep(1);

      cmd = "netstat -an";
      buf = info_send_cmd(cmd:cmd, nosudo:TRUE, timeout:60);
    }
    #NetApp Data ONTAP clustered
    else if (
      !buf &&
      "Error: Ambiguous command" >< ssh_cmd_error() ||
      "is not a recognized command" >< ssh_cmd_error()
    )
    {
      if (info_t == INFO_SSH)
        ssh_close_connection();
      sock_g = info_connect();
      if (!sock_g) exit(1, "Failed to reopen an SSH connection.");
      sleep(1);

      cmd = "system node run -node local -command netstat -an";
      buf = info_send_cmd(cmd:cmd, nosudo:TRUE, timeout:60);
      if ( !buf && "is not a recognized command" >< ssh_cmd_error() )
      cmd = "node run -node local -command netstat -an";
      buf = info_send_cmd(cmd:cmd, nosudo:TRUE, timeout:60);
      if ( !buf && "is not a recognized command" >< ssh_cmd_error() )
      cmd = "run -node local -command netstat -an";
      buf = info_send_cmd(cmd:cmd, nosudo:TRUE, timeout:60);
    }

    # ScreenOS
    else if (
      !buf &&
      "-NetScreen" >< ssh_banner
    )
    {
      if (info_t == INFO_SSH)
        ssh_close_connection();
      res = info_connect();
      if (!res) exit(1, "Failed to reopen an SSH connection.");
      sleep(1);

      cmd = "get socket";
      buf = info_send_cmd(cmd:cmd, nosudo:TRUE, timeout:60);
    }
    else
    {
      if (info_t == INFO_SSH)
        ssh_close_connection();

      cmd = 'netstat -a -n';
      /**
      - sshlib
      -- If there are no escalation credentials
      --- Try exec
      --- If that doesn't work, try sh shell handler
      -- If there are escalation credentials
      --- Try sh shell handler
      --- If that doesn't work
      ---- Try exec without credentials
      ---- If that doesn't work, try sh shell handler without credentials
      **/

      buf = run_cmd_by_sshlib(cmd: cmd);
      if ('command not found' >< buf || 'No such file or directory' >< buf || ' not found, but can be installed with:' >< buf)
      {
        dbg::detailed_log(
          lvl:3,
          src:SCRIPT_NAME,
          msg:"netstat not found, trying ss.",
          msg_details: {
            'netstat output' : {lvl:3, value:buf}
          }
        );
        # if netstat fails, try ss as a separate command
        // try ss
        # centos: /usr/bin/ss -a -n
        # ubuntu: /usr/sbin/ss -a -n
        # debian: /bin/ss -a -n
        cmd = '(/usr/sbin/ss -n -a 2>/dev/null && echo 1)|| (/bin/ss -n -a 2>/dev/null && echo 2)|| (/usr/bin/ss -n -a 2>/dev/null && echo 3)';

        buf = run_cmd_by_sshlib(cmd: cmd);
      }
    }

    if (
      !buf ||
      "Cmd exec error" >< buf ||
      "Cmd parse error" >< buf ||
      "command parse error before" >< buf ||
      "(Press 'a' to accept):" >< buf ||
      "Syntax error while parsing " >< buf ||
      ' not found, but can be installed with:' >< buf
    )
    {
      if (info_t == INFO_SSH)
        ssh_close_connection();
      exit(1, "The 'netstat' command failed to be executed.");
    }
  }
  if (info_t == INFO_SSH)
    ssh_close_connection();
  set_kb_item(name:"Host/netstat", value:buf);
  set_kb_item(name:"Host/netstat/method", value:"ssh");
  if ('/ss' >< cmd)
    set_kb_item(name:'Host/netstat/cmd', value:'ss');
}
else
{
  exit(0, "No credentials are available to login to the host.");
}

var ip = get_host_ip();
var lines = split(buf);
var scanned = 0;

var check = get_kb_item("PortscannersSettings/probe_TCP_ports");

var unscanned_closed, tested_tcp_ports, tested_udp_ports;

if ("yes" >< get_preference("unscanned_closed"))
  unscanned_closed = TRUE;
else
  unscanned_closed = FALSE;

if (unscanned_closed)
{
  tested_tcp_ports = get_tested_ports(proto: 'tcp');
  tested_udp_ports = get_tested_ports(proto: 'udp');
}
else
{
  tested_tcp_ports = make_list();
  tested_udp_ports = make_list();
}

var discovered_tcp_ports = make_array();
var discovered_udp_ports = make_array();

var v, last_seen_proto, proto, state, local_ip, local_port;
var addr, port, checktcp, addr_parts, soc;
var parsed_lines;

foreach var line (lines)
{
  line = chomp(line);
  # Windows - 2024 devnote: this plugin does not seem to run on windows
  v = netstat::process_netstat_win_line_open_ports(line:line);

  # Unix
  if (isnull(v))
    v = netstat::process_netstat_nix_line_open_ports(line:line);

  if (isnull(v))
    v = netstat::process_ss_nix_line_open_ports(line:line);

  # Solaris 9 / NetApp
  if (isnull(v))
  {
    if (last_seen_proto)
    {
      if (last_seen_proto == 'udp')
      {
        v = pregmatch(pattern: '^[ \t]*(?:::ffff[:.])?(\\*|[0-9.]+)\\.([0-9]+)[ \t]+Idle', string: line);
        if (isnull(v)) v = pregmatch(pattern: '^[ \t]*(\\*|[0-9.]+)\\.([0-9]+)[ \t]+(\\*\\.\\*|[0-9.]+)[ \t]+[0-9]+[ \t]+[0-9]+$', string: line);
      }
      else
        v = pregmatch(pattern: '^[ \t]*(?:::ffff[:.])?(\\*|[0-9.]+)\\.([0-9]+)[ \t]+\\*\\.\\*[ \t]+.*(Idle|LISTEN)', string: line);

      if (! isnull(v))
      {
        # "Fix" array
        v[3] = v[2]; v[2] = v[1]; v[1] = last_seen_proto;
      }
    }
    if (isnull(v))
    {
      v = pregmatch(pattern: '^(TCP|UDP)(: +IPv4)?[ \t\r\n]*$', string: line);
      if (isnull(v)) v = pregmatch(pattern: '^Active (TCP|UDP) (connections|sockets) \\(including servers\\)[ \t\r\n]*$', string: line);
      if (!isnull(v))
      {
        last_seen_proto = tolower(v[1]);
        v = NULL;
      }
    }
  }

  # ScreenOS
  # Socket  Type   State      Remote IP         Port    Local IP         Port
  #    1  tcp4/6  listen     ::                   0    ::                443
  #    2  tcp4/6  listen     ::                   0    ::                 23
  #    3  tcp4/6  listen     ::                   0    ::                 22
  #   67  udp4/6  open       ::                   0    ::                500
  if (isnull(v))
  {
    v = pregmatch(pattern:'^[ \t]*[0-9]+[ \t]+(tcp|udp)4/6[ \t]+(listen|open)[ \t]+([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+|::)[ \t]+[0-9]+[ \t]+([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+|::)[ \t]+([0-9]+)[ \t]*', string:line, icase:TRUE);
    if (!isnull(v))
    {
      proto = v[1];
      state = v[2];
      local_ip = v[4];
      local_port = v[5];

      # "Fix" array
      v[1] = proto;
      v[2] = local_ip;
      v[3] = local_port;
    }
  }

  if (!isnull(v))
  {
    proto = tolower(v[1]);
    addr = v[2];
    port = int(v[3]);
    checktcp = (check && proto == "tcp");

    if (port < 1 || port > 65535)
    {
      msg = strcat('netstat_portscan(', ip, '): invalid port number ', port, '\n');
      dbg::detailed_log(lvl:3, src:SCRIPT_NAME, msg:msg);
    }

    # Avoid reporting port allowances only for localhost
    addr_parts = split(addr, sep:".");
    if (addr_parts[0] == "127." || addr == "::1")
    {
      if (addr != ip)
      {
        msg = strcat('netstat_portscan(', ip, '): skipping localhost-specific line: ', line, '\n');
        dbg::detailed_log(lvl:3, src:SCRIPT_NAME, msg:msg);
        continue;
      }
      else if (addr == ip && "127.0.0.1" >< addr)
      {
        # this check corrects a problem where agent ip matches 127.0.0.1 localhost port offering
        msg = strcat('netstat_portscan(', ip, '): skipping IPv4 localhost-specific line: ', line, '\n');
        dbg::detailed_log(lvl:3, src:SCRIPT_NAME, msg:msg);
        continue;
      }
    }

    if (unscanned_closed)
    {
      if (
        (proto == "tcp" && ! tested_tcp_ports[port]) ||
        (proto == "udp" && ! tested_udp_ports[port])
      ) continue;
    }

    if (
      (proto == "tcp" && discovered_tcp_ports[port]) ||
      (proto == "udp" && discovered_udp_ports[port])
    ) continue;

    if (checktcp)
    {
      soc = open_sock_tcp(port);
      if (soc)
      { 
        netstat::add_port(proto: proto, port: port);
        close(soc);
      }
    }
    else
    {
      netstat::add_port(proto: proto, port: port);
    }

    if (proto == "tcp")
    {
      n_tcp ++;
      discovered_tcp_ports[port]++;
    }
    else if (proto == "udp")
    {
      n_udp ++;
      discovered_udp_ports[port]++;
    }
    scanned ++;
    parsed_lines = strcat(parsed_lines, line, '\r\n');
    msg = strcat('netstat_portscan(', ip, '): found valid line: ', line, '\n');
    dbg::detailed_log(lvl:3, src:SCRIPT_NAME, msg:msg);
  }

}


var check_debug_level = get_kb_item("global_settings/debug_level");
if ( !empty_or_null(parsed_lines) &&
     !empty_or_null(check_debug_level) &&
     int(check_debug_level) > 2 )
{
  replace_kb_item(name: "Host/netstat_debug_parsed_lines", value: parsed_lines);
}

if (scanned)
{
  set_kb_item(name: "Host/scanned", value: TRUE);
  set_kb_item(name: "Host/udp_scanned", value: TRUE);
  set_kb_item(name: "Host/full_scan", value: TRUE);

  set_kb_item(name:"NetstatScanner/TCP/OpenPortsNb", value: n_tcp);
  set_kb_item(name:"NetstatScanner/UDP/OpenPortsNb", value: n_udp);

  set_kb_item(name: "Host/TCP/scanned", value: TRUE);
  set_kb_item(name: "Host/UDP/scanned", value: TRUE);

  set_kb_item(name: "Host/TCP/full_scan", value: TRUE);
  set_kb_item(name: "Host/UDP/full_scan", value: TRUE);

  set_kb_item(name: 'Host/scanners/netstat', value: TRUE);
}

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

29 Oct 2025 00:00Current
5.6Medium risk
Vulners AI Score5.6
955