Answers:
该setuid位可以在一个可执行文件进行设置,以便在运行时,该程序将具有文件,而不是真正的用户的所有者的权限,如果它们是不同的。这是有效 uid(用户id)和实际 uid 之间的区别。
一些常见的实用程序(例如passwd
)是root拥有的,并且出于必要(passwd
需要/etc/shadow
通过root才能读取的访问权限)进行这种配置。
这样做的最佳策略是立即以超级用户身份执行所有操作,然后降低特权,以便在运行root时不太可能发生错误或滥用。为此,将流程的有效 uid设置为其实际uid。在POSIX C中:
#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void report (uid_t real) {
printf (
"Real UID: %d Effective UID: %d\n",
real,
geteuid()
);
}
int main (void) {
uid_t real = getuid();
report(real);
seteuid(real);
report(real);
return 0;
}
如果在POSIX系统上通常使用相关功能,则这些功能应该在大多数高级语言中具有等效功能:
getuid()
:获取真实的 uid。geteuid()
:获取有效的 uid。seteuid()
:设置有效的 uid。对于最后一个不适合实际uid的东西,您不能做任何事情,除非在可执行文件上设置了setuid位。因此,请尝试编译gcc test.c -o testuid
。然后,您需要具有特权:
chown root testuid
chmod u+s testuid
最后一个设置setuid位。如果现在./testuid
以普通用户身份运行,则默认情况下该进程将以有效的uid 0,root运行。
每个平台的平台不同,但是在Linux上,需要解释器(包括字节码)的东西不能利用setuid位,除非在解释器上设置了setuid位(这是非常愚蠢的)。这是一个模仿上面的C代码的简单perl脚本:
#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);
print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n";
真正的* nixy根源,perl内置了用于有效uid($>
)和real uid($<
)的特殊变量。但是,如果您尝试相同的操作chown
并chmod
与已编译的可执行文件(来自C,前面的示例)一起使用,则不会有任何区别。脚本无法获得特权。
答案是使用setuid二进制文件执行脚本:
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[]) {
if (argc < 2) {
puts("Path to perl script required.");
return 1;
}
const char *perl = "perl";
argv[0] = (char*)perl;
return execv("/usr/bin/perl", argv);
}
gcc --std=c99 whatever.c -o perlsuid
然后编译它chown root perlsuid && chmod u+s perlsuid
。现在,无论谁拥有它,都可以使用有效uid为0的任何 perl脚本执行。
类似的策略将适用于php,python等。 但是...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
请不要丢下这种东西。 最有可能的是,您实际上希望将脚本的名称编译为绝对路径,即,将所有代码替换main()
为:
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
他们必须确保/opt/suid_scripts
其中的所有内容对于非root用户都是只读的。否则,有人可以用交换任何东西whatever.pl
。
此外,请注意,许多脚本语言都允许环境变量更改其执行脚本的方式。例如,环境变量可能导致调用者提供的库被加载,从而允许调用者以root身份执行任意代码。因此,除非您知道解释器和脚本本身对所有可能的环境变量都具有鲁棒性,否则请不要这样做。
允许非root用户以root用户身份运行脚本的一种更安全的方法是添加sudo规则并让该用户运行sudo /path/to/script
。Sudo剥离了大多数环境变量,并且还允许管理员精细地选择谁可以运行命令以及使用哪些参数。请参阅如何在没有密码提示的情况下以root用户身份运行特定程序?举个例子
-privileged
外壳”和“负责脚本” 的概念不熟悉。您是否要对壳做其他回答?我当然不能答应你;)这样做的理由sudo
不多-如果可能的话,这是一个更好的选择-我将其写成通用答案,源于关于php中系统密码身份验证的早期问题。
sudo
-我认为sudo
作为精神病,因为它假装不为root
。您甚至可以将Shell Globs放入其中sudoers
。su
就脚本编写目的而言,我认为更好,尽管sudo
对实时应用程序也有好处。但是,它们都不像带有#!/bin/ksh -p
爆炸线的脚本或suid ksh
in 那样明确$PATH
。
是的,您可以,但这可能是一个非常糟糕的主意。通常,您不会直接在可执行文件上设置SUID位,而是使用sudo(8)或su(1)来执行它(并限制可以执行它的人)
但是请注意,允许普通用户以root用户身份运行程序(尤其是脚本!)存在许多安全问题。他们中的大多数人都必须这样做,除非该程序经过特别且非常仔细地编写以处理该问题,恶意用户才可以使用它轻松获得root shell。
因此,即使您要这样做,最好先清理要作为根用户运行的程序的所有输入,包括其环境,命令行参数,STDIN及其处理的任何文件,来自网络连接的数据它会打开,等等。这很难做,甚至做对也很难。
例如,您可以允许仅使用编辑器编辑某些文件的用户。但是编辑器允许外部帮助程序的命令执行或挂起,从而使用户可以根据自己的意愿执行root shell。或者,您可能正在执行对您来说非常安全的shell脚本,但是用户可以使用修改后的$ IFS或其他环境变量来运行它,从而完全改变其行为。或者可能是应该执行“ ls”的PHP脚本,但是用户在修改$ PATH后运行它,因此使其运行名为“ ls”的shell。或其他数十种安全问题。
如果该程序确实必须以root身份执行其部分工作,则可能最好对其进行修改,使其以普通用户身份运行,而只是通过suid helper程序(在尽可能多地清除后)执行绝对需要root的部分。
请注意,即使您完全信任所有本地用户(例如,您给他们提供了root密码),也不是一个好主意,因为任何一个用户(例如,在线运行PHP脚本,弱密码,或者不管如何)突然让远程攻击者完全破坏了整个计算机。
man sh
,即使这样做也无法执行command -p env -i ...
。毕竟很难做到。尽管如此,如果出于正确的目的正确地完成了该操作,那么它suid
对于使用有能力的解释器要比使用su
或更好,sudo
因为任何一个的行为都始终在脚本的控制范围之外(尽管su
不太可能将曲线球扔向脚本)少一点疯狂)。捆绑整个软件包是唯一可以肯定的选择-最好确保全部。
-privileged
外壳程序,则它们的行为会有所不同;如果从负责的脚本中调用,则也可以通过suid root
这种方式安全地进行调用。不过,恐怕负责任脚本的概念在现实世界中有点像做梦。无论如何,可能值得一提的是,在提高权限的情况下,尽可能避免所有形式的写入有多么重要……