如果有一個宇宙,你很幸福,你會不會想要去 - 奇異博士2:失控多元宇宙 (2022)
第16天要針對 DirtyPipe 的漏洞原理做說明,順便也來看一下當初漏洞發現者的心路歷程。
DirtyPipe 漏洞主要是因為當初發現者透過 zero-copy 的技術去傳輸資料時,發現傳輸的檔案有時候會發生檔案驗證碼驗證錯誤,疑似傳輸過程內容遭受竄改。在經過多方確認的情況下,他將問題指向了 Linux kernel 核心,那這算是一個很厲害的舉動,因為正常的開發者都不會覺得有 bug 是作業系統的問題。那他當時建立的驗證程式參考如下 :
#include <unistd.h>
int main(int argc, char **argv) {
for (;;) write(1, "AAAA\n", 5);
}
// ./writer >foo
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char **argv) {
for (;;) {
splice(0, 0, 1, 0, 2, 0);
write(1, "BBBB\n", 5);
}
}
// ./splicer <foo |cat >/dev/null
這邊稍微解釋一下 splice 的作用,其呼叫介面為 ssize_t splice(int fd_in, off64_t *off_in, int fd_out, off64_t *off_out, size_t len, unsigned int flags),它的用途是可以將來源的 file descriptor - fd_in 的內容寫到目的端的 file descriptor - fd_out。相關細節可以參考 splice(2) - Linux manual page 以及 Linux I/O 原理和 Zero-copy 技术全面揭秘。從上面程式分析後會發現理論上 foo 檔案應該只有 A 的字元寫入,但實際上讀取發現居然有 B 的字元混進來,代表著 Linux kernel 在實作上應該有缺陷導致其他來源資料混入。而且依照權限設定理論上普通帳號沒有權限去修改 foo 檔案,但寫入資料居然包含普通帳號的輸出資料,這也意味著 kernel 在透過 splice 以及 pipe 處理資料時並沒有考慮到寫入檔案的權限問題。
但究竟是甚麼原因導致這個檔案本身透過 splice + pipe 時讓另一個程式有機會改變檔案呢? 原因在於 Linux kernel 於 8.5 時可透過一些手法讓 pipe 結構內的 pipe_buffer 開啟 PIPE_BUF_FLAG_CAN_MERGE 的機制,一旦開啟後對該 pipe_buffer 對到的分頁進行寫入時,會蓋掉該分頁的內容。而一旦分頁"髒"掉的情況下,作業系統就會認為該分頁被修改,因此會將結果回寫回該分頁的來源,以這個漏洞為例就是檔案系統。以下就以 pipe_buffer 結構為是意圖進行說明 :
參考來源 : Linux I/O 原理和 Zero-copy 技术全面揭秘
也因此大概就了解 DirtyPipe 的漏洞原理,當然細節部分還是很多,包含 splice 的設計原理、pipe 的運作機制也都還是要補完的地方,這邊就只是快速的帶過而已。如果對後續更深入的資訊有興趣的話,也可以參考 HITCON PEACE 2022 的演講議題 How we use Dirty Pipe to get reverse root shell on Android Emulator and Pixel 6,另外我之前的影片有針對 pipe 的用法比較詳細的說明 跪著講解 Dirty Pipe(骯髒管線)的漏洞原理 - CVE-2022-0847 也可以參考一下。
最後提一下 Linux Dirty系列漏洞目前有三個,分別是 Dirty COW(CVE-2016-5195)、Dirty Pipe(CVE-2022-0847) 以及 DirtyCred (CVE-2022-2588)。 DirtyCred 的原理更加的困難,牽涉到 Use-After-Free 、 Double Free 的攻擊手法,有興趣的話也可以自己去研究看看。相關影片可參考 Linux 三髒之力 Assemble,先來了解一下 DirtyCred (CVE-2022-2588) 大概的攻擊原理!!! 以及 千呼萬喚髒出來~~~ 終於等到 DirtyCred (CVE-2022-2588) 攻擊程式惹!!!。