Cet*_*ert 12 linux security process environment-variables linux-kernel
$ k=v p &
[1] 3028
Run Code Online (Sandbox Code Playgroud)
有什么方法可以在仍在运行时p更改/proc/3028/environ不提及的内容吗?k=v p
Sté*_*las 12
在 Linux 上,您可以覆盖堆栈上环境字符串的值。
因此,您可以通过用零或其他任何内容覆盖它来隐藏条目:
#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 Code Online (Sandbox Code Playgroud)
运行为:
$ 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
Run Code Online (Sandbox Code Playgroud)
在k=v已被覆盖用\0\0\0。
请注意,setenv("k", "", 1)在这种情况下无法覆盖该值,会"k="分配一个新字符串。
如果您没有以其他方式k使用setenv()/修改环境变量putenv(),那么您还应该能够执行以下操作以获取k=v堆栈上字符串的地址(好吧,其中之一):
#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;
}
Run Code Online (Sandbox Code Playgroud)
但是请注意,它仅删除一个的的k=v环境中收到的条目。通常,只有一个,但没有什么能阻止任何人在传递给 的 env 列表中同时传递k=v1和k=v2(或k=v两次)execve()。这就是过去导致安全漏洞的原因,例如CVE-2016-2381。bash当以相同的名称导出变量和函数时,它可能真的发生在 shellshock 之前。
在任何情况下,总会有在这期间的环境变量字符串尚未覆盖尚未一个小窗口,所以你可能希望找到另一种方式来传递秘密信息的命令(例如像管),如果通过将它暴露/proc/pid/environ是一个问题。
另请注意,与 , 相反/proc/pid/cmdline,/proc/pid/environment只能由具有相同 euid 或 root 的进程访问(或仅当进程的 euid 和 ruid 看起来不同时才可访问 root)。
您可以在 中对他们隐藏该值/proc/pid/environ,但他们可能仍然能够获取您在内存中制作的任何其他字符串副本,例如通过将调试器附加到它。
请参阅https://www.kernel.org/doc/Documentation/security/Yama.txt了解至少防止非 root 用户这样做的方法。
Jde*_*eBP 10
自 2010 年以来,没有必要覆盖Linux上的主线程堆栈上面的字符串(不是真的在)。
两个/proc/self/cmdline和/proc/self/environ是由该方法本身在运行时修改的,通过调用的力prctl()功能分别与PR_SET_MM_ARG_START+PR_SET_MM_ARG_END或PR_SET_MM_ENV_START+ PR_SET_MM_ENV_END。这些直接将内存指针设置到进程的应用程序内存空间中,由内核为每个进程保存,用于检索和的内容,/proc/${PID}/cmdline以及/proc/${PID}/environ命令报告的命令行和环境ps。
所以只需要构造一个新的参数或环境字符串(不是向量,注意——指向的内存必须是实际的字符串数据,连接和␀分隔)并告诉内核它在哪里。
这在该prctl(2)函数的 Linux 手册页和手册页中都有记录environ(7)。什么是没有记载的是,内核拒绝任何试图设置结束地址,或起始地址低于结束地址上方的起始地址; 或(重新)将任一地址设置为零。此外,这不是 Bryan Donlan 在 2009 年提出的原始机制,它允许在单个操作中原子地设置开始和结束。此外,内核没有提供获取这些指针当前值的方法。
这使得棘手的修改环境和命令行的区域prctl()。必须prctl()最多调用该函数四次,因为第一次尝试可能会导致尝试将开始指针设置为高于结束指针,这取决于旧数据和新数据在内存中的位置。一个人把它称为进一步如果想要确保这不会导致其他进程的机会之窗在系统上检查进程的内存空间的任意范围内时,新的开始/结束期间的四倍已设置,但新的结束/开始尚未设置。
一次设置整个范围的单个原子系统调用对于应用程序安全使用要容易得多。
进一步的皱纹是,因为没有很好的理由(给出的检查在内核中,原始数据区的重写性能反正,和事实等同不是特权操作上的任何BSD系统的),在Linux上,这需要超级用户特权。
我写的相当简单setprocargv()和setprocenvv()我的工具集功能,即采用这种。来自内置工具集的链式加载程序,例如setenv和foreground,因此反映了 Linux 允许的链接到命令参数和环境。
# /package/admin/nosh/command/clearenv setenv WIBBLE 摆动前景暂停 \; 真的 & [1] 1057 # hexdump -C /proc/1057/cmdline 00000000 66 6f 72 65 67 72 6f 75 6e 64 00 70 61 75 73 65 |foreground.pause| 00000010 00 3b 00 74 72 75 65 00 |.;.true.| 00000018 # hexdump -C /proc/1057/environ 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 |WIBBLE=摆动。| 0000000e # hexdump -C /proc/1058/cmdline 00000000 70 61 75 73 65 00 |暂停。| 00000006 # hexdump -C /proc/1058/environ 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 |WIBBLE=摆动。| 0000000e #
请注意,这并不妨碍通过其他方式(而不是通过这两个伪文件)跟踪进程并直接访问其内存的事物,当然,在修改字符串之前会留下一个窗口,在那里可以看到这些信息,只是就像覆盖主线程堆栈上方的数据一样。就像覆盖数据的情况一样,这不考虑在各种情况下复制环境(在堆上)的语言运行时库。一般来说,不要认为这是一种将“秘密”传递给程序的机制,就像(比如说)让它继承一个打开的文件描述符到一个未命名管道的读端,读入一个完全由你控制的输入缓冲区你然后擦。