4 相关程序漏洞导致的容器逃逸
4.1 CVE-2019-5736
影响版本: Docker version <= 18.09.2
& RunC version <= 1.0-rc6
4.1.1 原理
CVE-2019-5736 一个能够覆盖宿主机 runc 程序的容器逃逸漏洞。
在执行功能类似于 docker exec
等命令时,底层实际上是容器运行时在操作。例如 runC,相应地, runc exec
命令会被执行。它的最终效果是在容器内部执行用户指定的程序。进一步讲,就是在容器的各种命名空间内,受到各种限制(如 Cgroups)的情况下,启动一个进程。除此以外,这个操作与在宿主机上执行一个程序并无二致。
执行过程大体如下:runC 启动并加入到容器的命名空间,接着以自身 ( /proc/self/
exe
)为范本启动一个子进程,最后通过 exec 系统调用执行用户指定的二进制程序。
proc 伪文件系统,即 /proc。关于这个概念,这里我们主要关注 proc 下的两类文件:
/proc/[PID]/exe
:它是一种特殊的符号链接,又被称为magic links
, 指向进程自身 对应的本地程序文件(例如我们执行ls /proc/[ls-PID]/exe
就指向/bin/ls
)。/proc/[PID]/fd/
:这个目录下包含了进程打开的所有文件描述符。
/proc/[PID]/exe
的特殊之处在于,当打开这个文件时,在权限检查通过的情况下,内核将 直接返回一个指向该文件的描述符,而非按照传统的打开方式做路径解析和文件查找。这样 一来,它实际上绕过了 mnt 命名空间及 chroot 机制对一个进程能够访问到的文件路径的限制。
那么,设想如下攻击场景:在 runc exec
加入到容器的命名空间之后,容器内进程已经能够 通过内部 /proc
观察到它,此时如果打开 /proc/runc-[PID]/exe
并写入一些内容,就能够实现 将宿主机上的 runc 二进制程序覆盖掉。这样一来,下一次用户调用 runc 来执行命令时,实 际执行的将是攻击者放置的指令。
在存在漏洞的容器环境内,上述思路是可行的,但是攻击者想要在容器内实现宿主机 上的代码执行(逃逸),还需要突破两个限制:
- 用户权限限制,需要具有容器内部 root 权限。
- Linux 不允许修改正在运行的进程对应的本地二进制文件。
事实上,很多容器就是以 root 身份启动服务的。
攻击步骤:
- 将容器内的
/bin/sh
程序覆盖为#!/proc/self/exe
。 - 持续遍历容器内
/proc
目录,读取每一个/proc/[PID]/cmdline
,对 runc 做字符串 匹配,直到找到 runc 进程号。 - 以只读方式打开
/proc/[runc-PID]/exe
,拿到文件描述符 fd。 - 持续尝试以写方式打开第 3 步中获得的只读 fd (
/proc/self/fd/[fd]
),一开始总是返回失败,直到 runc 结束占用后写方式打开成功,立即通过该 fd 向宿主机上的/usr/bin/runc
,(名字也可能是/usr/bin/docker/runc
)写入攻击载荷 - runc 最后将执行用户通过
docker exec
指定的/bin/sh
,它的内容在第 1 步中已经被替换成#/proc/self/exe
,因此实际上将执行宿主机上的 runc,而 runc 也已经在第 4 步中被覆盖掉了。
4.1.2 复现
./metarget cnv install cve-2019-5736
POC :
payload 的内容:
var payload = "#!/bin/bash \n cat /etc/shadow > /tmp/shadow && chmod 777 /tmp/shadow"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论