突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

申博_新闻事件 申博 162次浏览 已收录 0个评论

概述

2019年2月25日,runC的保护团队申报了一个新发明的破绽,该破绽最后由Adam Iwaniuk和Borys Poplawski发明。该破绽编号为CVE-2019-5736,破绽影响在默许设置下运转的Docker容器,而且进击者可以或许运用它来取得主机上的root级接见权限。

runC保护团队成员Aleksa Sarai发明LXC存在一样的瑕玷。但与Docker相反,只要特权LXC容器才轻易遭到进击。runC和LXC都对破绽举行了修复,并宣布了新的版本。

该破绽一经宣布,就取得了异常多的存眷,很多手艺站点和商业公司都撰写特地的文章来申明这一破绽。在Twistlock,我们的CTO John Morello撰写了一篇出色的文章,个中包孕Twistlock平台供应的一切相干细节和减缓步伐。

最后,官方透露表现破绽应用代码在2月18日之前不会公然宣布,以防备歹意进击者在用户还没有实时更新的时间段内举行该破绽的兵器化应用。然则,在破绽表露后的几天内,有几个人决议宣布他们自身的破绽应用代码,这直接致使了runC团队在2月13日就宣布了他们的破绽应用代码。因为,他们已没法阻挠人们对这一破绽的主动研讨。

关于该破绽的基础形貌、发明历程和观点证实,请浏览《影响大批云效劳厂商的严峻破绽:runC容器逃逸破绽剖析(CVE-2019-5736)》。本文将在此基础上,深切剖析这一破绽,并议论多种破绽应用要领。

关于RunC

RunC是一个容器运转时,最后是作为Docker的一局部开辟的,厥后作为一个零丁的开源对象和库被提取出来。作为“低级别”容器运转时,runC重要由“高级别”容器运转时(比方Docker)用于天生和运转容器,只管它可以或许用作自力对象。

像Docker如许的“高级别”容器运转时一般会完成镜像建立和治理等功用,而且可以或许运用runC来处置惩罚与运转容器相干的义务:建立容器、将历程附加到现有容器等。

Procfs

要相识破绽,我们起首须要相识一些procfs的基础知识。Proc文件体系是Linux中的一个假造文件体系,重要供应有关历程的信息,一般安装在/proc上。它在某种意义上来说是假造的,因为它在磁盘上现实不存在。相反,内核会在内存中建立它。它还可以或许被以为是一个内存为文件体系公然的体系数据接口。每一个历程在procfs中都有自身的目次,位于/proc/[pid]:

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

如上图所示,/proc/self是指向以后正在运转历程的目次的标记链接(在本例中为pid 177)。每一个历程的目次中都包罗几个文件和目次,个中包罗有关该历程的信息。与本次研讨的破绽相干的几个信息是:

· /proc/self/exe – 历程正在运转的可实行文件的标记链接;

· /proc/self/fd – 包罗历程翻开的文件形貌符的目次。

比方,我们运转ls /proc/self可以或许列出/proc/self下的文件,我们可以或许看到,/proc/self/exe指向“ls”可实行文件。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

这是有原理的,因为接见/proc/self的恰是Shell天生的“ls”历程。

破绽剖析

让我们回忆一下runC团队供应的破绽概述:

该破绽许可歹意容器(以起码的用户交互)掩盖主机runc二进制文件,从而在主机上胡蝶root级别代码实行。用户交互的级别可以或许许可高低文中的任何一个容器以root身份运转任何敕令:

1、运用进击者掌握的映像建立新的容器;

2、将(docker exec)附加到进击者之前具有写入权限的已有容器中。

这两种状况看起来能够分歧,但都须要runC来启动容器中的新历程,并以相似的体式格局完成。在这两种状况下,runC的义务是在容器中运转用户界说的二进制文件。在Docker中,这个二进制文件是启动新容器时映像的进口点,或者是附加到现有容器时的docker exec参数。

运转这个用户二进制文件时,它必需已被限定在容器内,不然能够会要挟主机平安。为了完成这一点,runC建立了一个名为“runC init”的子历程,它将一切须要的限定放在其自身(比方:输入、设置定名空间),并有效地将其自身安排在容器中。然后,如今在容器中的runC init历程挪用execve体系挪用,运用用户请求的二进制文件掩盖自身。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

这是runC用于建立新容器和将历程附加到现有容器的要领。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

研讨人员发明,进击者可以或许经由过程请求runC运转/proc/self/exe来诳骗runC实行其自身,这是一个指向主机上runC二进制文件的标记链接。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

然后,容器中具有root接见权限的进击者可以或许运用/proc/[runc-pid]/exe作为对主机上runC二进制文件的援用,并对其举行掩盖。因为runC二进制文件由root具有,因而须要在容器中举行root接见后能力实行此进击。在下次实行runC时,进击者将在主机上完成代码实行。因为runC一般以root身份运转(比方:经由过程Docker保卫顺序),因而进击者将取得主机上的root接见权限。

* 为何不运用runC init?

上面的图片能够会误导一些读者,让人以为破绽(即诳骗runC实行其自身)是过剩的。也就是说,为何进击者不克不及简朴地掩盖/proc/[runc-init-pid]/exe呢?

针对相似runC破绽的修补顺序CVE-2016-9962可以或许减缓此类进击。CVE-2016-9962破绽具体来说,是runC init历程具有来自宿主机的翻开文件形貌符,容器中的进击者可以或许应用它来遍历主机的文件体系,从而翻开容器。这个破绽的局部修补顺序在进入容器之前就将runC init历程设置为“弗成转储”(Non-dumpable)。

在CVE-2019-5736的高低文中,“弗成转储”标记谢绝其他历程消除援用/proc/[pid]/exe,因而减缓了经由过程/proc/[runc-init-pid]/exe [1]掩盖runC二进制文件的题目。挪用execve将会抛弃此标记,因而新的runC历程/proc/[runc-pid]/exe将可以或许接见。

* 标记链接题目

该破绽好像与Linux中完成标记链接的体式格局相抵牾。

标记链接只是连结其目的的途径。关于runC历程,/proc/self/exe中应当包罗相似/usr/sbin/runc的内容。

当历程接见标记链接时,内核运用链接中存在的途径在接见历程的根目次下查找目的。

这就引出了一个题目——当容器中的历程翻开runC二进制文件的标记链接时,为何内核不会在容器根目次中搜刮runC途径?

谜底是,/proc/[pid]/exe不遵照标记链接的一般语义。从手艺上讲,这能够算作违背POSIX,但正如我之前所提到的,procfs是一个特别的文件体系。当历程翻开/proc/[pid]/exe时,没有一般的读取和跟踪标记链接内容的历程。相反,内核只是让用户直接接见翻开的文件条目。

破绽应用

申报破绽后不久,在还没有公然宣布观点证实(PoC)时,我实验依据LXC的修复补钉(https://github.com/lxc/lxc/commit/6400238d08cdf1ca20d49bafb85f4e224348bf9d)中给出的破绽细致形貌来自行开辟PoC。人人可以或许在这里看到PoC的完全代码:https://github.com/twistlock/RunC-CVE-2019-5736/tree/master/exec_POC

接下来,让我们逐条剖析LXC对破绽的形貌:

当runC附加到容器时,进击者可以或许诳骗它实行其自身。这可以或许经由过程运用指向runC二进制文件自身的自界说二进制文件来替代容器内的目的二进制文件来完成。比方,若是目的二进制文件是/bin/bash,那末就可以或许用指定诠释器途径#!/proc/self/exe的可实行脚正本替代。

#!语法被称为Shebang,在剧本中用于指定诠释器。当Linux加载器碰到Shebang时,将会运转诠释器,而不是可实行文件。

视频:https://asciinema.org/a/228389

如视频中所示,终究由加载顺序实行的顺序是:

interpreter [optional-arg] executable-path

当用户运转docker exec container-name /bin/bash之类的条目时,加载器将辨认修正后的bash中的Shebang,并实行我们指定的诠释器,也就是/proc/self/exe,这是runC二进制文件的标记链接。

我们可以或许经由过程/proc/[runc-pid]/exe从容器中的零丁历程掩盖runC二进制文件。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

————————————-

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

然后,进击者可以或许继承写入/proc/self/exe的目的,以实验掩盖主机上的runC二进制文件。然则一般来说,这不会胜利,因为内核不许可在runC实行时掩盖它。

基础上,若是我们不克不及在历程运转时掩盖runC二进制文件。但是,若是runC历程退出,那末/proc/[runc-pid]/exe也会随之消逝,我们将丧失对runC二进制文件的援用。为相识决这一题目,在我们的历程中翻开/proc/[runc-pid]/exe举行读取,这会在/proc/[our-pid]/fd/3中建立一个文件形貌符。

然后我们守候runC历程退出,然后继承翻开/proc/[our-pid]/fd/3举行写入,并掩盖runC。

下面是overwrite_runc的代码,为简短起见,我们举行了响应的收缩。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

我们来看看!破绽应用的输出显现了掩盖runC所要接纳的步调。我们可以或许看到,runC历程正在运转(PID 20054)。

视频:https://asciinema.org/a/228632

运用这类要领,只要一点不足之处,就是它须要一个分外的历程来运转进击者的代码。因为容器只运用一个历程启动(即:Docker的映像进口点),因而没法运用此要领建立在运转时要挟到主机的歹意映像。

如今,另有其他一些实施与之相似要领的PoC,分别是由Frichetten和feexd提出的思绪。

同享库要领

在runC保护团队宣布的官方PoC中,运用了一种分歧的应用要领,而且优于我提出的PoC,因为它可以或许经由过程两种分歧的要领完成对主机的伤害:

1、当用户将敕令实行到现有进击者掌握的容器中时;

2、当用户运转歹意映像时。

我们如今将研讨构建歹意映像,因为之前的PoC中已展示了第一个场景。我以为这类要领写出来的PoC很大水平是基于q3k的PoC(https://github.com/q3k/cve-2019-5736-poc),据我所知,q3k是第一个宣布的歹意映像PoC。人人可以或许在这里看到完全的PoC代码:https://github.com/twistlock/RunC-CVE-2019-5736/tree/master/malicious_image_POC

让我们来看看用于构建歹意图象的Dockerfile。起首,将映像的进口点设置为/proc/self/exe,以便在运转映像时诳骗runC实行其自身。

# Create a symbolic link to /proc/self/exe and set it as the image entrypoint
RUN set -e -x ;\
   ln -s /proc/self/exe /entrypoint
ENTRYPOINT [ "/entrypoint" ]

RunC在运转时动态链接到多个同享库,我们可以或许运用ldd敕令列出。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

当在容器中实行runC历程时,动态链接器会将这些库加载到runC历程中。我们可以或许运用歹意版本替代个中一个库,这将在加载到runC历程时掩盖runC二进制文件。

我们的Dockerfile构建了libseccomp库的歹意版本:

# Append the run_at_link function to the libseccomp-2.3.1/src/api.c file and build libseccomp
ADD run_at_link.c /root/run_at_link.c
RUN set -e -x ;\
   cd /root/libseccomp-2.3.1 ;\
   cat /root/run_at_link.c >> src/api.c ;\
   DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -b -uc -us ;\
   dpkg -i /root/*.deb

Dockerfile将run_at_link.c的内容附加到libsecomp的源文件当中。随后,将构建歹意libsecomp。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

组织函数属性(GCC语法)注解,在动态链接器将库加载到runC历程以后,run_at_link函数将作为libseccomp的初始化函数实行。因为run_at_link将由runC历程实行,因而它可以或许接见/proc/self/exe中的runC二进制文件。

runC历程必需退出,能力使runC二进制文件可写。为了强制实行退出,run_at_link挪用execve体系挪用,来实行overwrite_runc。

因为execve不会影响历程翻开的文件形貌符,因而可以或许运用前一个PoC中雷同的文件形貌符技能:

1、runC历程加载libseccomp库,并将实行转移到run_at_link函数。

2、run_at_link翻开runC二进制文件,以读取/proc/self/exe。这会在/proc/self/fd/${runc_fd_read}建立一个文件形貌符。

3、run_at_link挪用execve来实行overwrite_runc。

4、该历程不再运转runC二进制文件,overwrite_runc翻开/proc/self/fd/runc_fd_read举行写入,并掩盖runC二进制文件。

我们构建了一个歹意映像,运用一个简朴的剧本掩盖runC二进制文件,该剧本将在2345端口发生反向Shell。

视频:https://asciinema.org/a/228625

Docker run敕令将实行runC两次。一旦建立并运转容器,就将会实行PoC以掩盖runC,然后再次运用runc delete住手容器。

在第二次实行runC时,它就已被掩盖,因而会实行反向Shell剧本。

破绽修复

RunC和LXC都是用了雷同的要领举行修复,这在LXC补钉提交中有明白申明:

为了防备这类进击,LXC已宣布补钉,以便在启动或附加到容器时建立挪用二进制文件自身的暂时副本。为此,LXC运用memfd_create()体系挪用建立一个匿名的内存文件,并将其自身复制到暂时内存文件中,然后将其封装以防备进一步修正。随后,LXC实行该封装后的内存文件,而不是磁盘上原始的二进制文件。从特权容器到主机LXC二进制文件的任何让步写入操纵都将写入到暂时内存中的二进制文件,而不是磁盘上的宿主机二进制文件,从而包管宿主机二进制文件的完全性。另外,因为暂时的内存LXC二进制文件已被封装,因而写入也会失利。

RunC已运用了雷同的要领举行修复。它在启动或附加到容器时,从其自身的暂时副本从新实行。因而,/proc/[runc-pid]/exe如今指向暂时文件,而且没法从容器内接见runC二进制文件。暂时文件也被封装,以阻挠任何写入操纵,只管对它的掩盖不会影响到宿主机。

这个补钉申清楚明了一些题目。在runC init历程已将容器的cgroup内存束缚应用于其自身以后,暂时runC副本将在内存中建立。在运转具有相对较低内存限定(比方10MB)的容器时,若是runC init历程连接到容器,能够会致使内核中的历程被损坏(内存不足)。

若是人人感兴趣,我们可以或许在后续细致议论这一复杂题目,并重点议论怎样能完成修复并包管不再引入雷同的题目。

CVE-2019-5736和特权容器

作为我们的一个履历,我们以为特权容器(特定的容器运转时)比非特权容器(统一运转时)越发不平安。

之前我们说过,破绽将会影响一切Docker容器,但只影响LXC的特权容器。那末,为何Docker非特权容器存在破绽,而LXC非特权容器就不存在呢?这是因为,LXC与Docker是以分歧体式格局来界说特权容器的。现实上,依据LXC的理念,Docker非特权容器现实上被以为是具有特权的。

特权容器的界说是,容器uid 0映射到宿主机uid 0的任何容器。

其重要区分在于,默许状况下LXC在零丁的用户定名空间中运转了非特权容器,而Docker则没有。

用户定名空间是Linux的一项功用,可以或许用于将容器root和宿主机root区离开。容器内的root和一切其他用户将映射到宿主机上的非特权用户。换而言之,历程可以或许对容器内的操纵具有root接见权限,但关于其外部的操纵没有特权。若是人人想相识越发深切的诠释,我引荐人人浏览LWN的定名空间系列文章。

突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)

那末,在用户定名空间中运转容器怎样减缓此破绽呢?

进击者是容器内的root用户,然则映射到宿主机后就变成非特权用户。因而,当进击者试图翻开宿主机的runC二进制文件并举行写入时,内核会谢绝实行这一操纵。

人人能够要问,为何Docker默许状况下不会在零丁的用户定名空间中运转容器呢?这是因为,用户定名空间在容器的高低文中确切存在一些瑕玷,这有点超出了本文的局限。若是人人感兴趣,Docker和rkt(另一个容器运转时)都列出了在用户定名空间中运转容器的限定。

结语

愿望本篇文章能让列位读者对这个破绽具有越发深切地相识。若是人人运用的是runC、Docker或LXC,请记得更新到最新版本。若是人人有任何题目,迎接随时经由过程电子邮件与我们联络。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明突破Docker:runC容器逃逸破绽的深入分析及多种应用要领(CVE-2019-5736)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址