Lucene search
K

Linux User List Enumeration

🗓️ 19 Dec 2016 00:00:00Reported by TenableType 
nessus
 nessus
🔗 www.tenable.com👁 436 Views

Linux User List Enumeration. Enumerated local Linux users and groups using supplied credentials

Code
#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

15 Jun 2026 00:00Current
5.4Medium risk
Vulners AI Score5.4
436