setuid位为何工作不一致?


8

我写了代码:

// a.c
#include <stdlib.h>
int main () {
  system("/bin/sh");
  return 0;
}

用命令编译:

gcc a.c -o a.out

在其上添加setuid位:

sudo chown root.root a.out
sudo chmod 4755 a.out

在Ubuntu 14.04上,当我以普通用户身份运行时,我获得了root特权。

但是在Ubuntu 16.04上,我仍然拥有当前用户的shell。

为什么不同?

Answers:


9

所发生的变化是,/bin/sh变得bash要么留下来,要么留下dash来了,并附加了一个-p模仿bash行为的标志。

Bash要求该-p标志不要放弃setuid特权,如其手册页所述

如果外壳程序以有效用户(组)ID不等于真实用户(组)ID的方式启动,并且未提供-p选项,则不会读取启动文件,并且不会从环境中继承外壳程序功能,SHELLOPTS ,BASHOPTS,CDPATH和GLOBIGNORE变量(如果它们出现在环境中)将被忽略,并且有效用户ID设置为实际用户ID。如果在调用时提供了-p选项,则启动行为是相同的,但是不会重置有效用户ID。

以前,dash并不关心此事,而是允许setuid执行(不采取任何措施来阻止它)。但是Ubuntu 16.04的dash联机帮助页描述了一个附加选项,类似于bash

-p priv
如果不匹配有效的uid,请不要尝试重置它。默认情况下未设置此选项,以帮助避免 setuid根程序通过system(3)或popen(3)错误使用

这个选项在上游(可能不会对建议的补丁*起作用)或Debian 9中都不存在,但在自2018年以来获得补丁的Debian Buster中存在。

注意:正如StéphaneChazelas所解释的那样,调用它为时已晚"/bin/sh -p"system()因为system()运行提供的所有内容/bin/sh,因此setuid已被删除。derobert的答案在之前的代码中解释了如何处理此问题system()

*历史更多的细节在这里那里


system("bash -p")运行,sh -c "bash -p"因此特权在bash执行时已被删除。
斯特凡Chazelas

哦好吧。无论如何,我将删除由derobert的回答更好处理的“解决方案”部分。
AB


看起来像是wily(15.10)中的Ubuntu固定破折号。bugs.launchpad.net/ubuntu/+source/dash/+bug/1215660
Mark Plotnick

1
请注意,Debian曾经使用相反的方法。它曾经修补bash,以便放弃特权。是否有改进是有争议的。现在,想要system()使用提升的特权(当然,他们不应该首先这样做)进行操作的人最终在做一个setresuid()before会更糟,因为那时shell不知道它是suid,所以不会激活不信任其环境的模式。
斯特凡Chazelas

8

出于某种原因,shell可能会在启动时将其有效用户ID更改回实际用户ID。您可以通过添加以下内容进行验证:

/* needs _GNU_SOURCE; non-Linux users see setregid/setreuid instead */
uid_t euid = geteuid(), egid = getegid();
setresgid(egid, egid, egid);
setresuid(euid, euid, euid);

在你之前system()。(实际上,即使在Linux上,您也可能只需要设置真实的;保存的就可以单独使用。这只是调试的蛮力。根据您为什么要设置set-id,您当然可能需要将真实ID也保存在某个位置。)

[此外,如果这不仅仅是学习setid的工作原理,那么还有很多安全问题值得关注,尤其是在调用shell时。例如,有许多环境变量会影响外壳的行为。sudo尽可能使用现有的方法。]


在您提供了如何避免该问题的同时,我着重介绍了它为什么会发生变化。+1
AB

当然,如果你真的需要的地方保存真正的标识,保存的ID是一个非常方便的地方去做。特别是如果有效ID不是root:root。
凯文,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.