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 和已儲存的 set-user-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」。

相關內容