system()vs execve()

Jak*_*ake 9 security setuid exec elevated-privileges

二者system()execve()可以用于程序内执行另一个命令.为什么在set-UID程序中,system()是危险的,同时execve()又安全吗?

cuo*_*glm 16

系统将调用shell(sh)来执行作为参数发送的命令.system因为shell行为的问题取决于运行命令的用户.一个小例子:

创建文件test.c:

#include <stdio.h>

int main(void) {
    if (system ("ls") != 0)
        printf("Error!");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然后:

$ gcc test.c -o test

$ sudo chown root:root test

$ sudo chmod +s test

$ ls -l test
-rwsr-sr-x 1 root root 6900 Dec 12 17:53 test
Run Code Online (Sandbox Code Playgroud)

创建ls当前目录中调用的脚本:

$ cat > ls
#!/bin/sh

/bin/sh

$ chmod +x ls
Run Code Online (Sandbox Code Playgroud)

现在:

$ PATH=. ./test
# /usr/bin/id
uid=1000(cuonglm) gid=1000(cuonglm) euid=0(root) egid=0(root) groups=0(root),
24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),105(scanner),
110(bluetooth),111(netdev),999(docker),1000(cuonglm)
# /usr/bin/whoami
root
Run Code Online (Sandbox Code Playgroud)

哎呀,你有一个具有root权限的shell.

execve不会调用shell.它执行作为第一个参数传递给它的程序.程序必须是二进制可执行文件或脚本以shebang行开头.

  • @JoelDavis:不,即使你使用完整路径,你仍然有问题.如果使用`/ bin/ls`,用户可以将`/`添加到`$ IFS`,导致shell将`/ bin/ls`拆分为`bin`和`ls`.现在,在我的答案中,当前目录中名为`bin`的可执行文件可以与`ls`做同样的事情. (4认同)
  • @JoelDavis,不,您至少需要清除整个环境,为几个envvar(路径,HOME ...)提供合理的默认值,如果需要,请在消毒后保留一些env var(TERM,DISPLAY,LANG)。 。)确保fds 0、1、2已打开...基本上执行sudo的操作。即使那样,我也不会去那里。如果可以避免,请勿在特权升级上下文中调用shell。请注意,`ls`可以对环境进行处理,因此即使没有`system()`,您也应该清理环境。使用setuid时,您希望最小化以root身份完成的操作(通常不执行命令)。 (2认同)
  • 谢谢你们俩。这实际上是一个非常有趣的答案。 (2认同)

Mar*_*elo 11

system()execve()以不同的方式工作. system()将始终调用shell,并且此shell将作为单独的进程执行该命令(这就是您在使用时可以在命令行中使用通配符和其他shell工具的原因system()).

execve()(以及系列中的其他函数exec())将当前进程替换为直接生成的进程(execve()除非发生故障,否则该函数不会返回).实际上,system()实现应该使用一系列的fork(),execve()wait()调用它来执行它的功能.

当然,两者都很危险,具体取决于进程具有root权限时正在执行的操作. system()但是,由于它使用了额外的shell"层",因为它在你的问题的情况下调用root shell(即,进程有suid位),因此会带来一些额外的危险.