Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save milesrichardson/8f1c9c7088f8a4e889019d7ce0559ec9 to your computer and use it in GitHub Desktop.
Save milesrichardson/8f1c9c7088f8a4e889019d7ce0559ec9 to your computer and use it in GitHub Desktop.
inherit environment variables from PID 1

You can inherit the environment variables from PID 1 by iterating over the list of null-terminated strings in /proc/1/environ, parsing the first characters up to the first = as the variable name, setting the remaining value as that variable, and exporting it.

The Code Snippet

This works with multiline environment variables, and environment variables with arbitrary values, like strings, including = or JSON blobs.

Paste this in your current terminal session to inherit the environment variables from PID 1:

while read -r -d $'\0' EVAR ; do
  evar_name="$(cut -d'=' -f1 <<< "$EVAR" | head -n1)"
  declare $evar_name="${EVAR#${evar_name}=}"
  export "$evar_name"
done < /proc/1/environ

Note: This only works with bash since it uses declare. But the advantage is that the values never need to pass through any serialization boundary (like they do when saving to a file, or printing export statements to eval them, or various other techniques that don't work for all corner cases )


See also: Unix StackExchange: "How do I source another process's environment variables?"

Why

scenario: You popped a reverse shell in your CI runner and you want to manually run some of your CI scripts. But your SSH session into the CI runner didn't inherit all the environment variables that are available to the actual CI runner. These are set by the CI platform to be environment variables in PID 1.

You can see them with:

cat /proc/1/environ

This file is a concatenation of null-terminated strings of variable assignments. You can reveal the non-printing characters:

cat -vet /proc/1/environ

This is equivalent to cat -v -vE -vT - it will show $ for newlines, ^I for tab characters, and will show non-printing characters, like the separator ^@ (null byte aka $'\0'). Note: you probably do not want to parse this output. It's just useful for debugging and seeing where the separators are.

@chfritz
Copy link

chfritz commented Jan 21, 2023

Why not just export $(sudo cat /proc/1/environ | tr '\0' '\n' | xargs) ? Is that one of the other techniques you were referring to that fails in some cases?

@khirbat
Copy link

khirbat commented Jan 21, 2023

@chfritz This fails on whitespace, as this quick experiment shows. SSH_CONNECTION=... 63882 100.119.0.27 22 is the culprit here.

pi@4b8:~ $ export $(xargs -0 </proc/$$/environ)
-bash: export: `63882': not a valid identifier
-bash: export: `22': not a valid identifier
-bash: export: `63882': not a valid identifier
-bash: export: `100.119.0.27': not a valid identifier
-bash: export: `22': not a valid identifier

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment