从setuid root C程序调用脚本 - 脚本不以root身份运行

时间:2009-02-17 10:18:36

标签: c scripting setuid

我需要以root身份运行bash脚本(无密码sudo或su不可行),因为你无法在Linux中设置脚本,我想从可执行文件中调用它并使 it setuid:

$ cat wrapper.c
int main(void)
{
        system("/bin/bash ./should_run_as_root.sh");
}
$ gcc -o wrapper wrapper.c
$ sudo chown root wrapper
$ sudo chmod ug+s wrapper
$ ll wrapper
-rwsr-sr-x 1 root users 6667 2009-02-17 11:11 wrapper
$

这样做 - 就像正确运行脚本一样 - 但脚本以执行“./wrapper”的用户身份运行。

为什么呢?以及如何正确实现这个?

谢谢!

5 个答案:

答案 0 :(得分:43)

由于可执行文件上的suid位仅更改了可执行文件将运行的有效UID(EUID),而不是getuid()返回的实际UID(RUID),以及对可执行文件的限制suid解释脚本(任何以“#!”开头的可执行文件),像bash这样的一些shell作为额外的安全措施会在这种情况下将EUID设置回RUID,你需要在执行脚本之前,请在C代码中使用调用setuid(0)

请参阅mansetuidseteuidgetuid的{​​{1}}页面,了解真实有效UID的确切语义。< / p>

警告)当然,这是适当的一点,在许多Unix系统,shell和解释器中对geteuid脚本的限制是有原因的,这是如果脚本在执行时对其输入和环境状态进行消毒并不十分谨慎,则它们很危险,可以利用它来进行安全性升级。这样做时要非常小心。尽可能严格地设置对脚本和包装器的访问权限,只允许执行此特定脚本,并在启动脚本之前清除C程序中的环境,设置suid等环境变量准确地包含正确顺序所需的内容以及没有可写给其他人的目录。

答案 1 :(得分:6)

这里要注意的另一件事是这里的限制来自bash而不是* nix系统本身。 Bash实际上对SUID脚本进行了验证,只能使用EUID root执行它们。如果你采用旧壳,你通常会得到你想要的东西。例如,sh不进行这种验证:

$ cat wrapper.c
int main(void)
{
            system("/bin/sh -c whoami");
}

$ ls -l wrapper
-rwsr-sr-x 1 root users 8887 Feb 17 14:15 wrapper
$ ./wrapper
root

使用bash:

$ cat wrapper.c
int main(void)
{
            system("/bin/bash -c whoami");
}

$ ls -l wrapper
-rwsr-sr-x 1 root users 8887 Feb 17 14:18 wrapper
$ ./wrapper
skinp

尽管如此,汤姆的回答通常是为SUID根程序制作包装器的方法

答案 2 :(得分:3)

在脚本中添加setuid(0)并进行编译。它应该在此之后工作。

$ cat wrapper.c 
int main(void) 
{ 
        setuid(0);
        system("/bin/bash ./should_run_as_root.sh"); 
} 
$ gcc -o wrapper wrapper.c 
$ sudo chown root wrapper 
$ sudo chmod ug+s wrapper 
$ ll wrapper 
-rwsr-sr-x 1 root users 6667 2009-02-17 11:11 wrapper 
$ 

答案 3 :(得分:1)

这些例子非常不安全,任何拥有两点知识的人都可以运行他们想要的任何程序作为setuid用户。

除非您首先清理环境,否则永远不要通过shell,此处显示的大多数示例都容易在运行之前设置IFS和PATH。

答案 4 :(得分:0)

为什么sudo不可行?它避免了肆虐的安全漏洞,例如:

bash-3.2$ cat test
#!/bin/bash
echo ima shell script durp durp
bash-3.2$ chmod +x test
bash-3.2$ ./test
heh heh
bash-3.2$ 

由于环境没有得到适当的消毒,例如在这种情况下:

export echo='() { builtin echo heh heh; }'

sudo清理这个案例,也许还有其他边缘案例和陷阱,这些案例和陷阱很难不写入自定义的suid包装器。