Linux Kernel – How to Change /proc/PID/environ After Process Start

environment-variableslinuxlinux-kernelprocessSecurity

$ k=v p &
[1] 3028

is there any way for p to change the contents of /proc/3028/environ to not mention k=v while p is still running?

Best Answer

On Linux, you can overwrite the value of the environment strings on the stack.

So you can hide the entry by overwriting it with zeros or anything else:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[], char* envp[]) {
  char cmd[100];

  while (*envp) {
    if (strncmp(*envp, "k=", 2) == 0)
      memset(*envp, 0, strlen(*envp));

    envp++;
  }

  sprintf(cmd, "cat /proc/%u/environ", getpid());

  system(cmd);
  return 0;
}

Run as:

$ env -i a=foo k=v b=bar ./wipe-env | hd
00000000  61 3d 66 6f 6f 00 00 00  00 00 62 3d 62 61 72 00  |a=foo.....b=bar.|
00000010

the k=v has been overwritten with \0\0\0.

Note that setenv("k", "", 1) to overwrite the value won't work as in that case, a new "k=" string is allocated.

If you've not otherwise modified the k environment variable with setenv()/putenv(), then you should also be able to do something like this to get the address of the k=v string on the stack (well, of one of them):

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char* argv[]) {
  char cmd[100];
  char *e = getenv("k");

  if (e) {
    e -= strlen("k=");
    memset(e, 0, strlen(e));
  }

  sprintf(cmd, "cat /proc/%u/environ", getpid());

  system(cmd);
  return 0;
}

Note however that it removes only one of the k=v entries received in the environment. Usually, there is only one, but nothing is stopping anyone from passing both k=v1 and k=v2 (or k=v twice) in the env list passed to execve(). That has been the cause of security vulnerabilities in the past such as CVE-2016-2381. It could genuinely happen with bash prior to shellshock when exporting both a variable and function by the same name.

In any case, there will always be a small window during which the env var string has not been overridden yet, so you may want to find another way to pass the secret information to the command (like a pipe for instance) if exposing it via /proc/pid/environ is a concern.

Also note that contrary to /proc/pid/cmdline, /proc/pid/environment is only accessible by processes with the same euid or root (or root only if the euid and ruid of the process are not the same it would seem).

You can hide that value from them in /proc/pid/environ, but they may still be able to get any other copy you've made of the string in memory, for instance by attaching a debugger to it.

See https://www.kernel.org/doc/Documentation/security/Yama.txt for ways to prevent at least non-root users from doing that.

Related Question