Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Remove environment variable from /proc/$pid/environ under Linux
/* Demonstrate how to remove an environment variable from /proc/$pid/environ,
under Linux.
This pseudo file is accessible by other processes with sufficient privileges
(i.e. same user or root) and exposes the original environment vector.
Removing sensitive variables from it can be part of a defense in depth
strategy (i.e. to make it harder for a casual attacker to access it).
*/
/** {{{ MIT No Attribution
Copyright 2020 Georg Sauthoff <mail@gms.tf>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
cf. https://github.com/aws/mit-0
}}} */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/prctl.h>
static int get_mm_map(const char *filename, struct prctl_mm_map *m)
{
// cf. proc(5)
const char fmt[] =
"%*d " // (1) pid
"%*s " // (2) comm
"%*c " // (3) state
"%*d " // (4) ppid
"%*d " // (5) pgrp
"%*d " // (6) session
"%*d " // (7) tty_nr
"%*d " // (8) tpgid
"%*u " // (9) flags
"%*u " // "%lu" // (10) minflt
"%*u " // "%lu" // (11) cminflt
"%*u " // "%lu" // (12) majflt
"%*u " // "%lu" // (13) cmajflt
"%*u " // "%lu" // (14) utime
"%*u " // "%lu" // (15) stime
"%*d " // "%ld" // (16) cutime
"%*d " // "%ld" // (17) cstime
"%*d " // "%ld" // (18) priority
"%*d " // "%ld" // (19) nice
"%*d " // "%ld" // (20) num_threads
"%*d " // "%ld" // (21) itrealvalue
"%*u " // "%llu" // (22) starttime
"%*u " // "%lu" // (23) vsize
"%*d " // "%ld" // (24) rss
"%*u " // "%lu" // (25) rsslim
/* 0 */ "%lu " // (26) start_code [PT]
/* 1 */ "%lu " // (27) end_code [PT]
/* 2 */ "%lu " // (28) start_stack [PT]
"%*u " // "%lu" // (29) kstkesp [PT]
"%*u " // "%lu" // (30) kstkeip [PT]
"%*u " // "%lu" // (31) signal
"%*u " // "%lu" // (32) blocked
"%*u " // "%lu" // (33) sigignore
"%*u " // "%lu" // (34) sigcatch
"%*u " // "%lu" // (35) wchan [PT]
"%*u " // "%lu" // (36) nswap
"%*u " // "%lu" // (37) cnswap
"%*d " // (38) exit_signal (since Linux 2.1.22)
"%*d " // (39) processor (since Linux 2.2.8)
"%*u " // (40) rt_priority (since Linux 2.5.19)
"%*u " // (41) policy (since Linux 2.5.19)
"%*u " // "%llu" // (42) delayacct_blkio_ticks (since Linux 2.6.18)
"%*u " // "%lu" // (43) guest_time (since Linux 2.6.24)
"%*d " // "%ld" // (44) cguest_time (since Linux 2.6.24)
/* 3 */ "%lu " // (45) start_data (since Linux 3.3) [PT]
/* 4 */ "%lu " // (46) end_data (since Linux 3.3) [PT]
/* 5 */ "%lu " // (47) start_brk (since Linux 3.3) [PT]
/* 6 */ "%lu " // (48) arg_start (since Linux 3.5) [PT]
/* 7 */ "%lu " // (49) arg_end (since Linux 3.5) [PT]
/* 8 */ "%lu " // (50) env_start (since Linux 3.5) [PT]
/* 9 */ "%lu " // (51) env_end (since Linux 3.5) [PT]
"%*d" // (52) exit_code (since Linux 3.5) [PT]
;
unsigned long start_code, end_code, start_stack, start_data, end_data,
start_brk, arg_start, arg_end, env_start, env_end;
FILE *f = fopen(filename, "r");
if (!f) {
perror("fopen");
return -1;
}
int r = fscanf(f, fmt,
&start_code, &end_code, &start_stack,
&start_data, &end_data, &start_brk,
&arg_start, &arg_end, &env_start, &env_end);
if (r != 10) {
fprintf(stderr, "parsing /proc/.../environ failed\n");
return -1;
}
uintptr_t brk = (uintptr_t) sbrk(0);
// cf. /usr/include/linux/prctl.h
*m = (struct prctl_mm_map){0};
m->start_code = start_code;
m->end_code = end_code;
m->start_data = start_data;
m->end_data = end_data;
m->start_brk = start_brk;
m->brk = brk;
m->start_stack = start_stack;
m->arg_start = arg_start;
m->arg_end = arg_end;
m->env_start = env_start;
m->env_end = env_end;
m->auxv_size = 0;
m->exe_fd = -1;
return 0;
}
static int filter_env(const char *begin, const char *end, const char *prefix, char **obegin, char **oend)
{
size_t n = strlen(prefix);
char *o = malloc(end-begin);
if (!o)
return -1;
*obegin = o;
for (const char *p = begin; p != end; ) {
const char *e = memchr(p, 0, end-p);
if (e) {
++e;
} else {
e = end;
}
if (e-p < n || memcmp(prefix, p, n))
o = mempcpy(o, p, e-p);
p = e;
}
*oend = o;
return 0;
}
int main(int argc, char **argv)
{
char filename[256];
int pid = getpid();
snprintf(filename, sizeof filename - 1, "/proc/%d/stat", pid);
system("< /proc/$PPID/environ tr '\\0' '\\n' | sort");
struct prctl_mm_map m;
int r = get_mm_map(filename, &m);
if (r)
return 1;
char *es = 0, *ee = 0;
r = filter_env((const char*)(uintptr_t)m.env_start, (const char*)(uintptr_t)m.env_end, "PASS=", &es, &ee);
if (r) {
fprintf(stderr, "filter failed\n");
return 1;
}
m.env_start = (uintptr_t)es;
m.env_end = (uintptr_t)ee;
// int opt, arg2, arg3, arg4, arg5
r = prctl(PR_SET_MM, PR_SET_MM_MAP, (unsigned long) &m, sizeof m, 0);
if (r == -1) {
perror("prctl");
return 1;
}
system("< /proc/$PPID/environ tr '\\0' '\\n' | sort");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment