Linux下的capability功能介紹

原文地址: http://rk700.github.io/2016/10/26/linux-capabilities/html

在傳統的Linux的權限控制機制中,SUID(Set User ID on execution)是一個比較有意思的概念。例如,文件/bin/passwd設置了SUID的flag,因此普通用戶在運行passwd命令時,便以passwd的所屬者,即root用戶的身份運行,從而修改密碼。linux

可是,這也帶來了安全隱患。咱們運行SUID的命令時,一般只是須要使用一小部分特權,可是使用SUID,卻能夠擁有root用戶的所有權限。因此,一旦SUID的文件存在漏洞,即可能被利用,以root身份執行其餘操做。所以,SUID機制,在某種程度上增大了安全***面。android

爲了緩解SUID帶來的安全風險,設置SUID須要謹慎,並且這些二進制文件也會經過下降自身euid,並只在須要特權時再恢復爲root,以此來縮小以root身份運行的窗口。git

而仔細反思,咱們即可以看到,SUID的問題,主要在於權限控制太粗糙。爲了對root身份進行更加精細的控制,Linux增長了另外一種機制,即capabilities。本文,便將對這一機制進行介紹。github


Capabilities簡介

Capabilities機制,是在Linux內核2.2以後引入的。它將root用戶的權限細分爲不一樣的領域,能夠分別啓用或禁用。從而,在實際進行特權操做時,若是euid不是root,便會檢查是否具備該特權操做所對應的capabilities,並以此爲依據,決定是否能夠執行特權操做。安全

例如,下表列出了一些常見的特權操做及其對應的capability:app

改變文件的所屬者(chown()) CAP_CHOWN
向進程發送信號(kill(), signal()) CAP_KILL
改變進程的uid(setuid(), setreuid(), setresuid()等) CAP_SETUID
trace進程(ptrace()) CAP_SYS_PTRACE
設置系統時間(settimeofday(), stime()等) CAP_SYS_TIME

Capabilities是細分到線程的,即每一個線程能夠有本身的capabilities。而完整的capabilities實現,除了對線程的capabilities具備如下相關功能:ide

  • 進行特權操做時,檢查該線程是否擁有該操做的capability函數

  • 提供系統調用,用於獲取或修改線程的capabilityui

還應該包含對於文件的capabilities的支持,即:

  • 文件系統支持文件附加屬性,使得可執行文件具備必定的capabilities,從而在運行時肯定其capabilities

對於文件capabilities的支持,直到內核2.6.24以後才完成。

如下,將分別介紹線程(進程)的capabilities和文件的capabilities。


線程與文件的capabilities

線程的capabilities

每個線程,具備3個capabilities的集合,分別是:

  • Permitted

  • Inheritable

  • Effective

每一個集合中,能夠包含零個或多個capabilities。這3種集合的具體含義以下:

Permitted

這個集合定義了線程所可以擁有的特權的上限。換句話說,若是某個capability不在Permitted集合中,那麼該線程便不能進行這個capability所對應的特權操做。Permitted集合是Inheritable和Effective集合的的超集。

Inheritable

當執行exec()系運行其餘命令時,可以被新命令繼承的capabilities,被包含在Inheritable集合中。

Effective

內核檢查該線程是否能夠進行特權操做時,檢查的對象即是Effective集合。如以前所說,Permitted集合定義了上限。線程能夠刪除Effective集合中的某capability,隨後在須要時,再從Permitted集合中恢復該capability,以此達到臨時禁用capability的功能。

(Linux 4.3以後,增長了一種集合Ambient。詳情可見相關manual)


文件的capabilities

文件的capabilities,是保存在文件的擴展屬性中。修改這些擴展屬性,須要具備CAP_SETFCAP的capability。文件與線程的capabilities,共同決定了經過exec運行該文件後的capabilities。

文件的capabilities功能,須要文件系統的支持。若是文件系統使用了nosuid選項進行掛載,那麼文件的capabilities將被忽略。

相似於線程的capabilities,文件的capabilities也包含了3個集合:

Permitted

這個集合中包含的capabilities,在文件被執行時,被加入其Permitted集合。

Inheritable

這個集合與線程的Inheritable集合的交集,是執行完exec後實際繼承的capabilities。

Effective

這僅僅是一個bit。若是設置開啓,那麼在運行exec後,Permitted集合中新增的capabilities會自動出如今Effective集合中;不然不會出如今Effective集合中。對於一些舊的可執行文件,因爲其不會調用capabilities相關函數設置自身的Effective集合,因此能夠將該可執行文件的Effective bit開啓,從而將Permitted集合中的capabilities自動添加到Effective集合中。


運行exec後capabilities的變化

上面介紹了線程和文件的capabilities,可能會以爲有些抽象難懂。下面將使用具體的計算公式,來講明執行exec後capabilities是如何肯定的。

咱們使用P表明執行exec前的capabilities,P’表明執行exec後的capabilities,F表明exec執行的文件的capabilities。那麼:

P’(Permitted) = (P(Inheritable) & F(Inheritable)) | (F(Permitted) & cap_bset)

P’(Effective) = F(Effective) ? P’(Permitted) : 0

P’(Inheritable) = P(Inheritable)

其中的cap_bset是capability bounding set。經過與文件的Permitted集合計算交集,可進一步限制某些capabilities的獲取,從而下降了風險。

而正如介紹文件的Effective bit時所說,文件能夠將其Effective bit關閉。由此,在經過exec執行該文件後,實際的Effective集合爲空集。隨後,在須要進行特權操做時,可再將Permitted集合中的capabilities加入Effective集合中。

獲取和設置capabilities

系統調用capget(2)capset(2),可被用於獲取和設置線程自身的capabilities。此外,也可使用libcap中提供的接口cap_get_proc(3)cap_set_proc(3)。固然,Permitted集合默認是不能增長新的capabilities的,除非CAP_SETPCAP在Effective集合中。

若是要查看線程的capabilities,能夠經過/proc/<PID>/task/<TID>/status文件,三種集合分別對應於CapPrm, CapInh和CapEff。但這種的顯示結果是數值,不適合人類閱讀。爲此,可以使用包libcap中的命令getpcaps <PID>獲取該進程的主線程的capabilities。

相似的,若是要查看和設置文件的capabilities,可使用命令getcap或者setcap

示例

ping

使用getcap命令,咱們能夠看到ping文件的capabilities:

root@debian:~# getcap /bin/ping /bin/ping = cap_net_raw+ep

即該文件的capabilities,設置了Effective bit,並且Permitted集合中包含了CAP_NEW_RAW,從而能夠發送raw packet。

run-as

另外一個例子,是Android系統中的run-as命令。根據源碼中的註釋,run-as具備CAP_SETUID和CAP_SETGID,從而能夠設置自身的resuid和resgid。由此,能夠達到run-as的效果,即以特定uid運行命令。

DirtyCow漏洞在Android上的利用,即是修改run-as文件的內容,並利用CAP_SETUID設置resuid爲root,實現權限提高。


Android上的capabilities

根據官方說明,Android 4.3以後,便再也不使用SUID/SGID,而是使用capabilities機制,以減小***面。

可是,雖然Android源碼中有包含庫libcap-ng的代碼,但並未編譯相關代碼。咱們下載libcap-ng,並修改以下:

$ diff -u libcap-ng-0.7.8/src/cap-ng.c ~/src/libcap-ng-0.7.8/src/cap-ng.c --- libcap-ng-0.7.8/src/cap-ng.c        2016-10-27 10:09:12.000000000 +0800 +++ /Users/liuruikai756/src/libcap-ng-0.7.8/src/cap-ng.c        2016-07-24 23:47:34.000000000 +0800 @@ -25,9 +25,7 @@  #include <string.h>  #include <stdarg.h>  #include <stdio.h> -#if !defined(ANDROID)  #include <stdio_ext.h> -#endif  #include <stdlib.h>  #include <sys/prctl.h>  #include <pwd.h> @@ -278,9 +276,7 @@         f = fopen(buf, "re");         if (f == NULL)                 return -1; -#if !defined(ANDROID)         __fsetlocking(f, FSETLOCKING_BYCALLER); -#endif         while (fgets(buf, sizeof(buf), f)) {                 if (strncmp(buf, "CapB", 4))                         continue;

隨後,複製Android源碼中的external/libcap-ng/config.hexternal/libcap-ng/Android.mk。在config.h中須要定義HAVE_SYS_XATTR_H,並修改Android.mk以編譯filecap

隨後,運行命令ndk-build進行編譯,並將相關文件push到設備:

$ ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk APP_PLATFORM=android-19 $ adb -e push libs/x86/libcap-ng.so /system/lib/ $ adb -e push libs/x86/filecap /data/local/tmp/

運行/data/local/tmp/filecap即可查看文件的capabilities了。

例如,查看以前提到的文件/system/bin/run-as的capabilities以下:

root@generic_x86:/ # /data/local/tmp/filecap /system/bin p/filecap /system/bin/run-as                                                  < file                 capabilities /system/bin/run-as     setgid, setuid

參考文獻

相關文章
相關標籤/搜索