2014-12-12 187 views

回答

9

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

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

当然,这两者都是危险的,具体取决于当进程具有root权限时正在执行的内容。但是,由于它使用了额外的shell“层”,因为它会在您的问题(即进程具有suid位)的情况下调用根shell时打开机房安全漏洞,所以它会带来一些额外的危险。

+0

所以,当使用execve()..你提到它取代当前进程..该进程仍然setuid? – Jake 2014-12-12 10:44:55

+0

是的。由execve启动的“新”进程继承了被替换的一些属性,例如filedescriptors,sockets等,而有效的uid就是其中之一,但是在执行execve期间uid会发生变化,比如如果execve参数指向的可执行文件已设置suid位。在这种情况下,uid会根据文件系统中的定义更改为文件所有者。 – Marcelo 2014-12-12 10:54:17

12

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

创建文件test.c

#include <stdio.h> 

int main(void) { 
    if (system ("ls") != 0) 
     printf("Error!"); 
    return 0; 
} 

然后:

$ 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 

创建在当前目录中名为ls脚本:

$ cat > ls 
#!/bin/sh 

/bin/sh 

$ chmod +x ls 

现在:

$ 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 

糟糕,你有一个拥有root权限的shell。

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

+0

不说'system()'没有问题,但不会通过在二进制可执行文件中使用绝对路径来解决上述问题吗? – Bratchley 2014-12-12 12:07:25

+1

@JoelDavis,不,你至少需要清除整个环境,给一些envvars(PATH,HOME ...)提供理智的默认值,如果需要,在消毒后保存一些env vars(TERM,DISPLAY,LANG。 ..)确保fds 0,1,2是开放的...基本上做什么sudo。即使那样,我也不会去那里。如果可以避免,请不要在特权升级上下文中调用shell。请注意,'ls'可以用它的环境做一些奇特的事情,所以即使没有'system()',你也应该清理环境。在使用setuids时,您希望尽量减少以root身份执行的操作(通常不执行命令)。 – 2014-12-12 14:07:52

+3

@JoelDavis:不,即使使用完整路径,仍然存在问题。如果你使用'/ bin/ls',用户可以将'/'添加到'$ IFS',导致shell将'/ bin/ls'分割为'bin'和'ls'。现在,在我的答案中,当前目录中名为'bin'的可执行文件可以与'ls'做同样的事情。 – cuonglm 2014-12-12 16:39:12

0

除了提及的system()安全问题之外,产生的进程继承了主程序的环境。当使用suid时,例如当调用进程设置LD_LIBRARY_PATH-环境变量时,这可能是非常有问题的。

exec()-家庭调用程序可以调用exec()之前调用程序所需的(和安全)所需的环境设置。

当然,由system()调用的shell本身可能有安全问题。