#TRUSTED 388d7ff8d1743fb6a9130cf47705c45da1bec5909f07c62b2fe8ddaa23cd18e14313d3353125b7222a0fca2d80a8a7467bada8e1f859c439f0cc05406b87221b27bfddd628311876609db03700b545c4c215e5dfd5a86905a82ea7e98c872e44d8024634653d489ce3ca639d575c0feb69399ed2e98e849bbf8fc82e826084c0f0b4845682542367a74bc258212045b03769ff96c71103e13e34de61807b7628e844111993a829c3da3e841541669e24a85439cf9b2de9226f93f7b28b2192651fe63f82834a6c2495ce1c6dfcec34bad87165d14da7a90a1a1394f341039d390b91246f9bd03dedb39a540649eb10e49e39c7bb51131ad4364dfb6ad33907a3d1b55e58881beb55920b488b73f812e0b4723bd19537096bfbd18fc9f2a7c7c7ce1b6aeee674399e00c5f54cae20c54f3fe3eae6263cabb6fc86acfb0c481796cc1f7451d1170b3cfff0211ef10ec6fa8d2b864afee97f5f84cf18315db3b7c18b34ed7a3b7e92e9d754c8e27845786d7ff78f1b5a7b15a510c9c9234ad01e3e4f6103308f281b2df1fa2d08e351aa19f2ec42089f029100c1aca27ba0e7ff8973eb930fb26b9c93ca8de90ca07b4432eef7c3982ed8ba904afc33c32e947a875d5afa82251b4bd066b0a137d991be9ea7682524bef059f01f0d2eebf627b601406724e0265f8ca162cf5d4690ca9365d347bb623e8bf1ea735098fb249476ff
#TRUST-RSA-SHA256 175a683ded76f3345a25dd55dcf740dcd6eb5fd7b83ec959bf2f49f8c4884c93aabc23aae633d0394b209d2b7bf362ee34829ceef12407a5faac2cd0fb09897a57b1c4443f5e0cc631f4d3c52d2ea264f4462173cf779635d91e3ed2e8a951dcdb8bd7d4837755f03010a04667a873d71cce75b896a2f7bb05198ec4ab6f88acf8941f85bcfa5b516cd576eb3c68c083c510e80dc0bfbeec30282f83916f70dd4800c97edc4b20410eef322225a3e949cfaba47948c60bd940b5df503a2bfd3120339a5c88e2dc9f4ce268d3a5f6c2cf12429d3d8b56b100e12c2b982e218db0ad7dca845e077fecab8df71fe43a3b125e7aa5d314a6c47dff83cf8e644f1acb47cc76bc5b92fe80005e5f8c1ce4c5e91c86a3255b6726b7bb6d950370d7aabefc774bac138fd17e7bbf8c59e4674771d850914c0b78e8d08a30c8b509ed562cf4d4be484ee688d8fb8e0ecf8f080a4ad801f2650ac896c4e99b9842613bdc520bcfe2b0f8316bffa8ee73ec40e18507933920989ae4c897baf192310bf8dd4f6d84b58e2e4d9890ff7e624ee809fadaff7d7a3c6738642808bc80b7abe36c3c28a8f49ed867fd7366203b309256b6929f51346e93c2881cdbfa07b2367c326fcf5dbd3095764bf7fe3d5c54b54599cdaba551cebc020ebfc7e5dd6ca60c77c349b5796a70201679bbd468fdfc64bc534fc255b281612c281bb802c621e1b822
##
# (C) Tenable, Inc.
##
if (!defined_func("bn_random")) exit(0);
if (NASL_LEVEL < 3000) exit(0);
include("compat.inc");
if (description)
{
script_id(95928);
script_version("1.18");
script_set_attribute(attribute:"plugin_modification_date", value:"2026/06/15");
script_name(english:"Linux User List Enumeration");
script_set_attribute(attribute:"synopsis", value:
"Nessus was able to enumerate local users and groups on the remote Linux host.");
script_set_attribute(attribute:"description", value:
"Using the supplied credentials, Nessus was able to enumerate the local users and groups on the remote Linux host.");
script_set_attribute(attribute:"solution", value:"None");
script_set_attribute(attribute:"risk_factor", value:"None");
script_set_attribute(attribute:"plugin_publication_date", value:"2016/12/19");
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:"General");
script_copyright(english:"This script is Copyright (C) 2016-2026 and is owned by Tenable, Inc. or an Affiliate thereof.");
script_dependencies("ssh_get_info.nasl");
script_require_keys("Host/local_checks_enabled", "Host/uname");
exit(0);
}
include('local_detection_nix.inc');
include('linux_groups_object.inc');
include('linux_accounts_object.inc');
include('command_builder.inc');
##
# Calls id utility to get data about target user's uid and groups and stores it in passed arrays.
# @param [user:&array] A reference to an array representing user data created by get_domain_users_and_groups().
# @param [groups:&array] A reference to an array representing groups created by get_domain_users_and_groups().
# @return NULL - data is retrieved by filling user and groups arrays.
#
##
function gather_user_id_data(&user, &groups)
{
var cmd = "id $1$@$2$";
var results = ldnix::run_cmd_template_wrapper(template:cmd, args:[user.username, user.domain]);
if(empty_or_null(results))
{
dbg::detailed_log(lvl:1, src:FUNCTION_NAME, msg:'id call failed, exiting!');
return FALSE;
}
var user_match = pregmatch(pattern:"uid=(\d+)", string:results);
if(!empty_or_null(user_match))
{
user.uid = user_match[1];
}
var default_gid_match = pregmatch(pattern:"gid=(\d+)", string:results);
if(!empty_or_null(user_match))
{
user.gid = default_gid_match[1];
}
user.groups = [];
var groups_part = pregmatch(pattern:"groups=(.*)", string:results);
if(empty_or_null(groups_part))
{
dbg::detailed_log(lvl:1, src:FUNCTION_NAME, msg:strcat('No groups found for ', user.fullname, ' exiting!'));
return;
}
groups_part = groups_part[1];
var group_entries = split(groups_part, sep:',', keep:FALSE);
foreach(var group_entry in group_entries)
{
var group_match = pregmatch(pattern:"(\d+)\(([^)]+)\)", string:group_entry);
if(empty_or_null(group_match)) continue;
var gid = int(group_match[1]);
var group_name = group_match[2];
if(empty_or_null(groups[gid]))
{
groups[gid] = {name:group_name, users:[]};
}
append_element(var:groups[gid].users, value:user.fullname);
user.groups[group_name] = TRUE;
}
}
##
# Discovers domain groups and users by scanning /home directory and calling id utility.
# @return A pair (array) of arrays: {users, groups}.
##
function get_domain_users_and_groups()
{
var cmd = "ls -1 /home | grep -E '.+@.+\.[a-zA-Z]+'";
var results = ldnix::run_cmd_template_wrapper(template:cmd);
if(empty_or_null(results))
{
dbg::detailed_log(lvl:1, src:FUNCTION_NAME, msg:'No domain users found, exiting!');
return [];
}
results = split(results, keep:FALSE);
var users = [];
var groups = {};
foreach(var res in results)
{
var match = pregmatch(pattern:"(\S+)@(\S+)", string:res);
if(empty_or_null(match)) continue;
var username = match[1];
var domain = match[2];
var user = {'username':username, 'domain':domain, 'fullname':res, 'home':ldnix::append_path(path:'/home', value:res)};
gather_user_id_data(user:user, groups:groups);
append_element(var:users, value:user);
}
return {'users':users, 'groups':groups};
}
function create_account_object(uid, usr, home, shell, default_gid, &users, usr_acct, scope)
{
if (typeof(uid) != 'int') return NULL;
if (!structured_accounts.make_account(key:uid)) return FALSE;
structured_accounts.set_name(usr);
structured_accounts.set_accountType(usr_acct);
structured_accounts.set_home_directory(home);
structured_accounts.set_command_shell(shell);
structured_accounts.set_account_scope(scope);
# Set default user group membership
structured_accounts.add_group_membership(default_gid);
structured_groups.focus_group(key:default_gid);
structured_groups.add_group_membership(uid);
var gid;
foreach var group_name(keys(users[usr]))
{
gid = structured_groups.get_gid_by_name(name:group_name);
if (!gid) continue;
structured_accounts.add_group_membership(gid);
structured_groups.focus_group(key:gid);
structured_groups.add_group_membership(uid);
}
}
function create_group_object(gid, name, scope)
{
if (typeof(gid) != 'int') return NULL;
if (!structured_groups.group_exists(key:gid))
{
if (!structured_groups.make_group(key:gid))
return FALSE;
}
else
{
structured_groups.focus_group(key:gid);
}
structured_groups.set_name(name);
structured_groups.set_group_scope(scope);
return TRUE;
}
ldnix::init_plugin();
info_connect(exit_on_fail:true);
var cmd = 'cat /etc/passwd';
var etcp = info_send_cmd(cmd:cmd);
cmd = 'cat /etc/group';
var etcg = info_send_cmd(cmd:cmd);
cmd = 'cat /etc/login.defs';
var etcl = info_send_cmd(cmd:cmd);
var domain_data = get_domain_users_and_groups();
var domain_users = domain_data.users;
var domain_groups = domain_data.groups;
if(info_t == INFO_SSH)
ssh_close_connection();
if('Permission denied' >< etcp || empty_or_null(etcp))
exit(0, 'Could not read /etc/passwd.');
var structured_groups = new('linux_groups');
var structured_accounts = new('linux_accounts');
var checkuid = FALSE, uid_min, uid_max;
if('UID_MIN' >< etcl)
{
var match = pregmatch(pattern:"UID_MIN\s+(\d+)\s+UID_MAX\s+(\d+)", string:join(split(etcl,keep:FALSE),sep:' '));
if(!empty_or_null(match))
{
uid_min = int(match[1]);
uid_max = int(match[2]);
}
checkuid = TRUE;
}
var users = make_array();
var groups = make_array();
var user;
var domain_user, domain_gid;
var injection_found, checkvals, inj_check;
foreach var grp(split(etcg, keep:FALSE))
{
injection_found = FALSE;
if (grp !~ "^[^:]+:[^:]*:[^:]*:[^:]*$") continue;
grp = split(grp, sep:':', keep:FALSE);
groups[grp[2]] = grp[0];
if (empty_or_null(grp[3]))
checkvals = [ grp[0], grp[2] ];
else
checkvals = [ grp[0], grp[2], grp[3] ];
foreach inj_check (checkvals)
{
# Check path for unexpected chars
if (!empty_or_null(inj_check) && !command_builder::validate_no_injection_denylist(inj_check))
{
dbg::detailed_log(lvl:1, msg:'Exiting due to injection attempt in Linux Group enumeration',
msg_details:{
'Group data':{'lvl':1, 'value':inj_check}
}
);
injection_found = TRUE;
}
}
if (injection_found == TRUE)
continue;
create_group_object(gid:int(grp[2]), name:grp[0], scope:'local');
foreach user(split(grp[3], sep:',' , keep:FALSE))
{
if(empty_or_null(users[user]))
users[user] = make_array(grp[0], TRUE);
else
users[user][grp[0]] = TRUE;
}
}
for(domain_gid in domain_groups)
{
create_group_object(gid:domain_gid, name:domain_groups[domain_gid].name, scope:'domain');
foreach(domain_user in domain_groups[domain_gid].users)
{
if(empty_or_null(users[domain_user]))
users[domain_user] = make_array(domain_groups[domain_gid].name, TRUE);
else
users[domain_user][domain_groups[domain_gid].name] = TRUE;
}
}
var report = '';
var report_usr = '';
var report_sys = '';
var report_dom = '';
var usr, uid, home, shell, gid, usr_acct;
var home_dirs = {};
foreach(domain_user in domain_users)
{
usr = domain_user.fullname;
uid = domain_user.uid;
home = domain_user.home;
shell = 'unknown';
gid = domain_user.gid;
create_account_object(uid:uid, usr:usr, home:home, shell:shell, default_gid:gid, users:users, usr_acct:'user', scope:'domain');
report_dom += '\n';
report_dom += join( 'User : ' + usr,
'Home folder : ' + home,
'Start script : ' + shell,
'Groups : ' + join(keys(users[usr]), sep:'\n '),
sep:'\n');
report_dom += '\n';
}
var line;
var usr_acct_list = ["root"];
foreach line(split(etcp, keep:FALSE))
{
injection_found = FALSE;
if(line !~ "^[^:]+:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*$")
continue;
usr_acct = FALSE;
usr = split(line, sep:':', keep:FALSE);
uid = int(usr[2]);
home = usr[5];
shell = usr[6];
gid = int(usr[3]);
usr = usr[0];
checkvals = [ usr, uid, gid, home, shell ];
foreach inj_check (checkvals)
{
# Check path for unexpected chars
if (!empty_or_null(inj_check) && !command_builder::validate_no_injection_denylist(inj_check))
{
dbg::detailed_log(lvl:1, msg:'Exiting due to injection attempt in Linux User enumeration',
msg_details:{
'User data':{'lvl':1, 'value':inj_check}
}
);
injection_found = TRUE;
}
}
if (injection_found == TRUE)
continue;
if(checkuid && uid >= uid_min && uid <= uid_max && shell !~ "/sbin/nologin$")
{
append_element(var:usr_acct_list, value:usr);
usr_acct = TRUE;
}
create_account_object(uid:uid, usr:usr, home:home, shell:shell, default_gid:gid, users:users, usr_acct:usr_acct, scope:'local');
# add default group in case it wasn't already added
if(empty_or_null(users[usr]))
users[usr] = make_array(groups[gid], TRUE);
else
users[usr][groups[gid]] = TRUE;
usr = data_protection::sanitize_user_enum(users:usr);
if(checkuid)
{
if(usr_acct)
{
report_usr += '\n';
report_usr += join( 'User : ' + usr,
'Home folder : ' + home,
'Start script : ' + shell,
'Groups : ' + join(keys(users[usr]), sep:'\n '),
sep:'\n');
report_usr += '\n';
}
else
{
report_sys += '\n';
report_sys += join( 'User : ' + usr,
'Home folder : ' + home,
'Start script : ' + shell,
'Groups : ' + join(keys(users[usr]), sep:'\n '),
sep:'\n');
report_sys += '\n';
}
}
else
{
report += '\n';
report += join( 'User : ' + usr,
'Home folder : ' + home,
'Start script : ' + shell,
'Groups : ' + join(keys(users[usr]), sep:'\n '),
sep:'\n');
report += '\n';
}
if(!empty_or_null(users[usr]))
replace_kb_item(name:'Host/Users/' + usr + '/Groups', value:join(keys(users[usr]), sep:'\n'));
if(!empty_or_null(home))
{
replace_kb_item(name:'Host/Users/' + usr + '/Home', value:home);
home_dirs[home] = TRUE;
}
}
if(!isnull(users))
replace_kb_item(name:'Host/Users', value:join(keys(users), sep:'\n'));
if(!isnull(usr_acct_list))
replace_kb_item(name:'Host/non-service/Users', value:join(usr_acct_list, sep:'\n'));
if(!empty_or_null(home_dirs))
replace_kb_item(name:'Host/UserHomes', value:join(keys(home_dirs), sep:'\n'));
if(checkuid)
{
report = strcat(
'\n-----------[ User Accounts ]-----------\n', report_usr,
'\n----------[ System Accounts ]----------\n', report_sys, '\n'
'\n----------[ Domain Accounts ]----------\n', report_dom, '\n'
);
}
structured_groups.report();
structured_accounts.report();
security_report_v4(severity:SECURITY_NOTE, port:0, extra:report);
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