You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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能正常输出内容。

另外提两个点:

  1. 要是想实现SetUID程序读取文件的需求,别用system(),直接用C标准库的文件操作函数(比如fopen、fread),这些操作会直接继承进程的SetUID权限,不会被降权。
  2. 你这段代码还有缓冲区溢出风险,sprintf没做长度限制,要是传入的参数太长会越界,建议换成snprintf来限制写入长度。

内容的提问来源于stack exchange,提问作者xiaogw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 00:06:04