Setuid

suid 執行檔沒有提升權限,但sudo 確實提升了

  • February 25, 2019

我正在嘗試編寫一個程序,該程序將在不提示輸入密碼或確認的情況下快速關閉筆記型電腦。筆記型電腦執行的是 Linux,特別是 Manjaro。

為此,我正在嘗試製作一個使用固定參數執行的setuid執行檔(並且還生成一個 shell)。我在這裡只是為了保持程序簡短,從安全的角度來看這是一個糟糕的主意。downnow``shutdown``system

// downnow.c
#include <stdlib.h>

int main() {
 system("shutdown --no-wall --halt now");
 return 0;
}

然後我編譯downnow,把它移到/bin,改變它的所有者並給它 setuid 和 setgid 權限:

$ sudo chown root /bin/downnow 

$ sudo chgrp root /bin/downnow 

$ sudo chmod u+s /bin/downnow 

$ sudo chmod g+s /bin/downnow 

但是,當我嘗試以downnow非特權使用者身份執行時,它無法與 systemd 通信。

$ downnow 
Failed to halt system via logind: Interactive authentication required.
Failed to talk to init daemon.

ch{own,grp,mod}我在ing之前和之後收到相同的消息。

stat``/bin/downnow在和上報告相同的權限/usr/bin/sudo

$ stat /bin/downnow 
 File: /bin/downnow
 Size: XXXX        Blocks: XX         IO Block: XXXX   regular file
Device: XXXXX/XXXXX Inode: XXXXXX     Links: 1
Access: (6755/-rwsr-sr-x)  Uid: (    0/    root)   Gid: (    0/    root)

$ stat /usr/bin/sudo
 File: /usr/bin/sudo
 Size: XXXXXX      Blocks: XXX        IO Block: XXXX   regular file
Device: XXXXX/XXXXX Inode: XXXXXXX     Links: 1
Access: (4755/-rwsr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)

為什麼在完美執行downnow時可能無法提升其特權?sudo

可能是因為沒有setuid(2)來電。這是這樣一個電話的前後對比:

#include <err.h>
#include <stdio.h>
#include <unistd.h>

#define GETUIDS(p) (p[0]=getuid(),p[1]=geteuid())

int main(void)
{
   uid_t ids[2];
   GETUIDS(ids);
   fprintf(stderr, "real %lu effective %lu\n", (unsigned long) ids[0],
           (unsigned long) ids[1]);
   if (setuid(0) == -1)
       err(1, "setuid failed");
   GETUIDS(ids);
   fprintf(stderr, "real %lu effective %lu\n", (unsigned long) ids[0],
           (unsigned long) ids[1]);
   return 0;
}

此外,您的包裝器也不是很安全;希望它不會逃到多使用者系統。避免不必要的 shell 呼叫可能會更安全(除非你喜歡 shellshock 類型的漏洞或重複環境變數的奇怪處理,比如說bash……),而是使用exec(3)呼叫來直接替換你的程序shutdown

#include <err.h>
#include <unistd.h>

int main()
{
   if (setuid(0) == -1)
       err(1, "setuid failed");
   //execl("/usr/bin/echo", "echo", "--no-wall", "--halt", "now",
   execl("/usr/sbin/shutdown", "shutdown", "--no-wall", "--halt", "now",
         (char *) 0);
   err(1, "exec failed");
   return 1;
}

之後幾次意外關機…

centos7# make badcode
cc     badcode.c   -o badcode
centos7# mv badcode /badcode
centos7# chmod u+s /badcode
centos7# su - jhqdoe
Last login: Thu Jan 10 01:39:03 UTC 2019 on pts/0
[jhqdoe@centos7 ~]$ /badcode
Connection to 192.168.99.2 closed by remote host.
Connection to 192.168.99.2 closed.

設置權限位僅允許您的應用程序使用該setuid呼叫,並且它本身不會更改您的權限。為此,您需要將 uid 設置為setuid(uid_t uid). 有關詳細資訊,請參見手冊頁:https ://linux.die.net/man/2/setuid

您可以使用geteuid()獲取目前有效的uid(即具有setuid 權限的文件的所有者)。參見手冊頁:https ://linux.die.net/man/2/geteuid

例子:

// downnow.c
#include <stdlib.h>
#include <unistd.h>

int main() {
 setuid(geteuid());
 system("shutdown --no-wall --halt now");
 return 0;
}

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