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

参考文献

相关文章
相关标签/搜索