溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行Ubuntu kernel eBPF 0day分析

發(fā)布時(shí)間:2021-12-24 10:58:56 來源:億速云 閱讀:160 作者:柒染 欄目:安全技術(shù)

今天就跟大家聊聊有關(guān)如何進(jìn)行Ubuntu kernel eBPF 0day分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

0x01 背景

中國(guó)武術(shù)博大精深,其中太極作為不以拙力勝人的功夫備受推崇。同樣如果從攻擊的角度窺視漏洞領(lǐng)域,也不難看出攻防之間的博弈不乏“太極”的身影,輕巧穩(wěn)定易利用的漏洞與工具往往更吸引黑客,今天筆者要著墨分析的就是這樣一個(gè)擅長(zhǎng)“四兩撥千斤”的0day漏洞。

0day漏洞的攻擊威力想必大家都聽說過,內(nèi)核0day更因?yàn)槠溆绊懛秶鷱V,修復(fù)周期長(zhǎng)而備受攻擊者的青睞。近期,國(guó)外安全研究者Vitaly Nikolenko在twitter[1]上公布了一個(gè)Ubuntu 16.04的內(nèi)核0day利用代碼[2],攻擊者可以無門檻的直接利用該代碼拿到Ubuntu的最高權(quán)限(root);雖然只影響特定版本,但鑒于Ubuntu在全球擁有大量用戶,尤其是公有云用戶,所以該漏洞對(duì)企業(yè)和個(gè)人用戶還是有不小的風(fēng)險(xiǎn)。 

筆者對(duì)該漏洞進(jìn)行了技術(shù)分析,不管從漏洞原因還是利用技術(shù)看,都相當(dāng)有代表性,是Data-Oriented Attacks在linux內(nèi)核上的一個(gè)典型應(yīng)用。僅利用傳入的精心構(gòu)造的數(shù)據(jù)即可控制程序流程,達(dá)到攻擊目的,完全繞過現(xiàn)有的一些內(nèi)存防護(hù)措施,有著“四兩撥千斤”的效果 。

0x02 漏洞原因

這個(gè)漏洞存在于Linux內(nèi)核的eBPF模塊,我們先來簡(jiǎn)單了解下eBPF。

eBPF(extended Berkeley Packet Filter)是內(nèi)核源自于BPF的一套包過濾機(jī)制,嚴(yán)格來說,eBPF的功能已經(jīng)不僅僅局限于網(wǎng)絡(luò)包過濾,利用它可以實(shí)現(xiàn)kernel tracing,tracfic control,應(yīng)用性能監(jiān)控等強(qiáng)大功能。為了實(shí)現(xiàn)如此強(qiáng)大的功能,eBPF提供了一套類RISC指令集,并實(shí)現(xiàn)了該指令集的虛擬機(jī),使用者通過內(nèi)核API向eBPF提交指令代碼來完成特定的功能。

看到這里,有經(jīng)驗(yàn)的安全研究者可能會(huì)想到,能向內(nèi)核提交可控的指令代碼去執(zhí)行,很可能會(huì)帶來安全問題。事實(shí)也確實(shí)如此,歷史上BPF存在大量漏洞 [3]。關(guān)于eBPF的更多細(xì)節(jié),可以參考這里[4][5]。

eBPF在設(shè)計(jì)時(shí)當(dāng)然也考慮了安全問題,它在內(nèi)核中實(shí)現(xiàn)了一套verifier機(jī)制,過濾不合規(guī)的eBPF代碼。然而這次的漏洞就出在eBPF的verifier機(jī)制。

從最初Vitaly Nikolenko公布的補(bǔ)丁截圖,我們初步判斷該漏洞很有可能和CVE-2017-16995是同一個(gè)漏洞洞[6],但隨后有2個(gè)疑問:

1.CVE-2017-16995在去年12月份,內(nèi)核4.9和4.14及后續(xù)版本已經(jīng)修復(fù),為何Ubuntu使用的4.4版本沒有修復(fù)?

2.CVE-2017-16995是Google Project Zero團(tuán)隊(duì)的Jann Horn發(fā)現(xiàn)的eBPF漏洞,存在于內(nèi)核4.9和4.14版本[7],作者在漏洞報(bào)告中對(duì)漏洞原因只有簡(jiǎn)短的描述,跟本次的漏洞是否完全相同?

注:筆者所有的代碼分析及調(diào)試均基于Ubuntu 14.04,內(nèi)核版本為4.4.0-31-generic #50~14.04.1-Ubuntu[8]。

先來回答第二個(gè)問題,中間的調(diào)試分析過程在此不表。

參考以下代碼,eBPF的verifer代碼(kernel/bpf/verifier.c)中會(huì)對(duì)ALU指令進(jìn)行檢查(check_alu_op),該段代碼最后一個(gè)else分支檢查的指令是:

1.BPF_ALU64|BPF_MOV|BPF_K,把64位立即數(shù)賦值給目的寄存器;

2.BPF_ALU|BPF_MOV|BPF_K,把32位立即數(shù)賦值給目的寄存器;

但這里并沒有對(duì)2條指令進(jìn)行區(qū)分,直接把用戶指令中的立即數(shù)insn->imm賦值給了目的寄存器,insn->imm和目的寄存器的類型是integer,這個(gè)操作會(huì)有什么影響呢?

如何進(jìn)行Ubuntu kernel eBPF 0day分析我們?cè)賮砜聪?,eBPF運(yùn)行時(shí)代碼(kernel/bpf/core.c),對(duì)這2條指令的解釋是怎樣的(bpf_prog_run)。

參考以下代碼,上面2條ALU指令分別對(duì)應(yīng)ALU_MOV_K和ALU64_MOV_K,可以看出verifier和eBPF運(yùn)行時(shí)代碼對(duì)于2條指令的語義解釋并不一樣,DST是64bit寄存器,因此ALU_MOV_K得到的是一個(gè)32bit unsigned integer,而ALU64_MOV_K會(huì)對(duì)imm進(jìn)行sign extension,得到一個(gè)signed 64bit integer。如何進(jìn)行Ubuntu kernel eBPF 0day分析至此,我們大概知道漏洞的原因,這個(gè)邏輯與CVE-2017-16995基本一致,雖然代碼細(xì)節(jié)上有些不同(內(nèi)核4.9和4.14對(duì)verifier進(jìn)行了較大調(diào)整)。但這里的語義不一致又會(huì)造成什么影響?

我們?cè)賮砜聪聉efier中以下代碼(check_cond_jmp_op),這段代碼是對(duì)BPF_JMP|BPF_JNE|BPF_IMM指令進(jìn)行檢查,這條指令的語義是:如果目的寄存器立即數(shù)==指令的立即數(shù)(insn->imm),程序繼續(xù)執(zhí)行,否則執(zhí)行pc+off處的指令;注意判斷立即數(shù)相等的條件,因?yàn)榍懊鍭LU指令對(duì)32bit和64bit integer不加區(qū)分,不論imm是否有符號(hào),在這里都是相等的。如何進(jìn)行Ubuntu kernel eBPF 0day分析再看下eBPF運(yùn)行時(shí)對(duì)BPF_JMP|BPF_JNE|BPF_IMM指令的解釋(bpf_prog_run),顯然當(dāng)imm為有符合和無符號(hào)時(shí),因?yàn)閟ign    extension,DST!=IMM結(jié)果是不一樣的。如何進(jìn)行Ubuntu kernel eBPF 0day分析注意這是條跳轉(zhuǎn)指令,這里的語義不一致后果就比較直觀了,相當(dāng)于我們可以通過ALU指令的立即數(shù),控制跳轉(zhuǎn)指令的邏輯。這個(gè)想象空間就比較大了,也是后面漏洞利用的基礎(chǔ),比如可以控制eBPF程序完全繞過verifier機(jī)制的檢查,直接在運(yùn)行時(shí)執(zhí)行惡意代碼。

值得一提的是,雖然這個(gè)漏洞的原因和CVE-2017-16995基本一樣,但但控制跳轉(zhuǎn)指令的思路和CVE-2017-16995中Jann Horn給的POC思路并不一樣。感興趣的讀者可以分析下,CVE-2017-16995中POC,因?yàn)锳LU sign extension的缺陷,導(dǎo)致eBPF中對(duì)指針的操作會(huì)計(jì)算不正確,從而繞過verifier的指針檢查,最終讀寫任意kernel內(nèi)存。但這種利用方法,在4.4的內(nèi)核中是行不通的,因?yàn)?.4內(nèi)核的eBPF不允許對(duì)指針類型進(jìn)行ALU運(yùn)算。

到這里,我們回過頭來看下第一個(gè)問題,既然漏洞原因一致,為什么Ubuntu 4.4的內(nèi)核沒有修復(fù)該漏洞呢?和Linux kernel的開發(fā)模式有關(guān)。

Linux kernel分mainline,stable,longterm 3種版本[9],一般安全問題都會(huì)在mainline中修復(fù),但對(duì)于longterm,僅會(huì)選擇重要的安全補(bǔ)丁進(jìn)行backport,因此可能會(huì)出現(xiàn),對(duì)某個(gè)漏洞不重視或判斷有誤,導(dǎo)致該漏洞仍然存在于longterm版本中,比如本次的4.4 longterm,最初Jann Horn并沒有在報(bào)告中提到影響4.9以下的版本。

關(guān)于Linux kernel對(duì)longterm版本的維護(hù),爭(zhēng)論由來已久[10],社區(qū)主流意見是建議用戶使用最新版本。但各個(gè)發(fā)行版(比如Ubuntu)出于穩(wěn)定性及開發(fā)成本考慮,一般選擇longterm版本作為base,自行維護(hù)一套kernel。

對(duì)于嵌入式系統(tǒng),這個(gè)問題更嚴(yán)重,大量廠商代碼導(dǎo)致內(nèi)核升級(jí)的風(fēng)險(xiǎn)及成本都遠(yuǎn)高于backport安全補(bǔ)丁,因此大部分嵌入式系統(tǒng)至今也都在使用比較老的longterm版本。比如Google Android在去年P(guān)ixel /Pixel XL 2發(fā)布時(shí),內(nèi)核版本才從3.18升級(jí)到4.4,原因也許是3.18已經(jīng)進(jìn)入EOL了(End of Life),也就是社區(qū)要宣布3.18進(jìn)入死亡期了,后續(xù)不會(huì)在backport安全補(bǔ)丁到3.18,而最新的mainline版本已經(jīng)到了4.16。筆者去年也在Android kernel中發(fā)現(xiàn)了一個(gè)未修復(fù)的歷史漏洞(已報(bào)告給google并修復(fù)),但upstream在2年前就修復(fù)了。

而Vitaly Nikolenko可能是基于CVE-2017-16995的報(bào)告,在4.4版本中發(fā)現(xiàn)存在類似漏洞,并找到了一個(gè)種更通用的利用方法(控制跳轉(zhuǎn)指令)。

0x03 漏洞利用

根據(jù)上一節(jié)對(duì)漏洞原因的分析,我們利用漏洞繞過eBPF verifier機(jī)制后,就可以執(zhí)行任意eBPF支持的指令,當(dāng)然最直接的就是讀寫任意內(nèi)存。漏洞利用步驟如下:

1.構(gòu)造eBPF指令,利用ALU指令缺陷,繞過eBPF verifier機(jī)制;

2.構(gòu)造eBPF指令,讀取內(nèi)核?;罚?/p>

3.根據(jù)泄漏的SP地址,繼續(xù)構(gòu)造eBPF指令,讀取task_struct地址,進(jìn)而得到task_struct->cred地址;

4.構(gòu)造eBPF指令,覆寫cred->uid, cred->gid為0,完成提權(quán)。

漏洞利用的核心,在于精心構(gòu)造的惡意eBPF指令,這段指令在Vitaly Nikolenko的exp中是16機(jī)制字符串(char *__prog),并不直觀,筆者為了方便,寫了個(gè)小工具,把這些指令還原成比較友好的形式,當(dāng)然也可以利用eBPF的調(diào)試機(jī)制,在內(nèi)核log中打印出eBPF指令的可讀形式。我們來看下這段eBPF程序,共41條指令(筆者寫的小工具的輸出):

parsing eBPF prog, size 328, len 41
ins 0: code(b4) alu | = | imm, dst_reg 9, src_reg 0, off 0, imm ffffffff
ins 1: code(55) jmp | != | imm, dst_reg 9, src_reg 0, off 2, imm ffffffff
ins 2: code(b7) alu64 | = | imm, dst_reg 0, src_reg 0, off 0, imm 0
ins 3: code(95) jmp | exit | imm, dst_reg 0, src_reg 0, off 0, imm 0
ins 4: code(18) ld | BPF_IMM | u64, dst_reg 9, src_reg 1, off 0, imm 3
ins 5: code(00) ld | BPF_IMM | u32, dst_reg 0, src_reg 0, off 0, imm 0
ins 6: code(bf) alu64 | = | src_reg, dst_reg 1, src_reg 9, off 0, imm 0
ins 7: code(bf) alu64 | = | src_reg, dst_reg 2, src_reg a, off 0, imm 0
ins 8: code(07) alu64 | += | imm, dst_reg 2, src_reg 0, off 0, imm fffffffc
ins 9: code(62) st | BPF_MEM | u32, dst_reg a, src_reg 0, off fffffffc, imm 0
ins 10: code(85) jmp | call | imm, dst_reg 0, src_reg 0, off 0, imm 1
ins 11: code(55) jmp | != | imm, dst_reg 0, src_reg 0, off 1, imm 0
ins 12: code(95) jmp | exit | imm, dst_reg 0, src_reg 0, off 0, imm 0
ins 13: code(79) ldx | BPF_MEM | u64, dst_reg 6, src_reg 0, off 0, imm 0
ins 14: code(bf) alu64 | = | src_reg, dst_reg 1, src_reg 9, off 0, imm 0
ins 15: code(bf) alu64 | = | src_reg, dst_reg 2, src_reg a, off 0, imm 0
ins 16: code(07) alu64 | += | imm, dst_reg 2, src_reg 0, off 0, imm fffffffc
ins 17: code(62) st | BPF_MEM | u32, dst_reg a, src_reg 0, off fffffffc, imm 1
ins 18: code(85) jmp | call | imm, dst_reg 0, src_reg 0, off 0, imm 1
ins 19: code(55) jmp | != | imm, dst_reg 0, src_reg 0, off 1, imm 0
ins 20: code(95) jmp | exit | imm, dst_reg 0, src_reg 0, off 0, imm 0
ins 21: code(79) ldx | BPF_MEM | u64, dst_reg 7, src_reg 0, off 0, imm 0
ins 22: code(bf) alu64 | = | src_reg, dst_reg 1, src_reg 9, off 0, imm 0
ins 23: code(bf) alu64 | = | src_reg, dst_reg 2, src_reg a, off 0, imm 0
ins 24: code(07) alu64 | += | imm, dst_reg 2, src_reg 0, off 0, imm fffffffc
ins 25: code(62) st | BPF_MEM | u32, dst_reg a, src_reg 0, off fffffffc, imm 2
ins 26: code(85) jmp | call | imm, dst_reg 0, src_reg 0, off 0, imm 1
ins 27: code(55) jmp | != | imm, dst_reg 0, src_reg 0, off 1, imm 0
ins 28: code(95) jmp | exit | imm, dst_reg 0, src_reg 0, off 0, imm 0
ins 29: code(79) ldx | BPF_MEM | u64, dst_reg 8, src_reg 0, off 0, imm 0
ins 30: code(bf) alu64 | = | src_reg, dst_reg 2, src_reg 0, off 0, imm 0
ins 31: code(b7) alu64 | = | imm, dst_reg 0, src_reg 0, off 0, imm 0
ins 32: code(55) jmp | != | imm, dst_reg 6, src_reg 0, off 3, imm 0
ins 33: code(79) ldx | BPF_MEM | u64, dst_reg 3, src_reg 7, off 0, imm 0
ins 34: code(7b) stx | BPF_MEM | u64, dst_reg 2, src_reg 3, off 0, imm 0
ins 35: code(95) jmp | exit | imm, dst_reg 0, src_reg 0, off 0, imm 0
ins 36: code(55) jmp | != | imm, dst_reg 6, src_reg 0, off 2, imm 1
ins 37: code(7b) stx | BPF_MEM | u64, dst_reg 2, src_reg a, off 0, imm 0
ins 38: code(95) jmp | exit | imm, dst_reg 0, src_reg 0, off 0, imm 0
ins 39: code(7b) stx | BPF_MEM | u64, dst_reg 7, src_reg 8, off 0, imm 0
ins 40: code(95) jmp | exit | imm, dst_reg 0, src_reg 0, off 0, imm 0
parsed 41 ins, total 41

稍微解釋下,ins 0 和 ins 1 一起完成了繞過eBPF verifier機(jī)制。ins 0指令后,regs[9] = 0xffffffff,但在verifier中,regs[9].imm = -1,當(dāng)執(zhí)行ins 1時(shí),jmp指令判斷regs[9] == 0xffffffff,注意regs[9]是64bit integer,因?yàn)閟ign extension,regs[9] == 0xffffffff結(jié)果為false,eBPF跳過2(off)條指令,繼續(xù)往下執(zhí)行;而在verifier中,jmp指令的regs[9].imm    == insn->imm結(jié)果為true,程序走另一個(gè)分支,會(huì)執(zhí)行ins 3 jmp|exit指令,導(dǎo)致verifier認(rèn)為程序已結(jié)束,不會(huì)去檢查其余的dead code。

這樣因?yàn)閑BPF的檢測(cè)邏輯和運(yùn)行時(shí)邏輯不一致,我們就繞過了verifier。后續(xù)的指令就是配合用戶態(tài)exp完成對(duì)kernel內(nèi)存的讀寫。

這里還需要知道下eBPF的map機(jī)制,eBPF為了用戶態(tài)更高效的與內(nèi)核態(tài)交互,設(shè)計(jì)了一套map機(jī)制,用戶態(tài)程序和eBPF程序都可以對(duì)map區(qū)域的內(nèi)存進(jìn)行讀寫,交換數(shù)據(jù)。利用代碼中,就是利用map機(jī)制,完成用戶態(tài)程序與eBPF程序的交互。

ins4-ins5: regs[9] = struct bpf_map *map,得到用戶態(tài)程序申請(qǐng)的map的地址,注意這2條指令,筆者的靜態(tài)解析并不準(zhǔn)確,獲取map指針的指令,在eBPF verifier中,會(huì)對(duì)指令內(nèi)容進(jìn)行修改,替換map指針的值。

ins6-ins12: 調(diào)用bpf_map_lookup_elem(map, &key),返回值為regs[0] = &map->value[0]

ins13: regs[6] = *regs[0], regs[6]得到map中key=0的value值

ins14-ins20: 繼續(xù)調(diào)用bpf_map_lookup_elem(map, &key),regs[0] = &map->value[1]

ins21: regs[7] = *regs[0],regs[7]得到map中key=1的value值

ins22-ins28: 繼續(xù)調(diào)用bpf_map_lookup_elem(map, &key),regs[0] = &map->value[2]

ins29: regs[8] = *regs[0],regs[8]得到map中key=2的value值

ins30: regs[2] = regs[0]

ins32: if(regs[6] != 0) jmp ins32 + 3,根據(jù)用戶態(tài)傳入的key值不同,做不同的操作

ins33: regs[3] = *regs[7],讀取regs[7]中地址的內(nèi)容,用戶態(tài)的read原語,就在這里完成,regs[7]中的地址為用戶態(tài)傳入的任意內(nèi)核地址

ins34: *regs[2] = regs[3],把上調(diào)指令讀取的值返回給用戶態(tài)

ins36: if(regs[6] != 1) jmp ins36 + 2

ins37: *regs[2] = regs[FP], 讀取eBPF的運(yùn)行時(shí)棧指針,返回給用戶態(tài),注意這個(gè)eBPF的棧指針實(shí)際上指向bpf_prog_run函數(shù)中的一個(gè)局部uint64數(shù)組,在內(nèi)核棧上,從這個(gè)值可以得到內(nèi)核棧的基址,這段指令對(duì)應(yīng)用戶態(tài)的get_fp

ins39: *regs[7] = regs[8],向regs[7]中的地址寫入regs[8],對(duì)應(yīng)用戶態(tài)的write原語,regs[7]中的地址為用戶態(tài)傳入的任意內(nèi)核地址

理解了這段eBPF程序,再看用戶態(tài)exp就很容易理解了。需要注意的是,eBPF指令中的3個(gè)關(guān)鍵點(diǎn):泄漏FP,讀任意kernel地址,寫任意kernel地址,在verifier中都是有檢查的,但因?yàn)殚_始的2條指令完全繞過了verifier,導(dǎo)致后續(xù)的指令長(zhǎng)驅(qū)直入。

筆者在Ubuntu 14.04上提權(quán)成功:這種攻擊方式和傳統(tǒng)的內(nèi)存破壞型漏洞不同,不需要做復(fù)雜的內(nèi)存布局,只需要修改用戶態(tài)傳入的數(shù)據(jù),就可以達(dá)到控制程序指令流的目的,利用的是原有程序的正常功能,會(huì)完全繞過現(xiàn)有的各種內(nèi)存防御機(jī)制(SMEP/SMAP等),有一種四兩撥千斤的效果。這也是這兩年流行的Data-Oriented Attacks,在linux kernel中似乎并不多見。

0x04 漏洞影響范圍&修復(fù)

因?yàn)閘inux kernel的內(nèi)核版本眾多,對(duì)于安全漏洞的影響范圍往往并不容易確認(rèn),最準(zhǔn)確的方式是搞清楚漏洞根因后,從代碼層面判斷,但這也帶來了高成本的問題,快速應(yīng)急時(shí),我們往往需要盡快確認(rèn)漏洞影響范圍。從前面的漏洞原理來看,筆者大致給一個(gè)全面的linux kernel受影響版本:

3.18-4.4所有版本(包括longterm 3.18,4.1,4.4);

<3.18,因內(nèi)核eBPF還未引入verifier機(jī)制,不受影響。

對(duì)于大量用戶使用的各個(gè)發(fā)行版,還需要具體確認(rèn),因?yàn)樵撀┒吹挠|發(fā),還需要2個(gè)條件

1.Kernel編譯選項(xiàng)CONFIG_BPF_SYSCALL打開,啟用了bpf syscall;

2./proc/sys/kernel/unprivileged_bpf_disabled設(shè)置為0,允許非特權(quán)用戶調(diào)用bpf syscall

而Ubuntu正好滿足以上3個(gè)條件。關(guān)于修復(fù),upstream kernel在3月22日發(fā)布的4.4.123版已經(jīng)修復(fù)該漏洞[11][12], Ubuntu官方4月5日也正式發(fā)布了安全公告和修復(fù)版本[13][14],沒有修復(fù)的同學(xué)可以盡快升級(jí)了。

但現(xiàn)在距漏洞Exp公開已經(jīng)過去20多天了,在漏洞應(yīng)急時(shí),我們顯然等不了這么久,回過頭看看當(dāng)初的臨時(shí)修復(fù)方案:

1.設(shè)置/proc/sys/kernel/unprivileged_bpf_disabled為1,也是最簡(jiǎn)單有效的方式,雖然漏洞仍然存在,但會(huì)讓exp失效;

2.使用Ubuntu的預(yù)發(fā)布源,更新Ubuntu 4.4的內(nèi)核版本,因?yàn)槭欠钦桨?,其穩(wěn)定性無法確認(rèn)。

Vitaly Nikolenko在twitter上公布的Ubuntu預(yù)發(fā)布源:all 4.4 ubuntu aws instances are vulnerable: echo “deb http://archive.ubuntu.com/ubuntu/xenial-proposed restricted main multiverse universe” > /etc/apt/sources.list    && apt update && apt install linux-image-4.4.0-117-generic

Ubuntu的非正式內(nèi)核版本,做了哪些修復(fù),我們可以看下補(bǔ)丁的關(guān)鍵內(nèi)容(注意這是Ubuntu的kernel版本,非upstream):

git diff Ubuntu-lts-4.4.0-116.140_14.04.1 Ubuntu-lts-4.4.0-117.141_14.04.1ALU指令區(qū)分了32bit和64bit立即數(shù),同時(shí)regs[].imm改為了64bit integer

如何進(jìn)行Ubuntu kernel eBPF 0day分析

還增加了一項(xiàng)有意思的檢查,把所有的dead_code替換為nop指令,這個(gè)明顯是針對(duì)exp來的,有點(diǎn)類似于exp的mitigation,upstream kernel可能并不一定喜歡這樣的修復(fù)風(fēng)格:)如何進(jìn)行Ubuntu kernel eBPF 0day分析

關(guān)于這個(gè)漏洞,Ubuntu還有一些相關(guān)的修復(fù)代碼,感興趣的讀者,可以自行發(fā)掘。 

我們?cè)倏聪聈pstream kernel 4.4.123的修復(fù),相比之下,要簡(jiǎn)潔的多,僅有3行代碼改動(dòng)[12]:

當(dāng)處理32bit ALU指令時(shí),如果imm為負(fù)數(shù),直接忽略,認(rèn)為是UNKNOWN_VALUE,這樣也就避免了前面提到的verifer和運(yùn)行時(shí)語義不一致的問題。

如何進(jìn)行Ubuntu kernel eBPF 0day分析

另外Android kernel上,bpf sycall是沒有啟用的,所以不受該漏洞影響。

0x05 引發(fā)的思考

我們回顧以下整個(gè)漏洞分析過程,有幾點(diǎn)值得注意和思考:

1.eBPF作為內(nèi)核提供的一種強(qiáng)大機(jī)制,因?yàn)槠鋸?fù)雜的過濾機(jī)制,稍有不慎,將會(huì)引入致命的安全問題,筆者推測(cè)后續(xù)eBPF可能還會(huì)有類似安全漏洞。

2.受限于linux kernel的開發(fā)模式及眾多版本,安全漏洞的確認(rèn)和修復(fù)可能存在被忽視的情況,出現(xiàn)N day變0 day的場(chǎng)景。

3.Vitaly Nikolenko公布漏洞exp后,有網(wǎng)友就提出了批評(píng),在廠商發(fā)布正式補(bǔ)丁前,不應(yīng)該公布細(xì)節(jié)。我們暫且不討論Vitaly Nikolenko的動(dòng)機(jī),作為一名安全從業(yè)者,負(fù)責(zé)任的披露漏洞是基本守則。


看完上述內(nèi)容,你們對(duì)如何進(jìn)行Ubuntu kernel eBPF 0day分析有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI