Answers:
所发生的变化是,/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()
使用提升的特权(当然,他们不应该首先这样做)进行操作的人最终在做一个setresuid()
before会更糟,因为那时shell不知道它是suid,所以不会激活不信任其环境的模式。
出于某种原因,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
尽可能使用现有的方法。]
system("bash -p")
运行,sh -c "bash -p"
因此特权在bash
执行时已被删除。