![setuid 不适用于标准用户帐户](https://linux22.com/image/782749/setuid%20%E4%B8%8D%E9%80%82%E7%94%A8%E4%BA%8E%E6%A0%87%E5%87%86%E7%94%A8%E6%88%B7%E5%B8%90%E6%88%B7.png)
看看这个 C 程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
printf("res=%d\n", setuid(1001));
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
return 0;
}
我的用户帐户是“test”(id 1000)。我还有第二个用户帐户:“test2”(id 1001)。
这是我所做的:
gcc test.c -o ./a.out
sudo chown test2 ./a.out
sudo chmod u+s ./a.out
现在,如果我启动./a.out,会发生以下情况:
UID: 1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID: 1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
我不明白为什么在第二部分我没有看到 uid=1001......
我尝试过这个:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
printf("res=%d\n", setuid(0)); // <- Here is the change
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
return 0;
}
gcc test.c -o ./a.out
sudo chown root ./a.out
sudo chmod u+s ./a.out
这是我运行./a.out 时得到的结果:
UID: 1000
EUID: 0
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID: 0
EUID: 0
uid=0(root) gid=1000(test) groups=1000(test),...
现在,它起作用了。
所以我不明白为什么 setuid 对 test2 用户不起作用,而对 root 用户却起作用......
任何想法 ?
谢谢
答案1
这里有几个问题导致了这种混乱。Setuid 在两种情况下都有效,只是它的工作方式与你想象的不同。首先,存在真实有效的用户 ID 问题。根据setuid(2) 手册页
如果调用进程具有特权(更准确地说:如果该进程
CAP_SETUID
在其用户命名空间中具有该能力),则还会设置真实的 UID 和保存的设置用户 ID。
这也意味着调用进程不是有CAP_SETUID
能力(这是普通用户的情况),那么 UID 将不是更改的,仅限 EUID。
因此我们继续进行system()
调用。调用system("anything")
相当于调用以下内容:
execl("/bin/sh", "sh", "-c", "anything", (char *) NULL);
因此它会产生/bin/sh
,提供参数,以便新 shell 执行您的命令。我假设您使用 bash 作为 shell,因为 bash 的工作方式是
如果 shell 启动时的有效用户(组)id 不等于真实用户(组)id,并且未提供 -p 选项,则 (...) 将有效用户 id 设置为真实用户 id。
因此,如果您的进程不具备此CAP_SETUID
功能,它将不会更改真实用户 ID,而只会更改有效 UID。然后,当 bash 生成时,它会删除有效 UID,因为 UID 不等于 EUID。您可以通过调用来确认这一点
system("touch /tmp/blah")
并添加
FILE *file = fopen("/tmp/blah2", "w+");
fclose(file);
到您的程序。您将看到 的所有者/tmp/blah
将是“test”(因为 EUID 将在 shell 执行时被删除),但/tmp/blah2
将属于“test2”。