setuid 不适用于标准用户帐户

Bob*_*421 0 setuid

看看这个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 用户......

任何想法 ?

谢谢

Lac*_*cek 5

这里发生了一些事情,导致了这种混乱。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”。