
看看這個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」。