【问题标题】:Calling a script from a setuid root C program - script does not run as root从 setuid root C 程序调用脚本 - 脚本不以 root 身份运行
【发布时间】:2010-10-08 01:23:17
【问题描述】:

我需要以 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”的用户身份运行。

为什么?以及如何正确实现这一点?

谢谢!

【问题讨论】:

标签: c scripting setuid


【解决方案1】:

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

查看setuidseteuidgetuidgeteuidman 页面,了解真实有效的 UID 的确切语义。

(WARNING) 当然,这是一个恰当的点,即在许多 Unix 系统、shell 和解释器中对 suid 脚本的限制是有原因的,即如果该脚本在执行时对清理其输入和环境状态不是很小心,它们很危险,可以被用于安全升级。因此,在执行此操作时要非常小心。尽可能严格地设置对脚本和包装器的访问,只允许您打算执行的这个非常特定的脚本,并在启动脚本之前清除 C 程序中的环境,设置环境变量,例如 PATH 以包含以正确的顺序确切地需要什么,并且没有可写给其他人的目录。

【讨论】:

  • 保存原始UID并在程序结束时重新设置是否更安全?
  • 如果你的程序在脚本完成后做其他事情,不需要这个,那么可以。如果你所做的只是返回,那么这并不重要。在典型情况下,脚本包装器将使用 exec 而不是 system(...),因此无论如何,在脚本结束后包装器将不再运行。
  • @TomAlsberg 你知道确认这个“自动重置 uid”的 Bash 行为的源代码/文档吗?
【解决方案2】:

这里要注意的另一件事是,这里的限制来自 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

不过,Tom 的回答通常是为 SUID 根程序制作包装器的方法

【讨论】:

  • 你是对的,当然。 Linux 通过在 exec 调用中检查可执行文件的模式来处理限制 suid 脚本,这种限制是某些解释器的额外安全措施。不知道为什么我在写这篇文章时会感到困惑。我更正了我的答案。谢谢!
  • 当然我错过了在 OP 的代码中,他实际上并没有用 # 执行脚本!解释器规范,但是直接调用 bash,所以内核的限制在这种情况下无论如何都不会适用 - 但内核确实有这个限制 #!带有 suid 文件模式的脚本。
  • 不起作用(不再)。一方面,man system 表示Do not use system() from a program with set-user-ID or set-group-ID privileges, because ...。另一方面,这适用于 Ubuntu 14.04,但不适用于 Ubuntu 16.04。
  • @anumi 正如我所提到的,这将取决于 shell。 ubuntu 中的 /bin/bash 实际上与 dash 相关联。看起来这是在 14.04 和 16.04 之间修复的,可能在 bugs.launchpad.net/ubuntu/+source/dash/+bug/1215660 中。其他发行版/版本上的其他 shell 将有不同的行为。
【解决方案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 
$ 

【讨论】:

  • 不要在具有 set-user-ID 或 set-group-ID 权限的程序中使用 system(),因为某些环境变量的奇怪值可能会被用来破坏系统完整性。请改用 exec(3) 系列函数,但不要使用 execlp(3) 或 execvp(3)。事实上,system() 在 /bin/sh 是 bash 版本 2 的系统上无法从具有 set-user-ID 或 set-group-ID 特权的程序正常工作,因为 bash 2 在启动时会放弃特权。 (Debian 使用修改后的 bash,它在作为 sh 调用时不会这样做。)
【解决方案4】:

这些示例非常不安全,并且允许具有两种知识的任何人以 setuid 用户身份运行任何他们想要的程序。

除非您首先清理环境,否则切勿通过 shell,此处显示的大多数示例都容易受到在运行之前设置 IFS 和 PATH 的影响。

【讨论】:

    【解决方案5】:

    为什么 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 包装器。

    【讨论】:

    • man sudo:“通过 sudo 运行 shell 脚本会暴露相同的内核错误,使 setuid shell 脚本在某些操作系统上不安全......” 制作 suid 脚本的标准包装器可能是最好的,但是这样做你认识一个吗? sudo 在这里是不安全的,因为它依赖内核通过 /dev/fd/... 传递脚本!
    猜你喜欢
    • 2011-10-30
    • 2014-04-29
    • 2023-03-16
    • 2014-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    相关资源
    最近更新 更多