CVE-2019-5736 的 PoC
在 @singe、@_cablethief 和 @feexd 的帮助下创建
已在 Ubuntu 18.04、Debian 9 和 Arch Linux 上测试。Docker 版本 18.09.1-ce 和 18.03.1-ce。此 PoC 目前不适用于 Ubuntu 16.04 和 CentOS。
请前往 Dragon Sector(发现该漏洞的团队)的漏洞利用代码,地址为 此处。
这是 CVE-2019-5736 的 Go 实现,一个针对 Docker 的容器逃逸漏洞。该漏洞利用通过覆盖并执行主机系统的 runc 二进制文件,从容器内部实现逃逸。
该漏洞利用有 2 种使用场景。第一种(也就是本仓库所实现的)本质上是一个陷阱。攻击者需要在容器内获得命令执行权限,并启动一个监听的恶意二进制文件。当有人(攻击者或受害者)使用 docker exec 进入容器时,将触发漏洞利用,从而以 root 身份执行代码。

第二种(不是本仓库所实现的)是创建一个恶意的 Docker 镜像。当该镜像运行时,漏洞利用将自动触发,无需 exec 进入容器。参见本 readme 底部的示例 gif。
要利用此漏洞,你需要在容器内拥有 root 权限(uid 0)。
是的,你会覆盖你的 runc 实现,这将导致你的系统无法再运行 Docker 容器。请备份 /usr/bin/docker-runc 或 /usr/bin/runc(取决于你系统上的是哪一个;也请检查 /usr/sbin)。
根据需要修改代码,然后使用 go build main.go 编译。将生成的二进制文件移动到你想逃逸的容器中。执行该二进制文件,下次有人附加到该容器并调用 /bin/sh 时,你的载荷就会触发。
此 PoC 是参照 lxc 项目的这个提交中的优秀解释(以及来自其他人的有用建议)创建的。
例如,如果目标二进制文件是 /bin/bash,可以将其替换为一个可执行脚本,指定解释器路径 #!/proc/self/exe(/proc/self/exe 是内核为每个进程创建的符号链接,指向该进程所执行的二进制文件)。因此,当容器内执行 /bin/bash 时,实际上会执行 /proc/self/exe 的目标——即指向主机上的 runc 二进制文件。
我们通过将容器内的 /bin/sh 覆盖为 #!/proc/self/exe 来实现这一点,这将指向启动此进程(Docker exec)的二进制文件。

然后,攻击者可以尝试写入 /proc/self/exe 的目标,以覆盖主机上的 runc 二进制文件。然而,通常情况下这不会成功,因为内核在 runC 执行期间不允许覆盖它。为了克服这一点,攻击者可以使用 O_PATH 标志打开 /proc/self/exe 的文件描述符,然后通过 /proc/self/fd/ 以 O_WRONLY 模式重新打开该二进制文件,并尝试在另一个进程中通过忙循环写入。
注意:上一节的部分内容并不完全准确。在获取 runcinit 的文件描述符时,不需要使用 O_PATH 标志。此外,无需在另一个进程中创建写入循环。我们通过获取 /proc/PID/exe 的文件句柄来获取 runcinit 的文件描述符。然后,使用该句柄获取 /proc/self/fd/FILEDESCRIPTOR 的文件句柄。我们将使用这个文件句柄进行写入。

最终,当 runC 二进制文件退出时,写入将成功。此后,runC 二进制文件被攻破,可用于攻击其他容器或主机本身。
如果我们能够写入该文件句柄,我们就覆盖了主机上的 runc 二进制文件。我们可以以 root 身份执行任意命令。
本仓库不包含此示例,但你可以在此处找到它。在我看来,这是更危险的场景。只需运行恶意容器镜像,就能以 root 身份执行代码。详细解释请参见这里。
