setuid 不适用于标准用户帐户

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;
}

我的用户帐户是“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”。

相关内容