概述
在过去几年里,在各种容器平台(包括Docker,Podman和Kubernetes)中发现了复制(cp)命令的的多个漏洞。其中最严重的一个是今年7月才暴露的CVE-2019-14271。CVE-2019-14271源于Docker cp命令实现中的安全bug,当被攻击者利用时,该漏洞可能导致完全容器权限穿越。自从二月份发现严重的runC漏洞之后,第一个完全的容器穿越的漏洞。
如果容器已经被篡改(例如,通过任何其他漏洞,泄露的机密信息等),或者当用户从不明途径来源(第三方恶意注册表或其他来源)运行恶意容器映像时,可能会引发漏洞。如果用户随后执行cp命令从受感染的容器中复制文件,则攻击者可以逃脱并完全控制宿主机机以及该宿主机下的所有容器。
CVE-2019-14271漏洞已经在Docker版本19.33.1中被标记为关键项并且已修复。
Docker cp
copy命令可以从容器复制文件,复制文件到容器中,或者在容器之间复制文件。其语法与标准Unix cp命令非常相似。要从容器中复制/var/logs,语法为:
docker cp container_name:/var/logs 宿主机目录
如您在下图中所看到的,要将文件复制到容器外,Docker使用了一个名为docker-tar的帮助程序。
docker-tar的工作原理是切入容器(如下图所示),将请求的文件和目录打包成tar文件,然后将生成的tar文件传递回Docker守护程序,该守护程序负责将其提取到目标目录在主机上。
进行Chrooting大多是为了避免符号链接问题,当主机进程尝试访问容器上的文件时,可能会发生符号链接问题。如果这些文件中有一个是符号链接,则可能会无意中将其解析为主机根目录。这为攻击者控制的容器打开了大门,使他们可以尝试诱使docker cp在主机而不是容器上读取和写入文件。去年,Docke和Podman中的几CVE漏洞都是关于符号链接(CVE-2019-10152,CVE-2018-15664)相关的漏洞。通过切入容器的根目录,docker-tar确保所有符号链接都将在其下有效解析。不幸的是,在从容器中复制文件时,Chrooting做法导致了更严重的问题。
漏洞介绍
Docker是使用Golang编写的。这个漏洞源于Go v1.11版本编译Docker。在这个版本中,某些包含嵌入式C代码(cgo)的软件包将在运行时动态加载共享库。这些软件包包括net和os/user,都被docker-tar使用,它们会在运行时加载多个libnss _ *.so的动态库。通常,库会从主机文件系统中加载,但是由于docker-tar被chroots到了容器中,因此它会从容器文件系统中加载这些动态库。这样docker-tar将加载并执行由容器发起和控制的代码。
需要注意的是,除了被chroot到容器文件系统之外,docker-tar并未被容器化。它运行在具有所有根功能且不受cgroups或seccomp限制的主机名称空间中。因此,通过将代码注入docker-tar,恶意容器获得了对主机全root权限。
可能的攻击情形是Docker用户从以下任一用户复制一些文件:
运行带有错误libnss _ *.so库的恶意映像的容器。
受到攻击的容器,攻击者在其中替换了libnss _ *.so库。
在这两种情况下,攻击者都可以获得主机root执行权限。
有趣的是该漏洞实际上是首先从GitHub问题中发现的。
用户试图从debian:buster-slim容器中复制文件,docker cp反复失败。问题在于该特定镜像不包含libnss库。因此,当用户运行docker cp且docker-tar进程尝试从容器文件系统加载它们时,报错崩溃了。
利用原理
CVE-2019-14271需要利用 libnss库。任意选择libnss_files.so。构造函数属性(特定于GCC的语法)指示run_at_link函数在由进程加载时将作为库的初始化函数执行。当docker-tar进程动态加载我们的恶意库时,将执行run_at_link。下面是run_at_link代码:
run_at_link首先验证它是否在docker-tar上下文中运行,因为其他常规容器进程也可能会加载它。这是通过检查/proc目录来完成的。如果run_at_link在docker-tar的上下文中运行,则此目录将为空,因为/ proc上的procfs挂载仅存在于容器挂接命名空间中。
接下来,run_at_link将libnss库替换为原始库。这样可确保利用此漏洞运行的所有后续进程都不会意外加载恶意版本并重新触发run_at_link的执行。
为简化利用,run_at_link尝试在容器中的路径/breakout处运行可执行文件。
以下是视频中使用的/breakout脚本的来源。为了获得对主机根文件系统的引用,脚本通过/proc挂载了procfs。由于docker-tar在主机的PID名称空间中运行,因此挂载的procfs将包含主机进程上的数据。然后,该脚本只需挂载主机PID 1r根。
漏洞修复
漏洞修复程序包括修补docker-tar的init函数从有问题的Go软件包中调用任意函数。
迫使docker-tar在chroot到容器之前从主机文件系统加载libnss库。
安全建议
允许在主机上执行根代码的漏洞非常危险。确保运行的是Docker 19.03.1版或更高版本,其中包括针对此安全问题的修复程序。为了限制此类攻击的攻击面,强烈建议不要运行不受信任的映像。
此外,在不需要严格使用root用户的情况下,强烈建议以非root用户身份运行容器,这样可以进一步提高了其安全性,并阻止了攻击者利用容器引擎或内核中可能发现的许多缺陷。对于CVE-2019-14271,如果容器使用非root用户运行,则你不会受漏洞影响。即使攻击者破坏了容器,也无法覆盖容器的libnss库,因该库为root拥有的,因此无法利用此漏洞。