一个内核网络漏洞详解|容器逃逸
CVE-2021-22555:一個影響2006年(Linux kernel v2.6.19-rc1 發(fā)布)至今(Linux kernel v5.12-rc8)的所有Linux內(nèi)核版本的漏洞,可導(dǎo)致本地提權(quán)與容器逃逸;該漏洞是個內(nèi)核級漏洞,跟Linux的發(fā)行版本沒有關(guān)系,也就是說只要Linux 內(nèi)核版本在v2.6.19-rc1 ~v5.12-rc8 之間的內(nèi)核,都存在被黑客利用該漏洞攻擊的可能。
該漏洞是由Andy Nguyen (theflow@)發(fā)現(xiàn),于2021年07月16日發(fā)布。換句話說,該漏洞已經(jīng)存在了15年之久,都沒有被人發(fā)現(xiàn)。該漏洞將允許本地用戶通過用戶名空間獲取權(quán)限提升,和容器逃逸。
來之:LFAPAC
原文:https://mp.weixin.qq.com/s/DjWCuBv5iegZBAgIrjfKA
1.漏洞概述
| 漏洞編號 | CVE-2021-22555 |
| 內(nèi)核組件 | kernel-netfilter |
| 漏洞類型 | Linux?kernel堆溢出、特權(quán)提升 |
| 影響版本 | Linux Kernel : v2.6.19-rc1?~?v5.12-rc8 |
簡述: Linux 內(nèi)核模塊Netfilter中存在一處權(quán)限提升漏洞,在 64位系統(tǒng) 上為 32位進程 處理 setsockopt IPT_SO_SET_REPLACE(或 IP6T_SO_SET_REPLACE)時,如果內(nèi)核選項CONFIG_USER_NS 、CONFIG_NET_NS被開啟,則攻擊者可以通過該漏洞實現(xiàn)權(quán)限提升,以及從docker、k8s容器中實施容器逃逸。
2.漏洞環(huán)境和樣例
2.1.環(huán)境準備
Linux發(fā)行版:Ubuntu 20.04.2 LTS
Linux?kernel?版本:5.8.0-48-generic
如圖:
2.2.利用樣例
3 漏洞原理
在Linux內(nèi)核代碼net/netfilter/x_tables.c中的?xt_compat_target_from_user?函數(shù)內(nèi)的第1129行,在使用memset()時并未對傳進來的參數(shù)進行校驗,會導(dǎo)致在寫0時,越過堆的大小,導(dǎo)致“堆溢出”,從而破壞堆與堆之間的邊界。
而寫超的0?的內(nèi)核內(nèi)存,剛好又在應(yīng)用層程序(惡意程序)通過堆噴技術(shù)拿到感知控制。通過搜索,可以找到被寫超的內(nèi)核內(nèi)存;惡意程序在應(yīng)用層建立與這段內(nèi)核內(nèi)存的聯(lián)系,把要執(zhí)行代碼commit_creds(prepare_kernel_cred(NULL))封裝到回調(diào)函數(shù)內(nèi);利用內(nèi)核機制在內(nèi)核內(nèi)存中建立與回調(diào)機制使用關(guān)系,再把剛才封裝的函數(shù)地址更新到內(nèi)核內(nèi)存中。當(dāng)主動釋放這段內(nèi)核內(nèi)存,觸發(fā)回調(diào)函數(shù),從而調(diào)用到commit_creds()函數(shù)把權(quán)限升級到root權(quán)限。
4 前導(dǎo)技術(shù)知識點
4.1 UAF (Use-After-Free)
是一個與程序運行期間不正確使用動態(tài)內(nèi)存相關(guān)的漏洞利用方式。
程序在釋放內(nèi)存位置后,系統(tǒng)不會馬上回收內(nèi)存。指針指向的那段內(nèi)存依然存在,并且內(nèi)容沒有被清除,攻擊者可以利用該錯誤來入侵該程序。(本次漏洞重點使用。通過UAF,可以達到同一塊內(nèi)存在不同的操作對象之間交換控制管理權(quán))
4.2 SMAP & SMEP
SMAP(Supervisor Mode Access Prevention):管理模式訪問保護–禁止內(nèi)核CPU訪問用戶空間的數(shù)據(jù).
SMEP(Supervisor Mode Execution Prevention):管理模式執(zhí)行保護–禁止內(nèi)核CPU執(zhí)行用戶空間的代碼(并不會因為你權(quán)限高就能訪問/執(zhí)行低權(quán)限的資源,你的就是你的,我的就是我的).
SMEP和SMAP導(dǎo)致我們不能像從前那樣,利用惡意進程提權(quán)到內(nèi)核權(quán)限后,扭頭去執(zhí)行布置在用戶態(tài)的惡意shellcode,用戶態(tài)shellcode注入也不好使了(Linux?內(nèi)核漏洞防御機制).
4.3 KASLR
KASLR(kernel address space layout randomization)也就是內(nèi)核地址空間布局隨機化。KASLR技術(shù)允許kernel image加載到VMALLOC區(qū)域的任何位置。當(dāng)KASLR關(guān)閉的時候,kernel image都會映射到一個固定的鏈接地址。
對于黑客來說是透明的,因此安全性得不到保證。KASLR技術(shù)可以讓kernel image映射的地址相對于鏈接地址有個偏移。如果bootloader支持每次開機隨機生成偏移數(shù)值,那么可以做到每次開機kernel image映射的虛擬地址都不一樣。
因此,對于開啟KASLR的kernel來說,不同的產(chǎn)品的kernel image映射的地址幾乎都不一樣。因此在安全性上有一定的提升(Linux?內(nèi)核漏洞防御機制)。
4.4 rop 鏈攻擊
rop(Return Oriented Programming)。X86架構(gòu)下,函數(shù)調(diào)用規(guī)則是,當(dāng)剛跳轉(zhuǎn)到其他函數(shù)去執(zhí)行時,從被調(diào)用者的視角看:棧頂是返回地址,緊接著是自己的參數(shù)。
被調(diào)用者會對棧空間進行一系列操作,保存寄存器和存儲臨時變量,但在即將退出時會清理自己消耗的棧空間,以使其回到自己被調(diào)用前的棧空間,保持棧平衡;
最后,被調(diào)用者以ret指令結(jié)尾,ret指令將棧頂?shù)刂穫鬟f到 RIP寄存器 ,隨后代碼也就跳轉(zhuǎn)到之前棧頂存放的返回地址處;
rop鏈即是基于以上這個簡單的原理,在代碼空間中尋找以ret結(jié)尾的代碼片段或函數(shù)(代碼片段稱為Rop gadgets),組合可以實現(xiàn)拓展可寫棧空間、寫入內(nèi)存、shell等功能,依靠ret將 代碼執(zhí)行權(quán) 緊握在自己的手里。
4.5?一段代碼的運行
在計算機體系中,所有的行為邏輯,都是要以“程序”為媒介得以執(zhí)行。當(dāng)然,這也包含了所有“惡意行為”在內(nèi)的病毒、木馬等待。
而“程序”的本質(zhì)是通過代碼來對“現(xiàn)實行為”在計算機內(nèi)的行為仿真(程序沒有“惡意”和“非惡意”之分,惡意的是寫這份代碼的人心);既然是代碼,也是要遵照計算機體系的約定。
4.5.1?代碼運行的條件
在Linux系統(tǒng)中所有的行為都是被“權(quán)限”所規(guī)范起來。
換句話說,程序想要被運行起來就需要“獲得”相應(yīng)的權(quán)限。而在Linux系統(tǒng)權(quán)限是被嚴格的劃分。我們可以從2個維度(應(yīng)用層/內(nèi)核層)來對權(quán)限進行了解。
4.5.2?應(yīng)用層(Ring?3)
也就是平時我們操作Linux界面。在應(yīng)用層中是以用戶為單位進行管理,又劃分成2類:特權(quán)用戶(也就是root),普通用戶。
Linux限定了普通用戶的行為,也包含普通用戶的程序;在普通用戶模式下,有很多系統(tǒng)級的接口、功能,都是不能使用的;基本也就是只能使用主機的“計算能力”,不能使用到“管理能力”。
4.5.4?內(nèi)核層(Ring?0)
操作系統(tǒng)是對物理硬件的抽象,而內(nèi)核層是操作系統(tǒng)的核心。
在內(nèi)核層中,沒有像應(yīng)用層那樣,做權(quán)限區(qū)分,任何執(zhí)行代碼都是一樣的權(quán)限。
而內(nèi)核層相對于用戶層,擁有的特權(quán)是最高。
4.5.5?漏洞利用邏輯
根據(jù)前面提到的,想要運行代碼,需要2個條件:
1、代碼在內(nèi)存中
2、裝載代碼的內(nèi)存擁有執(zhí)行權(quán)限
一個普通用戶的程序,正常情況下只能通過高權(quán)限的授權(quán),來提升自己的權(quán)限,除此別無他法。
而CVE-2021-22555漏洞可以通過“內(nèi)核特權(quán)”來打破這一規(guī)則。
5 漏洞利用步驟
5.1?堆噴:建立線性的內(nèi)核堆內(nèi)存
通過系統(tǒng)提供的消息隊列功能,建立大量的消息隊列,并往每個消息隊列里面?zhèn)魅胂?#xff08;每個消息大小:4096)。這些消息會緩存到內(nèi)核當(dāng)中。
緩存在內(nèi)核,也就意味著有大量的內(nèi)存占用。而當(dāng)有大量的內(nèi)存申請使用,內(nèi)核的內(nèi)存管理模塊為了減小內(nèi)存碎片的問題,會把相鄰的內(nèi)存返回給內(nèi)存使用者。
也就是說,這些緩存的消息,在內(nèi)存中是連續(xù)存在,緊挨著的。
如圖:
這樣還不夠,再次往各個消息隊列發(fā)送消息,這次發(fā)送的消息大小是1024。而在消息隊列中的消息是用鏈表的方式來連接的,在內(nèi)存中的布局如圖:
這里面的選用?4096?大小作為第一條消息,是為了適配漏洞代碼?memset()?做格式化的大小。
而選用1024?大小作為每二條消息,是為了利用上漏洞后,當(dāng)內(nèi)核內(nèi)存使用,與后續(xù)的回調(diào)機制匹配。
5.2?漏洞觸發(fā)?&?UAF攻擊
現(xiàn)在讀出部分各個消息隊中的?4096大小的消息,其目的是為了釋放在內(nèi)核內(nèi)存中緩存的內(nèi)存。在讀出的消息的同時內(nèi)存也得到釋放。
接下應(yīng)用層利用?socket?觸發(fā)漏洞函數(shù)。觸發(fā)代碼:
setsockopt(fd, SOL_IP, IPT_SO_SET_REPLACE, &data, sizeof(data))
data?的大小通過人為的方式來構(gòu)造,其中sizeof(data)?的大小,會傳到漏洞語句。造成堆寫0溢出,踩到相鄰的內(nèi)存。
內(nèi)核層對應(yīng)的漏洞函數(shù)?xt_compat_target_from_user?被觸發(fā)時,會在內(nèi)核層申請內(nèi)核內(nèi)存。根據(jù)UAF攻擊原理,xt_compat_target_from_user?函數(shù)內(nèi)申請的內(nèi)存剛好會是上面消息隊在內(nèi)核層釋放的內(nèi)存塊。
而該內(nèi)存塊相連的是其它的?4096大小的消息內(nèi)存塊。當(dāng)漏洞函數(shù)被觸發(fā),寫超的內(nèi)存,就會寫到相鄰的?4096?消息內(nèi)。
4096?消息在應(yīng)用層可以通過遍歷,來找出被踩的那塊內(nèi)核消息塊。
在溢出寫0的長度是在應(yīng)用層可以人為的控制,而這里,我們設(shè)計溢出的長度為?2?個字節(jié)。
根據(jù)消息結(jié)構(gòu)體在內(nèi)核層中的定義,溢出寫0的2個字節(jié)剛好會覆蓋消息結(jié)構(gòu)體內(nèi)的指向下一條1024消息的指針的后2位。就會導(dǎo)致被溢出踩的4096消息會指向另一個1024消息,而這條1024同時會被另一個4096的消息指向。
也就是同一個1024消息被2個4096消息指向,為后續(xù)的UAF利用提供了條件。
如圖:
5.2.1?一點題外話
該漏洞的利用開始就是從寫超2個字節(jié)的堆內(nèi)存,而該漏洞的發(fā)現(xiàn)者也因此獲取得了10000$的獎勵。而在作者后續(xù)的文章說明中,也是很幽默地把該點寫到文章的標題上。
如圖:
5.3?繞過內(nèi)核?SMAP?機制
其實目的是為了把權(quán)限升級的相關(guān)的代碼傳到內(nèi)核層讓,讓內(nèi)核層的權(quán)限去運行權(quán)限升級的代碼,來提升用戶層用戶的權(quán)限。
繞過SMAP的基礎(chǔ),就是要知道內(nèi)核內(nèi)存的地址。知道地址的基礎(chǔ),把我們要提升權(quán)限的代碼以ROP攻擊鏈的方式寫到該內(nèi)核內(nèi)存當(dāng)中。
通過對消息隊的遍歷,找到了被雙重引用的1024消息,而目前還不知道該被雙重引用的1024消息的地址。
不知道該消息的內(nèi)核內(nèi)存地址,是沒辦法繞過SMAP機制,同時在后面作為ROP鏈的媒介內(nèi)存,在不知道內(nèi)存地址的情況下,是沒辦法使用的。
接下來就是需要知道該消息的內(nèi)核內(nèi)存地址了。
好在可以通過消息結(jié)構(gòu)(struct msg_msg)的?m_ts字段,再利用 copy_msg()函數(shù)
來讀取出被雙重指向的1024消息的相鄰消息。(設(shè)置被雙重指向的1024消息的m_ts字段大于DATALEN_MSG(4096 - sizeof(struct msg_msg))?),再通過該相鄰消息找出被雙重指定的1024消息的內(nèi)核內(nèi)存地址,現(xiàn)在我們得到了內(nèi)核內(nèi)存地址,就可以把我們的ROP鏈的代碼寫到該內(nèi)核內(nèi)存地址上,從而達到繞過SMAP機制。
5.4?解除內(nèi)核消息隊列的限制
在內(nèi)核的消息隊列,也就只是消息結(jié)構(gòu)類型(struct msg_msg),并且還需要保證里面的元素的合法性。換句話說,我們就不能使用消息隊列的對象來做內(nèi)核內(nèi)存的修改對象,因為沒有可塑性。
需要找一種有可塑造的內(nèi)核結(jié)構(gòu)消息來指向該內(nèi)核消息隊列內(nèi)存地址。
這里使用?struct sk_buff?結(jié)構(gòu)體類型,?sk_buff就沒有?struct msg_msg的內(nèi)核限制。
利用雙重指向的1024消息的其中一個引用,來釋放該1024消息。再使用struct sk_buff類型的消息來堆噴1024大小,正常情況下,通過堆噴會找到剛釋放出被雙重指定的1024消息。而這樣做的目的是為了通過另一種控制的方式(sk_buff)來指向了該內(nèi)核內(nèi)存地址。
到目前為止,我們拿到了一段內(nèi)核內(nèi)存,大小1024,并且還一個struct sk_buff類型的指針可以對它進行操作。
(為了方便大家理解,再次解釋一下:該步驟的目的是為了把同一塊內(nèi)存的控制管理權(quán)限轉(zhuǎn)交給?struct sk_buff?結(jié)構(gòu);因為在原消息結(jié)構(gòu)?struct msg_msg?類型在內(nèi)核中是不允許對結(jié)構(gòu)體做超范圍的操作)
5.5?找一個有回調(diào)機制的結(jié)構(gòu)
也就是找到一個結(jié)構(gòu)體內(nèi)有函數(shù)指針的對象。
這里選用?struct pipe_buffer?結(jié)構(gòu)對象,該結(jié)構(gòu)體中的?const struct pipe_buf_operations *ops;剛好可以用來存放回調(diào)函數(shù)。
該結(jié)構(gòu)體剛好占1024字節(jié),同時也很容易使用?pipe()函數(shù)來制成。
5.6?繞過KASLR?&?SMEP
調(diào)用pipe()申請?struct pipe_buffer?結(jié)構(gòu)時,ops字段的內(nèi)容會默認填充為anon_pipe_buf_ops,而內(nèi)容又存在內(nèi)核的?.data段內(nèi):
因為在內(nèi)核中的.data和.text段之間的偏移固定,我們可以計算內(nèi)核程序代碼基地址。
這就又要用到堆噴技術(shù)了,在上面我們提到有一段內(nèi)核內(nèi)存被sk_buff指向,同時還被另一個消息隊列給指針。
利用消息隊列釋放該段內(nèi)核內(nèi)存。隨后,調(diào)用大量的pipe()函數(shù)來實現(xiàn)堆噴,找回剛才被釋放的內(nèi)核內(nèi)存。
這樣同一塊內(nèi)核內(nèi)存就被?pipe?和?sk_buff同時指向,同時具有操作權(quán)。
而const struct pipe_buf_operations *ops?中有一個名為?release的回調(diào)函數(shù),會在 pipe被閉時實調(diào)用到。
利用內(nèi)核內(nèi)存部署上我們的ROP攻擊鏈(也就是我們要提升權(quán)限的函數(shù)),把并ROP鏈的觸發(fā)寫到ops的release內(nèi)。
ROP鏈代碼:
最后關(guān)閉pipe會調(diào)用release,也是會執(zhí)行到我們的權(quán)限提升函數(shù),得到root權(quán)限。
到此已完成了漏洞的利用。再利用root權(quán)限返回一個root權(quán)限的shell。
6 ?修復(fù)建議
請盡快升級Linux內(nèi)核到安全版本,如下:
Linux Kernel 5.12(b29c457a6511435960115c0f548c4360d5f4801d),5.10.31, 5.4.113, 4.19.188, 4.14.231, 4.9.267, 4.4.267.
臨時修補建議:
RedHat建議,用戶可通過以下命令禁止非特權(quán)用戶執(zhí)行CLONE_NEWUSER、CLONE_NEWNET來執(zhí)行此漏洞:
echo 0 > /proc/sys/user/max_user_namespaces7 參考
https://github.com/google/security-research/blob/master/pocs/linux/cve-2021-22555/writeup.md
https://github.com/google/security-research/security/advisories/GHSA-xxx5-8mvq-3528
- END -
看完一鍵三連在看,轉(zhuǎn)發(fā),點贊
是對文章最大的贊賞,極客重生感謝你
推薦閱讀
圖解Linux 內(nèi)核TCP/IP 協(xié)議棧實現(xiàn)|Linux網(wǎng)絡(luò)硬核系列
網(wǎng)絡(luò)排障全景指南手冊v1.0精簡版pdf
一個奇葩的網(wǎng)絡(luò)問題
總結(jié)
以上是生活随笔為你收集整理的一个内核网络漏洞详解|容器逃逸的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云计算的前生今世
- 下一篇: C语言指针-从底层原理到花式技巧,用图文