Linux內核eBPF verifier邊界計算錯誤漏洞分析與利用(CVE-2021-31440)

發布時間 2021-05-31

漏洞背景


近日,ZDI官網披露一個Linux內核eBPF verifier邊界計算錯誤漏洞,該漏洞源于eBPF驗證器在Linux內核中沒有正確計算64位轉32位操作的寄存器邊界,導致本地攻擊者可以利用此缺陷進行內核信息泄露或特權提升,該漏洞編號為CVE-2021-31440。


影響范圍與防護措施


(1)影響范圍Linux-5.7 ~ Linux- 5.11.15Ubuntu 20.10

(2)防護措施

及時更新升級內核將kernel.unprivileged_bpf_disabled.sysctl設置為1,臨時限制普通用戶權限 


漏洞原理與調試分析


(1)漏洞原理

該漏洞和CVE-2020-8835,CVE-2020-27194這兩個漏洞的原理類似,均是在32位和64位之間進行轉換操作時,錯誤計算了寄存器的約束邊界,導致可以繞過驗證器檢查實現越界讀寫。缺陷代碼出現在kernel/bpf/verifier.c的__reg_combine_64_into_32()函數中,該函數是在commit_id:3f50f132d840中引入的,該功能實現了用64位寄存器上的已知范圍來推斷該寄存器低32位的范圍,但是同樣出現了類似的計算錯誤,該函數實現如下:


1.jpg


行1316,如果smin_value和smax_value都在帶符號的32位整數范圍內,則將相應地更新32位的帶符號范圍大小,對于有符號范圍來說,這種操作是正確的。接著看,在無符號范圍的相應邏輯中,對umin_value和umax_value分別在行1320和行1322進行了檢查。這里邏輯不正確,例如設置dreg->umin_value=1,dreg->umax_value=1<<32,即0x100000000,當進行如上操作后,reg->u32_min_value設置為1,這個是正確的,但是reg->u32_max_value卻變成了0,高位被截斷。這時reg寄存器的低32位范圍已經混亂。對于驗證器來說是混亂的,但是運行態時,reg的范圍是正常的。其實對于有符號邊界的情況,已經進行了修改。補丁commit為:b02709587ea3,關鍵補丁代碼如下所示:


2.jpg


而未對無符號邊界的情況進行解決。該漏洞補丁中,修改為同時對umin_value和umax_value進行了判斷,如下所示:


3.jpg


(2)調試分析


首先將BPF_REG_7寄存器設置為1<<32,即0x10000000,并通過兩個連續的NEG指令使驗證器無法跟蹤寄存器的范圍,同時可以保證寄存器的值在運行時不變??梢酝ㄟ^如下BPF指令實現:


4.jpg


執行到LSH指令時,如下所示:


5.jpg


此時BPF_REG_7寄存器的狀態如下所示:


6.jpg


執行完LSH后,此時BPF_REG_7寄存器的狀態如下圖所示:


7.jpg


但是此時umin_value也是0x100000000,還需將umin_value設置成0x1,可以通過如下eBPF指令實現:


8.jpg


斷點命中后,調用棧如下所示:


9.jpg


對BPF_JGE和BPF_JGT指令進行處理,這里不是32位指令操作,執行如下代碼:


10.jpg


如果R7 >= 0x1,則驗證器正確分支上,true_reg->umin_value設置為true_reg->umin_value和true_umin之間的最大值,這里設置成true_umin,為0x1。然后調用__reg_combine_64_into_32()函數更新一下true_reg的范圍。如下代碼所示:


11


進入該函數后,首先判斷有符號范圍的情況,如下代碼所示:


13.jpg


這里同時判斷有符號大小值,結果不為真,不進入if語句,因此不會修改32位的有符號大小值,打印true_reg的狀態如下所示:


14.jpg


然后開始判斷無符號最小值的情況,結果為真,然后修改32位無符號最小值,如下代碼:


15.jpg


 由于這里分開進行判斷,可以成功設置reg->u32_min_value為0x1。接下來判斷無符號最大值,reg->umax_value為0xffffffffffffffff,大于0xffffffff。因此條件不為真,不修改reg->u32_max_value。最后true_reg的狀態如下所示:


16.jpg


將寄存器的umin_value和u32_min_value都設置為0x1。接下來通過如下eBPF指令組合將u32_max_value也設置為0x1。如下所示:


17.jpg


該指令為W7<=0x1,W7為32位寄存器。命中斷點后,調用棧如下所示:


18.jpg


如果W7<=0x1,接下來設置正確分支下的true_reg->u32_max_value,如下圖所示:


19.jpg


行7200,將true_reg->u32_max_value設置為true_umax,為0x1。此時true_reg的狀態如下所示:


20.jpg


然后調用__reg_combine_32_into_64()函數更新true_reg的范圍,如下所示:


21.jpg


更新范圍后,最后true_reg的狀態如下所示:


22.jpg


此時在驗證器的視角中,R7寄存器的32位范圍是固定值,為常數0x1。接下來通過如下eBPF組合將R7變換成0,如下所示:


23.jpg


首先通過MOV32將R7的64位范圍也設置常數0x1。執行完MOV32指令后,在驗證器的視角下R7寄存器的狀態如下所示:


24.jpg


而在運行時,R7的值為1<<32,即0x100000000,低32為0,即R7的32位范圍為常數0,然后通過MUL和ADD兩次操作,將R7寄存器的狀態轉換成在驗證器的視角下為0x0,在運行時為0x1,最終便可以實現越界讀寫。


漏洞復現


在Linux-5.11.0內核版本的特定測試環境中進行漏洞利用測試,成功提權。


25.jpg


參考鏈接


1.https://www.zerodayinitiative.com/blog/2021/5/26/cve-2021-31440-an-incorrect-bounds-calculation-in-the-linux-kernel-ebpf-verifier
2.https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=10bf4e83167cc68595b85fd73bb91e8f2c086e36
3.https://github.com/torvalds/linux/commit/b02709587ea3d699a608568ee8157d8db4fd8cae
4.https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-31440


啟明星辰積極防御實驗室(ADLab)


ADLab成立于1999年,是中國安全行業最早成立的攻防技術研究實驗室之一,微軟MAPP計劃核心成員,“黑雀攻擊”概念首推者。截止目前,ADLab已通過CVE累計發布安全漏洞近1100個,通過 CNVD/CNNVD累計發布安全漏洞1000余個,持續保持國際網絡安全領域一流水準。實驗室研究方向涵蓋操作系統與應用系統安全研究、智能終端安全研究、物聯網智能設備安全研究、Web安全研究、工控系統安全研究、云安全研究。研究成果應用于產品核心技術研究、國家重點科技項目攻關、專業安全服務等。


adlab.jpg