#TRUSTED 8c1e58c49d58684cda2cbeb1051edc1db3cff6dbffe473a8d82b9412177b070fe76a4ff0c72e0e14cde834a4d48d9b7854f957face60b5fe220f34c07b4c0372375a9fde9959d32a7f5dd3cf77591b120e27feb73310cc83f8e64359b4c3dbb605e542fe2660cf6af2dfce2cf258d52e0e3aabcdd0a1a2851afb7444a443f73bc5dbb9bbcefb9f7a84d08d08ec2eb1d145e00fd210f756d7396aa039dfdad0e4e25e27622aea0462243f7a52a1510d2eddafc9acaf45bc7e1bebf2ba2773127bcfd4f994c985848ed2b4383f6ce8a5166b8a6e719809cd8694be8a6fc849800cc0ac4071b34bcf5417e0f042d59eb5e3be6ac9b1402d2e63974521a35ef96c2cc02b46a7e40ddefd04fa7fd34ded364e24909c0ab2d9d4a00782e3644838d91ddea5d0be85c493686a25217f9373b207b5d7360eff9e594863e1347731338b2f4255affcd3fee1a1b1dda70c5895bae425c5aa9d8a0dcd4fc4bd3ac87224e7caeaa332e8f55fbed7c72ec8f4014af26e330a37d1e88d49770c05d8b8783516b1c2b085c78ab8615ab0d122cf12b11baf55188c04eedd0829ad081c7411fedec2fa378cd4026e326e3efe3ca4abed1d3c0126607533236a4d2574211de964f646ecd17258d922165643308b88b1159f067864edae9b06bf4370f2485fda3b24588c512f0d3f0e2a9926d2b50f5bec4a07326931a98f2b630e7d99f85ebd7d8ed8
#TRUST-RSA-SHA256 4624575b351b9de3623c302d33eca4aa729538c275d6ef7aac834cdff19944550a5c95f050aafa99539078eff6b35923a7e1133da781ee2fdb6afbe9082ce894dd573d4eccfa6cf2019232d086a647db9ececb8906def2fc06d2d0f85280b9b8fca803dd76dc3644c8a7719eb15f5aa621942493110f9dd6ba4caa7b8f584a9d732f017245e8876c193b2964fed4b4a60be6dcb6501f371fd8f29b2fc57d58595a4b026c71ba53d3467419c63ccc1f1f17fc31c495897fb5928b2d9f9be9a290df2792236f1663893a8bdae60c576ae3933b5744671f265e4c00c63a9b55f0c586606197f59627c487aea2679df7d7230a40c1c123e7de71b8cdd2962dbe44ed3e8f02728d4a660e09414350d1178cadcdec6591bd8fe47df9629716b866f591a43d0630f55473e34bcbecc43311ee94a2a98a7d896d5569e6d5d05954072adf4000060fe6517d9ccebe91d57343937c1ed57321cc3afb7453f4a0d4295336dcdb9c6016f86df96e06018cf3985c52daff8fef5ca51d8d412625aba99fa43ae82777d3478d22689c88dd7e0524b51a66b5c33676679cfb0ab9ce93761869f6de8fd205b78a321425e76856a238583f5a52d5f6bbb4b0060ba0f0d12a636c481e2694b604ffc68d279c5b0c08ef32385082f4d2185fd875f36c097e6bfdf20a9c88c96585732c467861cecc93874e649e9315c9bf5c760b79bf19963ba892bfb8
#
# (C) Tenable Network Security, Inc.
#
include("compat.inc");
if (description)
{
script_id(25221);
script_version("1.30");
script_set_attribute(attribute:"plugin_modification_date", value:"2026/05/21");
script_name(english:"Remote listeners enumeration (Linux / AIX)");
script_summary(english:"Finds the process listening on each port with netstat.");
script_set_attribute(attribute:"synopsis", value:
"Using the supplied credentials, it was possible to identify the
process listening on the remote port.");
script_set_attribute(attribute:"description", value:
"By logging into the remote host with the supplied credentials, Nessus
was able to obtain the name of the process listening on the remote
port.
Note that the method used by this plugin only works for hosts running
Linux or AIX.");
script_set_attribute(attribute:"solution", value:"n/a");
script_set_attribute(attribute:"risk_factor", value:"None");
script_set_attribute(attribute:"plugin_publication_date", value:"2007/05/16");
script_set_attribute(attribute:"plugin_type", value:"local");
script_set_attribute(attribute:"agent", value:"unix");
script_end_attributes();
script_category(ACT_GATHER_INFO);
script_family(english:"Service detection");
script_copyright(english:"This script is Copyright (C) 2007-2026 and is owned by Tenable, Inc. or an Affiliate thereof.");
script_dependencies("ssh_settings.nasl", "ssh_get_info.nasl");
script_require_ports("Services/ssh", 22, "nessus/product/agent");
script_require_keys("Host/uname");
exit(0);
}
include("compat_shared.inc");
include("ssh_func.inc");
include("telnet_func.inc");
include("hostlevel_funcs.inc");
include("local_detection_nix.inc");
include("process_on_port_parser.inc");
include("command_builder.inc");
var cmdlines = make_array();
var cmdlines_enc = make_array();
var localaddrs = make_array();
var exes = make_array();
var pids = make_array();
var prelinked = make_array();
var md5s = make_array();
##
# Uses readlink, md5sum and cat to get executable, md5 and commandline of the target process
# @param pid PID of the target process
# @return An array containing {executable:string, commandline:string, b64_commandline:string}
# @remark This function can also write to md5s array if it finds a new broken executable link
##
function get_process_data(pid)
{
if (pid <= 0) return NULL;
var results = {};
var exe = ldnix::run_cmd_template_wrapper(template:'LC_ALL=C readlink \'/proc/$1$/exe\' 2>/dev/null', args:[pid]);
if (strlen(exe) > 0) exe = chomp(exe);
results['executable'] = exe;
# check md5sum of process image for further verification if needed (used in daemons_with_broken_links.nasl)
if(isnull(md5s[pid]) && preg(pattern:"^(.+) \(deleted\)$", string:exe))
{
var exe_md5sum = ldnix::run_cmd_template_wrapper(template:'LC_ALL=C md5sum \'/proc/$1$/exe\' 2>/dev/null', args:[pid]);
var item = pregmatch(pattern:'^([a-zA-Z0-9]{32}) ', string: exe_md5sum);
if(!isnull(item)) md5s[pid] = item[1];
}
var cmdline_pure = ldnix::run_cmd_template_wrapper(template:'LC_ALL=C cat \'/proc/$1$/cmdline\' 2>/dev/null', args:[pid]);
var cmdline = join(split(cmdline_pure, sep:'\x00', keep:FALSE), sep:' ');
var cmdline_enc = base64(str:cmdline_pure);
results['commandline'] = cmdline;
results['b64_commandline'] = cmdline_enc;
return results;
}
##
# Parses nestat output from a qualifying host
# @param buf netstat -anp or ss -anp output from a host
# @param @line_parser A reference to function that parses either an ss or netstat output line
# @remark This function will fill cmdlines, localaddrs, exes, pids, prelinked and md5s global arrays
##
function fill_socket_data(buf, line_parser)
{
var results, port, pid, socket, exe, process_info, cmdline, cmdline_enc;
var lines = split(buf, keep:FALSE);
foreach var line (lines)
{
results = line_parser(line:line);
if (isnull(results)) continue;
port = results['port'];
if (port < 0 || port > 65535) continue;
proto = results['proto'];
if (proto != "tcp" && proto != "udp") continue;
socket = strcat(proto, '/', port);
if (exes[socket]) continue;
pid = results['pid'];
process_info = get_process_data(pid:pid);
if(isnull(process_info) || empty_or_null(process_info['executable'])) exe = results['executable'];
else exe = process_info['executable'];
if (strlen(exe) == 0) continue;
localaddrs[socket] = results['address'];
exes[socket] = exe;
if (pid > 0) pids[socket] = pid;
cmdline = process_info['commandline'];
if (strlen(cmdline) > 0) cmdlines[socket] = cmdline;
cmdline_enc = process_info['b64_commandline'];
if (strlen(cmdline_enc) > 0) cmdlines_enc[socket] = cmdline_enc;
}
}
var uname = get_kb_item_or_exit("Host/uname");
if (
'Linux' >!< uname &&
'AIX' >!< uname
) audit(AUDIT_HOST_NOT, "Linux / AIX");
enable_ssh_wrappers();
info_connect();
# nb: On Solaris, you can do this with a command like:
#
# pfexec pfiles `ls /proc` 2>/dev/null | egrep '^[0-9]|port:'
#
# The problem is that pfiles, as its man page warns, can cause a process
# to stop while its being inspected by the tool, and that is to be
# avoided in a production environment!
var buf, item, entry, netstat_cmd, ss_cmd, netstat_buf, ss_buf, errmsg;
if ("Linux" >< uname)
{
buf = info_send_cmd(cmd:"prelink -p 2>/dev/null");
# sanity check
if('objects found in prelink cache' >< buf)
{
foreach entry (split(buf, sep:'\n', keep:FALSE))
{
# only interested in binaries, the code below
# will filter out the libraries
if(':' >< entry && entry !~ "\[0x[a-zA-Z0-9]+\]")
{
item = pregmatch(pattern:"^([^:]+):", string:entry);
if(!isnull(item)) prelinked[item[1]] = TRUE;
}
}
}
netstat_cmd = "netstat -anp";
ss_cmd = 'ss -anp';
netstat_buf = ldnix::run_cmd_template_wrapper(template:"LC_ALL=C "+netstat_cmd);
if (strlen(netstat_buf) == 0)
{
errmsg = ssh_cmd_error();
if (errmsg) errmsg ='for the following reason :\n\n' + errmsg + '\n\n';
else errmsg = 'for an unknown reason. ';
errmsg = "Failed to run '" + netstat_cmd + "' " + errmsg;
dbg::detailed_log(lvl:1, msg:errmsg);
}
if(!empty_or_null(netstat_buf))
{
set_kb_item(name:"Host/netstat_anp", value:netstat_buf);
fill_socket_data(buf:netstat_buf, line_parser:@process_on_port_parser::parse_netstat_output_line);
}
else
{
ss_buf = ldnix::run_cmd_template_wrapper(template:"LC_ALL=C "+ss_cmd);
if(strlen(ss_buf) == 0)
{
errmsg = ssh_cmd_error();
if (errmsg) errmsg ='for the following reason :\n\n' + errmsg + '\n\n';
else errmsg = 'for an unknown reason. ';
errmsg = "Failed to run '" + ss_cmd + "' " + errmsg;
dbg::detailed_log(lvl:1, msg:errmsg);
if (info_t == INFO_SSH) ssh_close_connection();
exit(1, "Both nestat and ss failed, see debug log for errors");
}
}
if(!empty_or_null(ss_buf))
{
set_kb_item(name:"Host/ss_anp", value:ss_buf);
fill_socket_data(buf:ss_buf, line_parser:@process_on_port_parser::parse_ss_output_line);
}
}
# Suggested by Bernhard Thaler
#
# http://www-01.ibm.com/support/docview.wss?rs=71&uid=swg21264632
else if ("AIX" >< uname)
{
var line, proto, port, v, v2, k, pcbaddr, pid, exe, cmd, cmdline;
netstat_cmd = "netstat -Aan";
buf = info_send_cmd(cmd:"LC_ALL=C "+netstat_cmd);
if (strlen(buf) == 0)
{
errmsg = ssh_cmd_error();
if (errmsg) errmsg ='for the following reason :\n\n' + errmsg + '\n\n';
else errmsg = 'for an unknown reason. ';
errmsg = "Failed to run '" + netstat_cmd + "' " + errmsg;
if (info_t == INFO_SSH) ssh_close_connection();
exit(1, errmsg);
}
set_kb_item(name:"Host/netstat_Aan", value:buf);
foreach line (split(buf, keep:FALSE))
{
v = pregmatch(string:line, pattern:'^(f[a-f0-9]{15})[ \t]+((tcp|udp)[46]?)[ \t]+[0-9]+[ \t]+[0-9]+[ \t]+(\\*|[0-9]+\\.[0-9.]+\\.[0-9]+\\.[0-9]+)\\.([0-9]+)[ \t]+(\\*|[0-9]+\\.[0-9.]+\\.[0-9]+\\.[0-9]+)\\.[0-9*]+([ \t]+LISTEN)?$');
if (isnull(v)) continue;
port = int(v[5]);
if (port < 0 || port > 65535) continue;
proto = tolower(v[3]);
if (proto != "tcp" && proto != "udp") continue;
pcbaddr = v[1];
exe = cmdline = '';
cmd = "rmsock " + pcbaddr + " ";
if (proto == "tcp") cmd += "tcpcb";
else cmd += "inpcb";
buf = info_send_cmd(cmd:"LC_ALL=C "+cmd + ' 2>/dev/null');
if (strlen(buf) > 0)
{
buf = chomp(buf);
v2 = pregmatch(string:buf, pattern:"The socket [^ ]+ is being held by proccess ([0-9]+)[ \t]+\(([^)]+)\)\.");
if (!isnull(v2))
{
pid = int(v2[1]);
exe = v2[2];
cmd = "proctree " + pid;
buf = info_send_cmd(cmd:"LC_ALL=C "+cmd+" 2>/dev/null");
if (strlen(buf) > 0)
{
foreach line (split(buf, keep:FALSE))
{
v2 = pregmatch(pattern:'^[ \t]*'+pid+'[ \t]+([^ \t].+)$', string:line);
if (!isnull(v2)) cmdline = v2[1];
}
}
}
else
{
v2 = pregmatch(string:buf, pattern:"The socket [^ ]+ is being held by Kernel/Kernel Extension\.");
if (!isnull(v2))
{
pid = "n/a";
exe = "[kernel/kernel extension]";
}
}
}
if (strlen(exe) == 0) continue;
k = strcat(proto, '/', port);
if (exes[k]) continue;
localaddrs[k] = v[4];
exes[k] = exe;
if (pid > 0 || pid == "n/a") pids[k] = pid;
if (strlen(cmdline) > 0) cmdlines[k] = cmdline;
}
}
if (max_index(keys(exes)) == 0)
{
if (info_t == INFO_SSH) ssh_close_connection();
exit(0, "The host does not have any listening services.");
}
var found, ip, sanchk, cmdline_enc, localaddr, match, lead_slash, report;
found = 0;
ip = get_host_ip();
foreach k (sort(keys(exes)))
{
v = pregmatch(pattern:"^(.+)/([0-9]+)$", string:k);
if (isnull(v))
{
if (info_t == INFO_SSH) ssh_close_connection();
exit(1, "Failed to parse protocol / port info for '"+k+"'.");
}
proto = v[1];
port = v[2];
exe = exes[k];
localaddr = localaddrs[k];
cmdline = cmdlines[k];
cmdline_enc = cmdlines_enc[k];
if (strlen(cmdline) == 0) cmdline = "n/a";
if (strlen(cmdline_enc) == 0) cmdline_enc = "n/a";
pid = pids[k];
# Check exe for unexpected chars
if (!command_builder::validate_no_injection_denylist(exe))
{
dbg::detailed_log(lvl:1, msg:'Continue due to injection attempt in listener',
msg_details:{
'listener':{'lvl':1, 'value':exe}
}
);
continue;
}
set_kb_item(name:'Host/Daemons/'+localaddr+'/'+proto+'/'+port, value:exe);
if (
(
TARGET_IS_IPV6 &&
(localaddr == "::" || localaddr == ip)
) ||
(
!TARGET_IS_IPV6 &&
(localaddr == '0.0.0.0' || localaddr == ip || localaddr == "::" || localaddr == "*")
)
)
{
set_kb_item(name: 'Host/Listeners/'+proto+'/'+port, value:exe);
set_kb_item(name: 'Host/Listeners/'+proto+'/'+port+'/cmdline', value:cmdline_enc);
set_kb_item(name: 'Host/Listeners/'+proto+'/'+port+'/pid', value:pid);
found++;
match = pregmatch(pattern:"^(.+) \(deleted\)$", string:exe);
if (!isnull(match)) exe = match[1];
if (exe[0] == '/') lead_slash = '';
else lead_slash = '/';
if(!isnull(md5s[pid]))
replace_kb_item(name: 'Host/DaemonMD5s' + lead_slash + exe, value:md5s[pid]);
# this is here so we only report on listening pre-linked daemons
if(prelinked[exe])
{
# whitelist
if(exe =~ "^[0-9A-Za-z_\-./]+$")
buf = info_send_cmd(cmd:"prelink -y " + exe + " | md5sum");
item = pregmatch(pattern:'^([a-zA-Z0-9]{32}) ', string: buf);
if(!isnull(item))
replace_kb_item(name: 'Host/PrelinkedDaemons' + lead_slash + exe, value:item[1]);
else
replace_kb_item(name: 'Host/PrelinkedDaemons' + lead_slash + exe, value:'md5_unknown');
}
report = '\n Process ID : ' + pid +
'\n Executable : ' + exe;
if (strlen(cmdline) > 0) report += '\n Command line : ' + cmdline;
report += '\n';
if (COMMAND_LINE) report = '\n Port : ' + port + ' (' + proto + ')' + report;
if (report_verbosity > 0) security_note(port:port, proto:proto, extra:report);
else security_note(port:port, proto:proto);
}
}
if (info_t == INFO_SSH) ssh_close_connection();
if (found) set_kb_item(name:"Host/Listeners/Check", value:netstat_cmd);
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