Linux Environment Variable Enumeration
#TRUSTED 160f54ca2bd598c29eea30aee721b6b7d8fad44790fa7694ed0f525821c19f7b7cec14a91b9af3adb879d8bfd20287a5d6890abd61a0df6fa6bc78750a200327628eba68393979461d3a59226d4fc8c2e320f542ca24e24b8acd94bf95a3b6b1e526cae46dc8f89cad19e7ee26831545995cea2170fec90c1229d2e4f19ec7ad9b75734a756dacddf1fa481e3c9634ba13883705e8e4f58fb04067083357f06edea3d03fa7bbcd5821938380caf1d90024a99eb7c565ca115e3693c4c979fdb8a3c002fba998190a092ab95819f7dd2dc5d35cebb034a5a112beb40e209caba9dc1ad1045224b52e52058f1b3d13787d539277a89db8c7cd156cbea9d37ccd8aca99a892fa037272b8437e033794964c38c4b9603f46f06582c42437d753544e5a67cde83160a972137ecf736162a07eeda87b4e20c9a80d76164a38a7a5b5a23dd6fcdc3d569647bace12f9c0cbdf9f169ecfd55cbd641e1be836049a08bd510662a19208e4c08f3c57a06cf2951f58a89205a47f84ba9165ea1223d5b6a73e506a5efbfa185a6e1ef9e5054126d007363c3022fcfd4593442283ed2a0fa97a7cf8a40d3674342b19f20a42b5ecfbc2b3cb8e766687788f2e5bbf4679e4214699d853aa337fabb23435341f71b5838710709cddda17b87d76aec129676be1e03a391b85411d9928be440bdd5d32ce398c3186a0f0194a7ca4cffda809bd4f9f
#TRUST-RSA-SHA256 4cdd46286c0c82498d5d5de079f64785fe3d1b59840c53879edccbbe85e1bbf7a3edd768e60f130526c4e451f863a41a9895b07fafdf4a4a17a356ca4f55c23a42465e9fa8506beec036560400cc57b837ad65a5341a497115997c1290e3e097ade1a82347d7d71792bf4749dd91127e70154b29803282a52bd20e2838d37ae4e821eef91e83bfc4d9df6c6b5f484c2fbd8af673357622fc3335960139d6006bf46222386f290ed55922adbf2707d6a09430b8f2a043afcc5e75d4ae9324b63de14bcb5d978dfb35f15f1d0f4b41906c816f9cf23c253f972511260386e4fb335ad9397bbcf050fe3b0b3a48df75bf9a345744e6a105a61db9d6668e67e5aeaad578c1fe7d3aad05558d78cbc1da2f8834597d0940ee2d5ad312f98c5d65149a8f80deb34416be5ad1050d3f77f028244db6e3f9937cb134a3f6084e40b4c4284cd70bfa5e5707d015f42edf16f4231c79d94c7eb2116a6c1be64b4ba4cb29703413b3d66a6bb711e221af344aba1e4c833ea44684d559d0d66e7384e0fcec7df49db99c67b1e8039747122bdd11edb4ca35f6171e5c5725f9417aec558ce4de982b7f074783140eade02511f8be20fd5a0237f49c18cc555259faf5ff4f2be861875165d506b3caf47a9497faca18f80cf5c463fa114fd13eae2539f46592240c9dac3bdb51c3b83264ff13ad71bd1cbff371021b1ed7b82da6d8b75dfd7d27
#%NASL_MIN_LEVEL 80900
##
# (C) Tenable, Inc.
##
include('compat.inc');
if(description)
{
script_id(333918);
script_version("1.1");
script_set_attribute(attribute:"plugin_modification_date", value:"2026/08/10");
script_name(english:"Linux Environment Variable Enumeration");
script_set_attribute(attribute:"synopsis", value:
"Enumerates system and user environment variables on Linux hosts.");
script_set_attribute(attribute:"description", value:
"Enumerates environment variables at the SYSTEM and USER scope on Linux.
SYSTEM-scope variables are collected from /etc/environment,
/etc/security/pam_env.conf, /usr/share/pam/security/pam_env.conf and
shell scripts under /etc/profile.d/ (plus /etc/profile and /etc/bashrc).
USER-scope variables are collected from each login user's shell profile
files (.bashrc, .bash_profile, .profile, .zshrc, .zshenv, .zsh_profile).
Collected data is stored as structured data (environment-variable schema)
and written to the Knowledge Base so that downstream plugins (credential
exposure, insecure PATH checks) can consume it without re-running commands.");
script_set_attribute(attribute:"solution", value:"n/a");
script_set_attribute(attribute:"risk_factor", value:"None");
script_set_attribute(attribute:"plugin_publication_date", value:"2026/08/10");
script_set_attribute(attribute:"agent", value:"unix");
script_set_attribute(attribute:"plugin_type", value:"local");
script_end_attributes();
script_category(ACT_GATHER_INFO);
script_family(english:"General");
script_copyright(english:"This script is Copyright (C) 2026 and is owned by Tenable, Inc. or an Affiliate thereof.");
script_dependencies("ssh_get_info2.nasl");
script_require_keys("Host/local_checks_enabled");
exit(0);
}
include("ssh_lib.inc");
include("structured_data_environment_variable.inc");
include("debug.inc");
get_kb_item_or_exit('Host/local_checks_enabled');
if('Linux' >!< get_kb_item("Host/uname"))
audit(AUDIT_OS_NOT, 'Linux');
var USER = "USER";
var SYSTEM = "SYSTEM";
var user_homes = [];
# Shell profile filenames checked per-user.
var shell_env_sources = {
"/bin/bash": {
"/etc/profile" : @check_env_script,
"/etc/bashrc" : @check_env_script,
"/etc/bash.bashrc" : @check_env_script,
"/usr/share/Modules/init/bash" : @check_env_script,
"$HOME/.bash_profile" : @check_env_script,
"$HOME/.bash_login" : @check_env_script,
"$HOME/.profile" : @check_env_script,
"$HOME/.bashrc" : @check_env_script
},
"/bin/csh": {
"/etc/csh.cshrc" : @check_env_script,
"/etc/csh.login" : @check_env_script,
"$HOME/.tcshrc" : @check_env_script,
"$HOME/.cshrc" : @check_env_script,
"$HOME/.login" : @check_env_script
},
"/bin/ksh": {
"/etc/profile" : @check_env_script,
"$HOME/.profile" : @check_env_script,
"$HOME/.kshrc" : @check_env_script
},
"/bin/zsh": {
"/etc/zshenv" : @check_env_script,
"$ZDOTDIR/.zshenv" : @check_env_script,
"/etc/zprofile" : @check_env_script,
"$ZDOTDIR/.zprofile" : @check_env_script,
"/etc/zshrc" : @check_env_script,
"$ZDOTDIR/.zshrc" : @check_env_script,
"/etc/zlogin" : @check_env_script,
"$ZDOTDIR/.zlogin" : @check_env_script
}
};
var system_env_sources = {
"/usr/share/pam/security/pam_env.conf" : @check_pam_env,
"/etc/security/pam_env.conf" : @check_pam_env,
"/etc/environment" : @check_name_value_file,
};
##
# Captures environment variables by sourcing an environment script
# into a clean environment and then displaying that environment. Environment
# variables processed in scripts will replace variables already discovered.
# Generally, only the $HOME environment variable is expanded inline. If the user
# shell is zsh then the $DOTDIR variable will also be expanded inline.
#
# @param [home:list] Values taken from /etc/passwd: [username, home dir, shell]
# @param [source_file:string] Full path of environment script to read.
# @param [envs:array] By-reference storage of environment results.
##
function check_env_script(home, source_file, &envs)
{
var source_path = source_file;
var home_dir, shell;
var user = SYSTEM;
var source_cmd = "";
dbg::detailed_log(lvl:3, msg:"Checking environment script: " + source_file);
if(!isnull(home))
{
home_dir = home[1];
shell = home[2];
}
# Because we source into a clean environment we have to expand vars in the source file path.
if(shell == "/bin/zsh" && "$DOTDIR" >< source_path)
{
var zdotdir;
for(var u of envs)
{
if(!empty_or_null(u["ZDOTDIR"]))
{
zdotdir = u["ZDORDIR"];
break;
}
}
if(empty_or_null(zdotdir))
zdotdir = home_dir;
user = home[0];
source_path = str_replace(string:source_path, find:"$ZDOTDIR", replace:zdotdir);
}
else
{
if("$HOME" >< source_path)
{
user = home[0];
source_path = str_replace(string:source_path, find:"$HOME", replace:home_dir);
}
}
# The source path is null for shell default variables
if(!empty_or_null(source_path))
source_cmd = "source " + source_path + ";";
var check_cmd = "env -i " + shell + " -ci '" + source_cmd + " /usr/bin/printenv' 2>/dev/null";
if(shell =~ INJECTION_PATTERN || source_path =~ INJECTION_PATTERN)
{
lcx::store_dangerous_nix_filepath(item:check_cmd);
dbg::detailed_log(lvl:1, msg:'Either the shell or env path contain potential command injection characters. Ignoring.',
msg_details:{'env path':{'lvl':2, 'value':source_path},
'shell':{'lvl':2, 'value':shell}});
return NULL;
}
var env_vars = info_send_cmd(cmd:check_cmd);
var user_result = envs[user];
if(isnull(user_result))
user_result = {};
if(!empty_or_null(env_vars))
{
env_vars = split(env_vars, keep:false);
var env_v, v_entry, varr, vname, vval, lastline;
var in_func = false;
var maybe_not_in_func = false;
foreach env_v(env_vars)
{
if(in_func)
{
if(env_v =~ "^[^\s=]+\s*=" && lastline =~ "}")
{
in_func = false;
# Eliminate dups created by system scripts if no change
if((isnull(envs[SYSTEM]) || isnull(envs[SYSTEM][vname]) || envs[SYSTEM][vname][0] != vval))
{
v_entry = [vval, source_path];
user_result[vname] = v_entry;
}
}
else
{
vval += '\n' + env_v;
lastline = env_v;
continue;
}
}
if(!in_func)
{
if("=" >!< env_v)
continue;
varr = split(env_v, sep:"=", keep:false);
if(isnull(varr[1]))
varr[1] = "";
vname = varr[0];
delete_element(var:varr, idx:0);
vval = strjoin(list:varr, separator:"=");
if(strlen(vval) > 2 && substr(vval, 0, 1) == "()")
{
in_func = true;
lastline = env_v;
continue;
}
}
# Eliminate dups created by system scripts if no change
if((isnull(envs[SYSTEM]) || isnull(envs[SYSTEM][vname]) || envs[SYSTEM][vname][0] != vval))
{
# value, file, source type (SYSTEM or USER)
v_entry = [vval, source_path];
user_result[vname] = v_entry;
}
}
# Eliminate dups created by system scripts if no change
if(in_func && (isnull(envs[SYSTEM]) || isnull(envs[SYSTEM][vname]) || envs[SYSTEM][vname][0] != vval))
{
v_entry = [vval, source_path];
user_result[vname] = v_entry;
}
envs[user] = user_result;
}
}
##
# Captures environment variables from PAM configuration files.
#
# @param [home:list] Values taken from /etc/passwd: [username, home dir, shell]
# @param [source_file:string] Full path of environment script to read.
# @param [envs:array] By-reference storage of environment results.
##
function check_pam_env(home, source_file, &envs)
{
dbg::detailed_log(lvl:3, msg:"Checking PAM configuration: " + source_file);
# Captures pam_env_conf entries and allows quoted material in values
var capture_re = '^\\s*([^\\s]+)\\s*DEFAULT=(["\'](?:[^"\']+)["\']|(?:[^\\s]+)|(?:))\\s*(?:OVERRIDE=(["\'](?:[^"\']+)["\']|(?:[^\\s]+)|(?:)))?$';
var user, home_dir, shell;
if(!isnull(home))
{
user = home[0];
home_dir = home[1];
shell = home[2];
}
var user_result = envs[user];
if(isnull(user_result))
user_result = {};
var env_vars = info_send_cmd(cmd:"/usr/bin/cat " + source_file + " 2>/dev/null");
if(!empty_or_null(env_vars))
{
var env_lines = split(env_vars, keep:false);
var value, line, match;
var name, default, override, sharp_idx;
for(line of env_lines)
{
#Remove comments
sharp_idx = stridx(line, "#");
if(sharp_idx == 0)
continue;
if(sharp_idx > 0)
line = substr(line, 0, sharp_idx - 1);
match = pregmatch(pattern:capture_re, string:line);
if(isnull(match) || isnull(match[1]) || (empty_or_null(match[2]) && empty_or_null(match[3])))
continue;
name = match[1];
default = match[2];
override = match[3];
#substitute @{HOME} and @{SHELL} if they exist.
if(!empty_or_null(default))
{
default = str_replace(string:default, find:"@{HOME}", replace:home_dir);
default = str_replace(string:default, find:"@{SHELL}", replace:shell);
value = default;
}
if(!empty_or_null(override))
{
override = str_replace(string:override, find:"@{HOME}", replace:home_dir);
override = str_replace(string:override, find:"@{SHELL}", replace:shell);
value = override;
}
user_result[name] = [value, source_file];
}
envs[user] = user_result;
}
}
##
# Captures environment variables from /etc/environment.
#
# @param [home:list] Values taken from /etc/passwd: [username, home dir, shell]
# @param [source_file:string] Full path of environment script to read.
# @param [envs:array] By-reference storage of environment results.
##
function check_name_value_file(home, source_file, &envs)
{
dbg::detailed_log(lvl:3, msg:"Checking /etc/environment");
var user = home[0];
var env_vars = info_send_cmd(cmd:"/usr/bin/cat " + source_file + " 2>/dev/null");
var user_result = envs[user];
if(isnull(user_result))
user_result = {};
if(!empty_or_null(env_vars))
{
var var_val_pairs = split(env_vars, keep:false);
var var_pair, line, varr;
for(line of var_val_pairs)
{
var_pair = [];
varr = split(line, sep:"=", keep:false);
if(len(varr) < 2)
continue;
var_pair[0] = varr[0];
delete_element(var:varr, idx:0);
var_pair[1] = strjoin(list:varr, separator:"=");
user_result[var_pair[0]] = [var_pair[1], source_file];
}
envs[user] = user_result;
}
}
var pw_line, user, home, split, shell;
info_connect(exit_on_fail:TRUE);
var env_sd = create_structured_data_environment_variable_obj();
env_sd.set('observation_scope', 'LINUX');
# ---- discover login users via /etc/passwd ----
var passwd_out = info_send_cmd(cmd:
'awk -F: \'($3>=1000||$1=="root")&&$7!~/nologin|false/{print $1":"$6":"$7}\' /etc/passwd 2>/dev/null'
);
passwd_out = split(passwd_out);
foreach pw_line(passwd_out)
{
split = split(pw_line, sep:':', keep:false);
if(empty_or_null(split[0]) || empty_or_null(split[1]) || empty_or_null(split[2]))
continue;
user = chomp(split[0]);
home = chomp(split[1]);
shell = chomp(split[2]);
if(empty_or_null(shell_env_sources[shell]))
{
dbg::detailed_log(lvl:1, msg:"User's shell not currently supported . Ignoring.",
msg_details:{'user':{'lvl':2, 'value':user}, 'shell':{'lvl':2, 'value':shell}});
continue;
}
append_element(var:user_homes, value:[user, home, shell]);
}
var env_shell, env_file, env_func, env_var, env_details;
var env_result = {};
for(env_file in system_env_sources)
{
env_details = system_env_sources[env_file];
env_details(home:[SYSTEM], source_file:env_file, envs:env_result);
}
foreach home(user_homes)
{
env_result[home[0]] = {
"HOME": [home[1], "/etc/passwd"],
"SHELL": [home[2], "/etc/passwd"]
};
env_details = shell_env_sources[home[2]];
if(!empty_or_null(env_details))
{
for(env_file in env_details)
{
env_func = env_details[env_file];
env_func(home:home, source_file:env_file, envs:env_result);
}
}
}
var report_rows=[];
var total = 0;
var env_source, env_name, env_vals, env_scope, env_vars;
for(env_source in env_result)
{
env_vars = env_result[env_source];
total += max_index(keys(env_vars));
for(env_name in env_vars)
{
if(env_source == SYSTEM)
env_scope = SYSTEM;
else
env_scope = USER;
env_vals = env_vars[env_name];
env_sd.append("variables", {
name:env_name, value:env_vals[0], scope:env_scope, user: env_source, source_path:env_vals[1]
});
if(strlen(env_source) > 28)
env_source = substr(env_source, 0, 28);
if(strlen(env_name) > 38)
env_source = substr(env_name, 0, 28);
append_element(var:report_rows, value:env_source + crap(data:" ", length:30 - strlen(env_source)) +
env_name + crap(data:" ", length:40 - strlen(env_name)) +
env_vals[1]);
}
}
if(info_t == INFO_SSH)
ssh_close_connection();
if(empty_or_null(env_result))
audit(AUDIT_HOST_NOT, 'providing enumerable environment variable information');
# ---- Structured data ----
env_sd.report_internal();
# ---- Human-readable report ----
var report = 'Nessus enumerated ' + total + ' environment variable(s) ' +
'on this host.\n\n' +
'Scope Name [Source]\n' +
'----- ---- --------\n' +
join(report_rows, sep:'\n');
security_report_v4(port:0, severity:SECURITY_NOTE, 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
10 Aug 2026 00:00Current
5.3Medium risk
Vulners AI Score5.3