Linux

為什麼 setuid() 不適用於非 root 使用者?

  • October 24, 2019

我遇到了關於setuid()setuid 位的奇怪行為。

suid 位和 setuid() 似乎沒有按預期工作。我期待一個帶有 +s 並由 uid 1001 擁有的二進製文件,該二進製文件setuid(1001)從任何 uid 呼叫,並在呼叫後假定 uid 1001。然而,這似乎只有在以下任一情況下才有效:

  1. +s 未設置且呼叫使用者為 root
  2. +s 已設置並且二進製文件屬於 root

我期待我忽略了一個細節,但是我找不到我的錯誤。

最終目標是擁有一個可以從任何使用者呼叫的二進製文件並假設一個固定的 uid。我不希望它由 root 擁有,而是由應該假定其身份的使用者擁有(主要是因為這是一個關於堆棧粉碎的練習,並且允許 priv esc)。

我創建了一個最小的範例來確定我的問題,這裡是:

考慮 test.c:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
int main() {
       int t = setuid(1001);
       if (t < 0) {
               perror("Error with setuid() - errno " + errno);
       }
       else {
               printf("did work fine, look who I am:.\n");
               system("/bin/bash -c whoami");
       }
}

此外,passwd 在相關部分看起來像這樣:

test1:x:1000:1000::/home/test1:/bin/sh
test2:x:1001:1001::/home/test2:/bin/sh

現在,考慮這個輸出:

root@kali:/tmp/test# ls -la
total 12
drwxr-xr-x  2 root root 4096 Oct 24 09:53 .
drwxrwxrwt 18 root root 4096 Oct 24 09:52 ..
-rw-r--r--  1 root root  304 Oct 24 09:51 test.c
root@kali:/tmp/test# gcc test.c -o test
root@kali:/tmp/test# ./test
did work fine, look who I am:.
test2
root@kali:/tmp/test# chown test2:test2 test
root@kali:/tmp/test# ./test
did work fine, look who I am:.
test2
root@kali:/tmp/test# chmod +s test
root@kali:/tmp/test# ./test
did work fine, look who I am:.
root
root@kali:/tmp/test# su test1
$ ./test
did work fine, look who I am:.
test1
$ 

如您所見,沒有顯示錯誤,但未正確假定所需的 uid。雪上加霜,考慮一下:

root@kali:/tmp/test# chown root:root test
root@kali:/tmp/test# chmod +s test
root@kali:/tmp/test# ./test
did work fine, look who I am:.
test2
root@kali:/tmp/test# su test1
$ ./test
did work fine, look who I am:.
test2

所以我想我的問題是:我做錯了什麼?為什麼setreuid()有效而setuid()無效?

我嘗試過的其他事情:使用execve(),在 ubuntu 18.04 下複製,使用 /bin/sh 而不是 /bin/bash。

for 的定義setuid()有點奇怪,因為它根據應用程序是否為 setuid root 執行不同的操作。(有充分的理由,但乍一看不一定是顯而易見的原因。)

從根本上說,您需要在呼叫之前放棄您的原始使用者身份system(),因為某些 shell 會盡力拒絕接受 setuid。這是您的程式碼的修改版本;在有和沒有註釋行的情況下執行它以查看差異。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>

int main() {
   int t;

   printf("before, geteuid() returned %d\n", geteuid());
   printf("before, getuid() returned %d\n", getuid());

   t = setuid(geteuid());
   if (t < 0) {
       perror("Error with setuid() - errno " + errno);
       exit(1);
   }

   printf("after, geteuid() returned %d\n", geteuid());
   printf("after, getuid() returned %d\n", getuid());

   // setreuid(geteuid(), geteuid());

   printf("finally, geteuid() returned %d\n", geteuid());
   printf("finally, getuid() returned %d\n", getuid());

   printf("did work fine, look who I am:\n");
   system("/bin/bash -c whoami");
}

引用自:https://unix.stackexchange.com/questions/548480