为何SetUID程序调用system()时无法获取对应权限?
问题背景
我编写了如下程序:
#include <err.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char *argv[]) { char buf[32]; if(argc == 1) { errx(1, "please specify an argument."); } sprintf(buf, "cat %s", argv[1]); system(buf); return 0; }
编译及权限配置步骤如下:
$ gcc cat.c -o mycat $ chmod +s mycat; sudo chown root mycat $ ls -lth mycat -rwsr-sr-x 1 root ... 11:12 mycat $ ls -lth secret -rw------- 1 root ... 11:10 secret
我原本认为执行./mycat secret应该能读取secret文件,因为mycat是root所有的SetUID程序,但实际执行结果为:
$ ./mycat secret cat: secret: Permission denied $ sudo chown user1 secret $ ./mycat secret 4ea6f17dd6818
请问我的理解存在哪些误区?
误区解析
你的核心问题是没搞懂system()函数的权限逻辑:
system()本质是调用系统的shell(一般是/bin/sh)来执行你传入的命令串,但绝大多数现代类Unix系统的shell,在被SetUID程序调用时,会主动清除提升的权限——这是个安全机制,防止恶意利用SetUID程序通过shell获取高权限。- 所以哪怕你的
mycat是root权限的SetUID程序,调用system("cat secret")时,实际跑cat的进程已经被shell降回了你的普通用户身份,自然读不到root专属的secret文件。 - 后来你把
secret的所有者改成user1,普通用户本来就有权限读,所以cat能正常输出内容。
另外提两个点:
- 要是想实现SetUID程序读取文件的需求,别用
system(),直接用C标准库的文件操作函数(比如fopen、fread),这些操作会直接继承进程的SetUID权限,不会被降权。 - 你这段代码还有缓冲区溢出风险,
sprintf没做长度限制,要是传入的参数太长会越界,建议换成snprintf来限制写入长度。
内容的提问来源于stack exchange,提问作者xiaogw
相关产品推荐
相关产品推荐

