daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

linux-environment-variables.md (15808B)


      1 ---
      2 title: "Linux Environment Variables"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/linux-basics/linux-environment-variables.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/linux-basics/linux-environment-variables.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Linux Environment Variables
     14 
     15 ## Global variables
     16 
     17 The global variables **will be** inherited by **child processes**.
     18 
     19 You can create a global variable for your current session doing:
     20 
     21 ```bash
     22 export MYGLOBAL="hello world"
     23 echo $MYGLOBAL #Prints: hello world
     24 ```
     25 
     26 This variable will be accessible by your current sessions and its child processes.
     27 
     28 You can **remove** a variable doing:
     29 
     30 ```bash
     31 unset MYGLOBAL
     32 ```
     33 
     34 ## Local variables
     35 
     36 The **local variables** can only be **accessed** by the **current shell/script**.
     37 
     38 ```bash
     39 LOCAL="my local"
     40 echo $LOCAL
     41 unset LOCAL
     42 ```
     43 
     44 ## List current variables
     45 
     46 ```bash
     47 set
     48 env
     49 printenv
     50 cat /proc/$$/environ
     51 cat /proc/`python -c "import os; print(os.getppid())"`/environ
     52 ```
     53 
     54 The contents of `/proc/*/environ` are **NUL-separated**, so these variants are usually easier to read:
     55 
     56 ```bash
     57 tr '\0' '\n' </proc/$$/environ | sort -u
     58 tr '\0' '\n' </proc/<PID>/environ | sort -u
     59 ```
     60 
     61 If you are looking for **credentials** or **interesting service configuration** inside inherited environments, also check [Linux Post Exploitation](/hacktricks/linux-hardening/post-exploitation/linux-post-exploitation/overview).
     62 
     63 ## Common variables
     64 
     65 From: [https://geek-university.com/linux/common-environment-variables/](https://geek-university.com/linux/common-environment-variables/).<sup>[[5]](#references)</sup>
     66 
     67 - **DISPLAY** – the display used by **X**. This variable is usually set to **:0.0**, which means the first display on the current computer.
     68 - **EDITOR** – the user’s preferred text editor.
     69 - **HISTFILESIZE** – the maximum number of lines contained in the history file.
     70 - **HISTSIZE** – Number of lines added to the history file when the user finish his session
     71 - **HOME** – your home directory.
     72 - **HOSTNAME** – the hostname of the computer.
     73 - **LANG** – your current language.
     74 - **MAIL** – the location of the user’s mail spool. Usually **/var/spool/mail/USER**.
     75 - **MANPATH** – the list of directories to search for manual pages.
     76 - **OSTYPE** – the type of operating system.
     77 - **PS1** – the default prompt in bash.
     78 - **PATH** – stores the path of all the directories which holds binary files you want to execute just by specifying the name of the file and not by relative or absolute path.
     79 - **PWD** – the current working directory.
     80 - **SHELL** – the path to the current command shell (for example, **/bin/bash**).
     81 - **TERM** – the current terminal type (for example, **xterm**).
     82 - **TZ** – your time zone.
     83 - **USER** – your current username.
     84 
     85 ## Interesting variables for hacking
     86 
     87 Not every variable is equally useful. From an offensive perspective, prioritize variables that change **search paths**, **startup files**, **dynamic linker behavior**, or **audit/logging**.
     88 
     89 ### **HISTFILESIZE**
     90 
     91 Change the **value of this variable to 0**, so when you **end your session** the **history file** (\~/.bash_history) will be **truncated to 0 lines**.
     92 
     93 ```bash
     94 export HISTFILESIZE=0
     95 ```
     96 
     97 ### **HISTSIZE**
     98 
     99 Change the **value of this variable to 0**, so commands are **not kept in the in-memory history** and won't be written back to the **history file** (\~/.bash_history).
    100 
    101 ```bash
    102 export HISTSIZE=0
    103 ```
    104 
    105 ### **HISTCONTROL**
    106 
    107 If the **value of this variable is set to `ignorespace` or `ignoreboth`**, any command prepended with an extra space will not be saved in the history.
    108 
    109 ```bash
    110 export HISTCONTROL=ignorespace
    111 ```
    112 
    113 ```bash
    114 $ echo "to save or"
    115 $  echo "not to save"
    116 ```
    117 
    118 ### **HISTFILE**
    119 
    120 Point the **history file** to **`/dev/null`** or unset it completely. This is usually more reliable than only changing the history size.
    121 
    122 ```bash
    123 export HISTFILE=/dev/null
    124 unset HISTFILE
    125 ```
    126 
    127 ### http_proxy & https_proxy
    128 
    129 The processes will use the **proxy** declared here to connect to internet through **http or https**.
    130 
    131 ```bash
    132 export http_proxy="http://10.10.10.10:8080"
    133 export https_proxy="http://10.10.10.10:8080"
    134 ```
    135 
    136 ### all_proxy & no_proxy
    137 
    138 - `all_proxy`: default proxy for tools/protocols that honor it.
    139 - `no_proxy`: bypass list (hosts/domains/CIDRs) that should connect directly.
    140 
    141 ```bash
    142 export all_proxy="socks5h://10.10.10.10:1080"
    143 export no_proxy="localhost,127.0.0.1,.corp.local,10.0.0.0/8"
    144 ```
    145 
    146 Both lowercase and uppercase variants may be used depending on the tool (`http_proxy`/`HTTP_PROXY`, `no_proxy`/`NO_PROXY`).
    147 
    148 ### SSL_CERT_FILE & SSL_CERT_DIR
    149 
    150 The processes will trust the certificates indicated in **these env variables**. This is useful to make tools such as **`curl`**, **`git`**, Python HTTP clients, or package managers trust a CA controlled by the attacker (for example, to make an interception proxy look legitimate).
    151 
    152 ```bash
    153 export SSL_CERT_FILE=/path/to/ca-bundle.pem
    154 export SSL_CERT_DIR=/path/to/ca-certificates
    155 ```
    156 
    157 ### **PATH**
    158 
    159 If a privileged wrapper/script executes commands **without absolute paths**, the **first attacker-controlled directory** in `PATH` wins. This is the primitive behind many **PATH hijacks** in `sudo`, cron jobs, shell wrappers, and custom SUID helpers. Look for `env_keep+=PATH`, weak `secure_path`, or wrappers that call `tar`, `service`, `cp`, `python`, etc. by name.
    160 
    161 ```bash
    162 mkdir -p /dev/shm/bin
    163 cat > /dev/shm/bin/tar <<'EOF'
    164 #!/bin/sh
    165 echo '[+] PATH hijack reached' >&2
    166 id
    167 EOF
    168 chmod +x /dev/shm/bin/tar
    169 PATH=/dev/shm/bin:$PATH vulnerable-wrapper
    170 ```
    171 
    172 For full privilege-escalation chains abusing `PATH`, check [Linux Privilege Escalation](/hacktricks/linux-hardening/linux-basics/linux-privilege-escalation/overview).
    173 
    174 ### **HOME & XDG_CONFIG_HOME**
    175 
    176 `HOME` is not only a directory reference: many tools automatically load **dotfiles**, **plugins**, and **per-user configuration** from `$HOME` or `$XDG_CONFIG_HOME`. If a privileged workflow preserves these values, **config injection** may be easier than binary hijacking.
    177 
    178 ```bash
    179 export HOME=/dev/shm/fakehome
    180 export XDG_CONFIG_HOME=/dev/shm/fakehome/.config
    181 mkdir -p "$XDG_CONFIG_HOME"
    182 ```
    183 
    184 Interesting targets include `.gitconfig`, `.wgetrc`, `.curlrc`, `.inputrc`, `.pythonrc.py`, and tool-specific files such as `.terraformrc`.
    185 
    186 ### **LD_PRELOAD, LD_LIBRARY_PATH & LD_AUDIT**
    187 
    188 These variables influence the **dynamic linker**:
    189 
    190 - `LD_PRELOAD`: force extra shared objects to be loaded first.
    191 - `LD_LIBRARY_PATH`: prepend library search directories.
    192 - `LD_AUDIT`: load auditor libraries that observe library loading and symbol resolution.
    193 
    194 They are extremely valuable for **hooking**, **instrumentation**, and **privilege escalation** if a privileged command preserves them. In **secure-execution** mode (`AT_SECURE`, e.g. setuid/setgid/capabilities), the loader strips or restricts many of these variables. However, parser bugs in that early loader stage are still high-impact because they run **before** the target program.<sup>[[2]](#references)</sup>
    195 
    196 ```bash
    197 env | grep -E '^LD_'
    198 ldso=$(ls /lib64/ld-linux-*.so.* /lib/*-linux-gnu/ld-linux-*.so.* 2>/dev/null | head -n1)
    199 "$ldso" --list-diagnostics /bin/true | head
    200 "$ldso" --list-tunables /bin/true | head
    201 ```
    202 
    203 ### **GLIBC_TUNABLES**
    204 
    205 `GLIBC_TUNABLES` changes early glibc behavior (for example, allocator tunables) and is very handy in exploit labs. It also matters from a security perspective because the **dynamic loader parses it very early**. The 2023 **Looney Tunables** bug was a good reminder that a single environment variable parsed in the loader can become a **local privilege-escalation primitive** against SUID programs.<sup>[[6]](#references)</sup>
    206 
    207 ```bash
    208 GLIBC_TUNABLES=glibc.malloc.tcache_count=0 ./binary
    209 ```
    210 
    211 ### **BASH_ENV & ENV**
    212 
    213 If **Bash** is started **non-interactively**, it checks `BASH_ENV` and sources that file before running the target script. When Bash is invoked as `sh`, or in POSIX-style interactive mode, `ENV` may also be consulted. This is a classic way to turn a shell wrapper into code execution if the environment is attacker-controlled.
    214 
    215 ```bash
    216 cat > /tmp/pre.sh <<'EOF'
    217 echo '[+] sourced before the target script'
    218 EOF
    219 BASH_ENV=/tmp/pre.sh bash -c 'echo target'
    220 ```
    221 
    222 Bash ignores these startup files when the **real/effective IDs differ**; `-p` preserves the effective ID but does not enable those startup files, so the exact behavior depends on how the wrapper invokes the shell. Be careful with privileged wrappers that call `setuid()`/`setgid()` **before** launching Bash: once the IDs match again, Bash may trust `BASH_ENV`, `ENV`, and related shell state that would otherwise be ignored.<sup>[[1]](#references)</sup>
    223 
    224 ### **PYTHONPATH, PYTHONHOME, PYTHONSTARTUP & PYTHONINSPECT**
    225 
    226 These variables change how Python starts:
    227 
    228 - `PYTHONPATH`: prepend import search paths.
    229 - `PYTHONHOME`: relocate the standard library tree.
    230 - `PYTHONSTARTUP`: execute a file before the interactive prompt.
    231 - `PYTHONINSPECT=1`: drop into interactive mode after a script finishes.
    232 
    233 They are useful against maintenance scripts, debuggers, shells, and wrappers that call Python with a controllable environment. `python -E` and `python -I` ignore all `PYTHON*` variables.
    234 
    235 ```bash
    236 mkdir -p /tmp/pylib
    237 printf 'print("owned from PYTHONPATH")\n' > /tmp/pylib/htmod.py
    238 PYTHONPATH=/tmp/pylib python3 -c 'import htmod'
    239 PYTHONPATH=/tmp/pylib python3 -I -c 'import htmod'   # ignored in isolated mode
    240 ```
    241 
    242 A recent real-world example was the 2024 **needrestart** LPE on Ubuntu/Debian systems: the root-owned scanner copied an unprivileged process's `PYTHONPATH` from `/proc/<PID>/environ` and then executed Python. The published exploit planted `importlib/__init__.so` in the attacker-controlled path so Python executed attacker code during its own initialization, before the helper's hard-coded script even mattered.<sup>[[3]](#references)</sup>
    243 
    244 ### **PERL5OPT & PERL5LIB**
    245 
    246 Perl has equally useful startup variables:
    247 
    248 - `PERL5LIB`: prepend library directories.
    249 - `PERL5OPT`: inject switches as if they were on every `perl` command line.
    250 
    251 This can force **automatic module loading** or change interpreter behavior before the target script does anything interesting. Perl ignores these variables in **taint / setuid / setgid** contexts, but they still matter a lot for normal root-run wrappers, CI jobs, installers, and custom sudoers rules.
    252 
    253 ```bash
    254 mkdir -p /tmp/perllib
    255 cat > /tmp/perllib/HT.pm <<'EOF'
    256 package HT;
    257 BEGIN { print "PERL5OPT_TRIGGERED\n" }
    258 1;
    259 EOF
    260 PERL5LIB=/tmp/perllib PERL5OPT=-MHT perl -e 'print "target\n"'
    261 ```
    262 
    263 ### **NODE_OPTIONS**
    264 
    265 `NODE_OPTIONS` prepends **Node.js CLI flags** to every `node` process that inherits the environment. This makes it useful against wrappers, CI jobs, Electron helpers, and sudo rules that eventually invoke Node. The most interesting flags offensively are usually:
    266 
    267 - `--require <file>`: preload a CommonJS file before the target script.
    268 - `--import <module>`: preload an ES module before the target script.
    269 
    270 Node rejects some dangerous flags in `NODE_OPTIONS`, but `--require` and `--import` are explicitly allowed and are processed **before** the regular command-line arguments.<sup>[[4]](#references)</sup>
    271 
    272 ```bash
    273 cat > /tmp/preload.js <<'EOF'
    274 console.error('[+] NODE_OPTIONS preload reached')
    275 EOF
    276 NODE_OPTIONS='--require /tmp/preload.js' node -e 'console.log("target")'
    277 ```
    278 
    279 For remote gadget chains that set `NODE_OPTIONS` indirectly (for example, prototype-pollution to RCE), check [this other page](/hacktricks/pentesting-web/deserialization/nodejs-proto-prototype-pollution/prototype-pollution-to-rce).
    280 
    281 ### **RUBYLIB & RUBYOPT**
    282 
    283 Ruby offers the same class of startup abuse:
    284 
    285 - `RUBYLIB`: prepend directories to Ruby's load path.
    286 - `RUBYOPT`: inject command-line options such as `-r` into every `ruby` invocation.
    287 
    288 ```bash
    289 mkdir -p /tmp/rubylib
    290 printf 'warn "[+] RUBYOPT preload reached"\n' > /tmp/rubylib/ht.rb
    291 RUBYLIB=/tmp/rubylib RUBYOPT='-rht' ruby -e 'puts :target'
    292 ```
    293 
    294 The 2024 **needrestart** vulnerabilities showed that this is not just a lab trick: the same root-owned helper that was vulnerable to `PYTHONPATH` abuse could also be coerced into running Ruby with an attacker-controlled `RUBYLIB`, loading `enc/encdb.so` from an attacker directory.<sup>[[3]](#references)</sup>
    295 
    296 ### **PAGER, MANPAGER, GIT_PAGER, GIT_EDITOR & LESSOPEN**
    297 
    298 Some tools do not just read a path from the environment; they pass the value to a **shell**, an **editor**, or an **input preprocessor**. This makes the following variables especially interesting when a privileged wrapper runs `git`, `man`, `less`, or similar text viewers:
    299 
    300 - `PAGER`, `MANPAGER`, `GIT_PAGER`: choose the pager command.
    301 - `GIT_EDITOR`, `VISUAL`, `EDITOR`: choose the editor command, often with arguments.
    302 - `LESSOPEN`, `LESSCLOSE`: define pre/post-processors that run when `less` opens a file.
    303 
    304 ```bash
    305 PAGER='sh -c "exec sh 0<&1 1>&1"' man man
    306 
    307 cat > /tmp/lesspipe.sh <<'EOF'
    308 #!/bin/sh
    309 echo '[+] LESSOPEN triggered' >&2
    310 cat "$1"
    311 EOF
    312 chmod +x /tmp/lesspipe.sh
    313 LESSOPEN='|/tmp/lesspipe.sh %s' less /etc/hosts
    314 ```
    315 
    316 Git also supports **env-only config injection** without touching disk via `GIT_CONFIG_COUNT`, `GIT_CONFIG_KEY_<n>`, and `GIT_CONFIG_VALUE_<n>`:
    317 
    318 ```bash
    319 GIT_CONFIG_COUNT=1 \
    320 GIT_CONFIG_KEY_0=core.pager \
    321 GIT_CONFIG_VALUE_0='sh -c "exec sh 0<&1 1>&1"' \
    322 git -p help
    323 ```
    324 
    325 From a post-exploitation perspective, also remember that inherited environments often contain **credentials**, **proxy settings**, **service tokens**, or **cloud keys**. Check [Linux Post Exploitation](/hacktricks/linux-hardening/post-exploitation/linux-post-exploitation/overview) for `/proc/<PID>/environ` and `systemd` `Environment=` hunting.
    326 
    327 ### PS1
    328 
    329 Change how your prompt looks.
    330 
    331 [**This is an example**](https://gist.github.com/carlospolop/43f7cd50f3deea972439af3222b68808)
    332 
    333 Root:
    334 
    335 ![PERL5OPT & PERL5LIB - PS1: This is an example](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/images/image%20%28897%29.png)
    336 
    337 Regular user:
    338 
    339 ![PERL5OPT & PERL5LIB - PS1: One, two and three backgrounded jobs](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/images/image%20%28740%29.png)
    340 
    341 One, two and three backgrounded jobs:
    342 
    343 ![PERL5OPT & PERL5LIB - PS1: One, two and three backgrounded jobs](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/images/image%20%28145%29.png)
    344 
    345 One background job, one stopped and last command didn't finish correctly:
    346 
    347 ![PERL5OPT & PERL5LIB - PS1: One background job, one stopped and last command didn't finish correctly](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/images/image%20%28715%29.png)
    348 
    349 ## References
    350 
    351 - [1] [GNU Bash Manual - Bash Startup Files](https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html)
    352 - [2] [ld.so(8) - Linux manual page](https://man7.org/linux/man-pages/man8/ld.so.8.html)
    353 - [3] [Qualys - LPEs in needrestart](https://www.qualys.com/2024/11/19/needrestart/needrestart.txt)
    354 - [4] [Node.js CLI documentation - `NODE_OPTIONS`](https://nodejs.org/api/cli.html)
    355 - [5] [Common environment variables - Geek University](https://geek-university.com/linux/common-environment-variables/)
    356 - [6] [CVE-2023-4911: Looney Tunables - Local Privilege Escalation in the glibc's ld.so - Qualys](https://blog.qualys.com/vulnerabilities-threat-research/2023/10/03/cve-2023-4911-looney-tunables-local-privilege-escalation-in-the-glibcs-ld-so)