Hardening Linux Against Environment Variable Attacks
Understanding Environment Variable Vulnerabilities
Environment variables provide a way for processes to configure their runtime environments. However, malicious actors can take advantage of how Linux handles environment variables to escalate privileges, bypass security controls, or achieve remote code execution. Developers must understand common attack techniques in order to effectively defend against them.
How Malicious Actors Can Exploit Exported Environment Variables
Exported environment variables are inherited by child processes. Attackers can take advantage of this to inject arbitrary code or manipulate the behavior of other programs. Some dangerous environment variables include:
- LD_PRELOAD – Allows loading arbitrary shared objects before any other shared library. This can be used to intercept functions calls from existing libraries.
- PERL5LIB – Alters module search path in Perl, allowing remote code execution if Perl scripts are executed from insecure sources.
- PYTHONPATH – Modifies where Python modules are loaded from, enabling execution of malicious Python code.
- PATH – Controls the search order for executables. An attacker can place malicious versions of system utilities earlier in the PATH to have them executed instead of the real binaries.
Examples of Attacks via LD_PRELOAD, PERL5LIB, and PYTHONHOME
Some real-world examples of attacks abusing environment variables include:
- LD_PRELOAD attack to escalate privileges by intercepting sudo function calls.
- Setting PERL5LIB to load malicious Perl modules containing backdoors.
- PYTHONPATH manipulation to achieve remote code execution as a privileged user.
These variables provide control over linking, code loading, and module search paths – all leveraged by attackers to sideload malicious payloads or inject unexpected code into legitimate processes.
Limiting Access to Sensitive Environment Variables
To reduce the attack surface, access to particularly sensitive environment variables should be restricted. This can prevent less privileged processes from modifying these variables to inject malicious behavior into running programs.
Restricting Permissions to Modify Key Environment Variable Files
Many environment variables are loaded from startup files like /etc/environment or ~/.bashrc. Write access to these files should be limited only to the root user or individual owners:
# Restrict access to /etc/environment chmod 644 /etc/environment # Restrict bashrc only to owner chmod 700 ~/.bashrc
This prevents unprivileged users or processes from appending arbitrary environment variable definitions into these startup sources.
Blocking Inheritance of Potentially Dangerous Variables
Whitelisting environment variable inheritance prevents uncontrolled passing of dangerous variables to child processes:
# In /etc/sudoers Defaults env_keep += "HOME PATH TERM"
The above sudo configuration only preserves HOME, PATH, and TERM while stripping all other environment variables for sudo commands. This best practice drastically reduces attack surface from inherited environments.
Sanitizing Values of Environment Variables
When environment variable use is necessary, sanitizing their values can also limit attacks by removing dangerous inputs. Some methods include:
Filtering and Validating Variable Content
Before executing any privileged process, scrutinize its environment variables:
filter_env() { # Blocklisted strings unset ${blocklist[@]} # Filter allowed paths LD_PRELOAD=$(echo $LD_PRELOAD | grep "/lib/") # Validate safe values if [ -z "$PYTHONPATH" ]; then export PYTHONPATH="/usr/local/lib/python" fi } filter_env sudo ./admin_script.sh
This removes entire blacklist variables, sanitizes allowed values to known-good directories, and sets safe defaults where possible.
Blocklisting Dangerous Values Like External Paths
When filtering variable values, blocklist dangerous inputs like external paths:
# Blocklists blocklist=( LD_PRELOAD PERL5LIB PYTHONPATH ) path_blocklist=( ".." "/" "~/.." ) # Filter implementation if [[ ":$PATH:" == *":$path_blocklist:"* ]]; then echo "Illegal PATH value" exit 1 fi
This denies PATH and other variables from containing directory traversal sequences that could reach unexpected payloads.
Preventing Code Injection Through Environment Variables
Limiting entire classes of attacks via environment variables requires system-wide configurations:
Configuring Execution Permissions in /etc/suid-debug to Block Attacks
The suid-debug feature can selectively disable environment variable attacks like LD_PRELOAD for SUID binaries:
LD_PRELOAD=/tmp/evil.so ls = nope LD_PRELOAD=/tmp/evil.so sudo = nope # But normal usage still allowed: LD_PRELOAD=/usr/lib64/libmql1.so steam
This denies any attempt to influence SUID binaries with LD_PRELOAD or similar variables by dropping their values. Configure via:
/etc/suid-debug/LD_PRELOAD = 1
Using System Call Filtering with seccomp-bpf
The Linux kernels seccomp-bpf can filter all system calls made by processes within defined policies. Environment variable attacks often rely on executions, code injections, and disc operations.
seccomp.blacklist = execve, process_vm_writev, mknod docker run --security-opt=seccomp:/path/to/seccomp.blacklist
A strict policy effectively jails the container processes – denying critical system calls required by most attacks leveraging environment variables yet permitting the process to otherwise function safely.
Detecting and Responding To Environment Variable Attacks
Timely detection and diagnosis of attacks abusing environment variables allows rapid incident response.
Analyzing Process Execution Data and Command-Line Arguments
Collecting detailed audit records of all process executions reveals attack attempts trying to inherit manipulated variables:
type=SYSCALL msg=audit(1677564124.248:604527): arch=x86_64 syscall=execve success=no exit=-13 a0=68732F6E69622F6C64a1=1 a2=7FFECD2FA2D0 a3=7FFECD2F9F20 items=2 ppid=1 pid=14667 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=5 comm="ldconfig" exe="/usr/bin/ldconfig" subj==unconfined key="ldconfig"
In this case LD_PRELOAD was likely set maliciously prior to execution to intercept library calls from a privileged binary. Analyzing all execve and open syscall patterns reveals privilege escalation efforts.
Identifying Privilege Escalation Attempts in Audit Logs
Centralized logging also enables actual exploitations to be identified post-attack:
type=PROCTITLE msg=audit(1677564299.377:604546): proctitle=2F746D702F746D705F78783B2F746D702F746D705F7878 type=SYSCALL msg=audit(1677564299.377:604546): arch=x86_64 syscall=openat success=yes exit=3 a0=FFFF9C5592317000 a1=2F746D702F746D705F7878 a2=O_WRONLY|O_CREAT|O_TRUNC a3=0x1b6 items=0 ppid=2671 pid=11004 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="bash" exe="/usr/bin/bash" subj==unconfined key="sshd_config"
This unauthorized file write had the SUID bit set, indicating successful privilege escalation likely due to a malicious environment variable influencing bash.
Stopping Suspicious Processes and Restricting Accounts
Once an attack via environment variable is isolated, affected processes should be halted and user accounts disabled as appropriate:
kill -9 1337 usermod -L attacker01 iptables -I INPUT -s 192.168.1.x -j DROP
Rapid containment limits potential damages from exploitation attempts and allows for controlled repair of misconfigurations permitting the breach before resuming operations.