看看这个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;
}
Run Code Online (Sandbox Code Playgroud)
我的用户帐户是“test”(id 1000)。我有第二个用户帐户:“test2”(id 1001)。
这是我所做的:
gcc test.c -o ./a.out
sudo chown test2 ./a.out
sudo chmod u+s ./a.out
Run Code Online (Sandbox Code Playgroud)
现在,如果我启动 ./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),...
Run Code Online (Sandbox Code Playgroud)
我不明白为什么我在第二部分看不到 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
Run Code Online (Sandbox Code Playgroud)
这是我运行 ./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),...
Run Code Online (Sandbox Code Playgroud)
现在,它起作用了。
所以我不明白为什么 setuid 不适用于 test2 用户而适用于 root 用户......
任何想法 ?
谢谢
这里发生了一些事情,导致了这种混乱。Setuid 在这两种情况下都可以工作,只是它不像你想象的那样工作。首先,存在真实有效的用户ID问题。根据setuid(2) 手册页
如果调用进程有特权(更准确地说:如果进程
CAP_SETUID
在其用户命名空间中具有该功能),则还会设置真实 UID 和保存的 set-user-ID 。
这也意味着,如果调用进程没有这个能力CAP_SETUID
(你的普通用户就是这种情况),那么UID不会改变,只会改变EUID。
所以我们继续通话system()
。调用system("anything")
相当于调用以下内容:
execl("/bin/sh", "sh", "-c", "anything", (char *) NULL);
Run Code Online (Sandbox Code Playgroud)
因此它会生成/bin/sh
,并提供参数,以便新的 shell 执行您的命令。我假设你使用 bash 作为你的 shell,因为 bash 的工作原理是这样的
如果 shell 启动时有效用户(组)id 不等于真实用户(组)id,并且未提供 -p 选项,则 (...) 有效用户 id 设置为真实用户 id。
所以如果你的进程没有这个CAP_SETUID
能力,它不会改变真实的用户ID,只会改变有效的UID。然后,当 bash 生成时,它会删除有效 UID,因为 UID 不等于 EUID。您可以通过致电确认这一点
system("touch /tmp/blah")
Run Code Online (Sandbox Code Playgroud)
并添加
FILE *file = fopen("/tmp/blah2", "w+");
fclose(file);
Run Code Online (Sandbox Code Playgroud)
到你的程序。您将看到 的所有者/tmp/blah
将是“test”(因为 EUID 将在 shell 执行时被删除),但/tmp/blah2
将属于“test2”。