天天看點

Linux setuid與權能介紹 轉載

目前有個項目要求 Apache以other的身份模拟root 使用者權限來執行!

目前想到的就是 fakeroot 以及S位,在此bing轉載了一篇setuid相關部落格,

Linux下對于一個檔案我們熟悉的權限有rwx,表示讀寫和執行。

今天介紹的是S位,稱為setuid位。

setuid

從一個例子開始

ping指令常常用來測試網絡是否通暢,我們看看ping的權限是啥:

1
2      
$ ll /bin/ping
-rwsr-xr-x 1 root root 55724 6月   15  2016 /bin/ping*      

rws!第三個s是啥東西?将其删掉:

1
2
3
4      
$ sudo chmod u-s   /bin/ping
$ ll /bin/ping
-rwxr-xr-x 1 root root 55724 6月 15 2016 /bin/ping*      

發現就無法ping了,因為沒有打開socket的權限。而當我們又将s位給它的時候,又可以ping了。

Linux setuid與權能介紹 轉載

Setuid位的作用

 Setuid 是讓程式在執行過程中擁有檔案擁有者的權限。

用passwd指令舉例來說,passwd指令也一樣設定了setuid位。

為啥這個需要呢?我們知道linux中賬号與密碼分别存放在 /etc/passwd與 /etc/shadow,而後者的權限如下:

1
2
3
4
5
6      
$ which passwd
/usr/bin/passwd
$ ll /usr/bin/passwd
-rwsr-xr-x 1 root root 54256 3月 29 2016 /usr/bin/passwd*
ll /etc/shadow
-rw-r----- 1 root shadow 978 7月 11 11:39 /etc/shadow      

當我們使用passwd指令修改自己的密碼(非root)的時候,顯然是不行的,因為無法對/etc/shadow進行修改。而setuid位此時就可以幫到忙啦!

此時程式暫時得到了檔案擁有者root的權限,于是就可以修改shadow檔案了。

Linux中設定Setuid的程式

要找出所有設定setuid的程式,隻需要使用下面任意一條指令

1
2      
find / -perm /u=s
find / -perm -4000      

結果如下:

/bin/ping /bin/mount /bin/su /bin/fusermount /bin/ntfs-3g /bin/umount /usr/sbin/pppd /usr/lib/snapd/snap-confine /usr/lib/i386-linux-gnu/ubuntu-app-launch/oom-adjust-setuid-helper /usr/lib/i386-linux-gnu/oxide-qt/chrome-sandbox /usr/lib/openssh/ssh-keysign /usr/lib/eject/dmcrypt-get-device /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/lib/policykit-1/polkit-agent-helper-1 /usr/bin/chsh /usr/bin/newgrp /usr/bin/arping /usr/bin/traceroute6.iputils /usr/bin/gpasswd /usr/bin/chfn /usr/bin/passwd /usr/bin/pkexec /usr/bin/sudo

權能

在早先的Linux系統中,執行特權操作的時候,核心對于有效的user ID為0 (超級使用者root)的時候不進行檢查,而對user ID非0的情況進行許可檢查。主要是effective UID、effective GID。

Linux 2.2起,Linux有了權能的概念。當執行特權操作的時候,可以檢視是否有相應的權能。

權能可以讓普通使用者完成超級使用者可以做的事情。

繼續上一個例子

在上一個例子中,我們把setuid位取消了,然後發現ping指令不行了,現在我們用setcap賦予cap_new_raw的權能:

Linux setuid與權能介紹 轉載

發現可以Ping!并且沒有setuid位!

權能的作用

權能相當于規範了你這個程式所擁有的權限。相當于特權的需求的細化。

比如ping指令需要有socket的連接配接,需要CAP_NET_RAW的權能,其說明如下:

CAP_NET_RAW
  • Use RAW and PACKET sockets;
  • bind to any address for transparent proxying.

現在linux的權能有好多,可以見​​man手冊​​

上面列印出了那麼多的setuid位的程式,比如ping 隻需要CAP_NET_RAW權能即可

其它部分需要的權能如下:

程式 需要的權能
/bin/ping CAP_NET_RAW
/bin/mount CAP_SYS_ADMIN
/bin/su CAP_DAC_OVERRIDE,CAP_SETGID,CAP_SETUID
/bin/fusermount CAP_SYS_ADMIN
/bin/umount CAP_SYS_ADMIN
/usr/bin/passwd CAP_CHOWN ,CAP_DAC_OVERRIDE ,CAP_FOWNER

權能三項與權能變換

在linux中,權能有如下三種:

  • effective:目前有效的權能,執行某特權操作時,作業系統檢查cap_effective的對應位是否有效,而不再是檢查程序的有效UID是否為0。
  • permitted:目前程序所有能使用的能力,effective包含于permitted
  • inheritable:可以被繼承的能力

上面我們設定ping的權能就是設定了effective和permitted。

linux程式可以調用子程式,其權能變換如下:

During an execve, the kernel calculates the new capabilities of the process using the following algorithm:

1

2

3

P'(permitted) = (P(inheritable) & F(inheritable)) (F(permitted) & cap_bset)

P'(effective) = F(effective) ? P'(permitted) : 0

P'(inheritable) = P(inheritable)

  • P:denotes the value of a thread capability set before the execve(2)
  • P':denotes the value of a capability set after the execve(2)
  • F:denotes a file capability set
  • cap_bset:is the value of the capability bounding set (described below).

權能變換小程式

實作一個程式其滿足以下的功能:

  • 能夠永久的删除其子程序的某個權能。
  • 能暫時性的删除其子程序的某個權能。
  • 能讓上面被暫時性删除的權能重新獲得

以ping為例,我們知道ping需要的權能為cap_net_raw,為了能在execve後執行ping,即execve中的ping有cap_net_raw權能,根據上面的變換規則:

  • 設定/bin/ping權能為cap_net_raw+ei,使得可以繼承
  • sudo setcap cap_net_raw+ei /bin/ping
  • 設定目前的程序的權能有cap_net_raw+i權能

此外,我們的程式先進行fork(),在子程序中調用execve執行ping,因為不用fork執行ping後會退出程式。

fork()建立子程序,那麼子程序繼承父程序的所有能力,是以沒必要重新設定fork後的權能。

為了讓有更改權能的能力,首先以sudo運作程式,賦予 CAP_SETUID, CAP_SETGID, CAP_SETPCAP權能,然後setuid和gid為1000。

  • sudo apt-get install libcap-dev
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#undef _POSIX_SOURCE

extern int errno;

void whoami() {
printf("uid=%i  euid=%i  gid=%i\n", getuid(), geteuid(), getgid());
}


void listCaps(){
cap_t caps = cap_get_proc();
ssize_t y = 0;
printf("The Process %d was give capabilities %s\n",(int) getpid(), cap_to_text(caps, &y));
0);
    cap_free(caps);
}


void do_ping(){
if(!fork()){
        whoami();
        listCaps();
"ping","ping","-c 1","baidu.com",NULL);
    }
}


void init(){
if(getuid() != 0){
puts("請使用sudo執行本程式");
exit(1);
    }
        
cap_value_t cap_values[] = {CAP_SETUID, CAP_SETGID, CAP_SETPCAP ,CAP_NET_RAW};

cap_t caps = cap_init();
4, cap_values, CAP_SET);
4, cap_values, CAP_SET);
    cap_set_proc(caps);
1, 0, 0, 0);
    cap_free(caps);

1000);
1000);
    
    caps = cap_get_proc();
4, cap_values, CAP_SET);
4, cap_values, CAP_SET);
    cap_set_proc(caps);
    cap_free(caps);
    whoami();
    listCaps();
}

void printInfo(){
puts("\n------權能測試小程式------\n\
請輸入如下字元:\n\
1 : 永久的删除其子程序的某個權能\n\
2 : 暫時性的删除其子程序的某個權能\n\
3 : 暫時性删除的權能重新獲得\n\
4 : ping -c 1 baidu.com\n\
5 : 檢視目前權能\n\
q : 退出\n\
---------------------------\n");
}

int main(int argc, char **argv)
{
    init();
char line[128];
    printInfo();
while(fgets(line, sizeof(line), stdin)){
char cmd = line[0];

if(cmd == '1' || cmd == '2' || cmd == '3'){
printf("輸入要操作的權能名,如cap_net_raw\n");
sizeof(line), stdin);
strlen(line) - 1] = '\0';
cap_value_t temp;
if(cap_from_name(line, &temp) < 0){
printf("Error capability name\n");
            }
else{
cap_t caps = cap_get_proc();

if(cmd == '3'){
1, &temp, CAP_SET);
1, &temp, CAP_SET);
                }
else{
1, &temp, CAP_CLEAR);
1, &temp, CAP_CLEAR);
if(cmd == '1')
1, &temp, CAP_CLEAR);
                }
if(cap_set_proc(caps))
"cap_set_proc() ERROR: ");
else      

繼續閱讀