本文提出的技術(shù)最開始在2020 BlackHat上展示,名字叫Exploiting Kernel Races Through Taming Thread Interleaving,今年又在2021 USENIX會議上發(fā)表出來ExpRace: Exploiting Kernel Races through Raising Interrupts,實驗材料尚未公布。
目標(biāo):對于多變量競爭漏洞,其觸發(fā)時指令的執(zhí)行順序很特殊,沒有人工干預(yù)的話很難觸發(fā)。
解決:提出ExpRace,采用中斷機制(rescheduling IPI,TLB shootdown IPI,membarrier IPI,hardware interrupts)來增大競爭窗口。
實驗:對10個真實CVE進行測試,全部在10~118s內(nèi)成功利用;如果不利用ExpRace的話24小時都不能利用成功。
貢獻:分析內(nèi)核數(shù)據(jù)競爭漏洞的可利用性;提出采用多種中斷機制來利用多變量競爭漏洞;對10個真實的CVE進行測試,全部利用成功。
1.內(nèi)核競爭漏洞的可利用性

內(nèi)核競爭漏洞可分為兩類,一是單變量競爭,二是多變量競爭,多變量競爭又分為包含式和非包含式。
1.1 單變量競爭
描述:見Figure 1-a,3條競爭指令A(yù)、B、C訪問同一變量,若B在AC之間修改了變量M,則C會獲得和A不同的M值,如果多線程競爭使得指令執(zhí)行順序為A->B->C,就會觸發(fā)競爭漏洞。
利用方法:蠻力攻擊。用戶層不斷調(diào)用Syscallx和Syscally。利用成功率 (通常情況下
),盡管Psingle看上去很低,但蠻力攻擊還是很有效。
1.2 多變量競爭
描述:見Figure 1-b,A、B訪問同一變量M1,C、D訪問同一變量M2,如果C在A、D之間訪問M2,則D點獲得不同的M2值,導(dǎo)致原子違例。原子違例的前提條件是嚴(yán)格按A->B->C->D的順序執(zhí)行。
原因:內(nèi)核訪問變量有一種常見的模式,這很容易產(chǎn)生多變量競爭漏洞。(i) 先搜索數(shù)據(jù)位置,例如枚舉數(shù)據(jù)結(jié)構(gòu)(list 或 tree);(ii) 根據(jù)虛地址讀取數(shù)據(jù)或更新數(shù)據(jù)。
利用挑戰(zhàn):通過蠻力攻擊,使得Tx位于Ty中(假設(shè))??赏ㄟ^線段圖來進行理解,如果
(非包含式),則競爭幾乎不可能成功。

實例-CVE-2017-15265:見Figure 2。
- 分析:Tasky采用create命令創(chuàng)建緩沖區(qū)port,并插入到
p->list,對應(yīng)A點;然后將用戶輸入拷貝到port->name,對應(yīng)D點。Taskx采用delete命令釋放port,先從p->list找到相應(yīng)的port,對應(yīng)B點;再釋放,對應(yīng)C點。 - 競爭變量:
p->list和 port - 漏洞:如果按照A->B->C->D,則會導(dǎo)致UAF。
- 利用:需要觸發(fā)UAF 3次。首先采用
msgsnd()噴射file指針;然后觸發(fā)漏洞來部分覆寫snd_seq_queue->tickq,以泄露噴射的file指針;接著,觸發(fā)漏洞來覆寫iovec結(jié)構(gòu),構(gòu)造任意讀,讀取struct file中的struct *f_cred;最后,觸發(fā)漏洞來覆寫iovec,構(gòu)造任意寫,修改cred提權(quán)。 - 問題:Tx比Ty大太多(12倍),幾乎不可能競爭成功。

2.概率模型與中斷分類
本文目標(biāo):針對非包含的多變量競爭(non-inclusive multivariable race,也即的情況),采用中斷來增大AD競爭窗口(也即Ty的值)。

競爭成功率(概率模型):TE—中斷處理耗時;Ty'=Ty+TE,稱Ty'為競爭窗口(race window)。
- (1)若Tx肯定位于Ty'中,只需看中斷是否恰好出現(xiàn)在Ty中間;
- (2)若Tx<Ty'<Tsyscallx,需保證中斷出現(xiàn)在Ty中間,且Tx位于Ty'中間;
- (3)若Tx>Ty',則很難競爭成功。

中斷分類:主要分為兩類。
(1)硬件中斷IRQ(Hardware interrupt request):通過IO-APIC,從外部硬件設(shè)備向OS發(fā)送信號;
(2)處理器間中斷IPI(Inter Processor Interrupt):從某個CPU核向其他核發(fā)送信號,例如rescheduling IPI, wake-up IPI, stop IPI, function call IPI。

3.中斷利用方法
3.1 Reschedule IPI
CONFIG_PREEMPT:設(shè)置該選項,表示如果需要調(diào)度,則會調(diào)用schedule,內(nèi)核態(tài)將被搶占。
介紹:Reschedule IPI由內(nèi)核函數(shù)smp_send_reschedule()來發(fā)送,包含參數(shù)cpu,來指定哪個核將接收到IPI。
用戶層調(diào)用:觸發(fā)smp_send_reschedule()的用戶態(tài)函數(shù)有兩種。第1種系統(tǒng)調(diào)用只需要1個進程,且相同時間內(nèi)能夠發(fā)送更多的 IPI,所以采用sched_setaffinity()調(diào)用。
- 一是用戶態(tài)調(diào)用
sched_setaffinity(),參數(shù)是pid和mask,最終設(shè)置在smp_send_reschedule()的cpu上運行指定pid進程; - 二是通過喚醒等待線程,首先綁定特定核與task A,通過
read()將線程狀態(tài)改為等待狀態(tài),然后從task B調(diào)用write()喚醒等待的線程,內(nèi)核會將task A的進程狀態(tài)從waiting改為running,并向task A所在的核發(fā)送 reschedule IPI。
方法:創(chuàng)建3個task,Taskx / Tasky / Taskint,分別在核心C0 / C1 / C2上運行(Taskx和Taskint可以是Tasky的子線程或子進程,Taskx和Tasky負責(zé)觸發(fā)競爭),Taskint負責(zé)調(diào)用sched_setaffinity(C1)—B點,內(nèi)核會將Taskint從C2遷移到C1運行隊列,并向C1發(fā)送reschedule IPI—C點。如果C1在競爭窗口Ty中接收到 reschedule IPI,就會轉(zhuǎn)而去處理IPI—D點,然后C1切換到Task int的上下文—E點,最后再調(diào)度回來—F點。這樣就增大了Ty競爭窗口。

3.2 Non-Reschedule IPI
分類:根據(jù)發(fā)送命令的不同,將非調(diào)度的IPI分為兩類,一是TLB管理,二是內(nèi)存柵欄。
3.2.1 TLB Shootdown IPI
Translation Lookaside Buffer (TLB):地址轉(zhuǎn)換旁路緩沖存儲器。虛地址轉(zhuǎn)換為物理地址,每個核都有自己的TLB,需要進行核間同步。OS實現(xiàn)TLB shootdown機制來確保TLB正確同步。
原理:如果某核更新了TLB入口,則通過向具有相同入口的其他核發(fā)送TLB shootdown IPI,告知其刷新TLB。mm_struct->cpu_bitmap負責(zé)存儲含相同頁表入口的核。
用戶層調(diào)用:mprotect()或munmap()修改內(nèi)存權(quán)限,則內(nèi)核首先刷新當(dāng)前核的TLB,再向其他核發(fā)送IPI。
方法:
- (1)Taskx和Tasky必須位于不同進程,如果二者位于同一進程(不同線程),二者會指向同一
mm_struct,且cpu_bitmap會將C0和C1都設(shè)置,導(dǎo)致IPI會發(fā)給C0和C1。Taskint則必須和Tasky位于同一進程,這樣才能有相同的頁表入口(便于發(fā)送TLB Shootdown IPI)。 - (2)Tasky或 Taskint調(diào)用
mmap()分配內(nèi)存M。 - (3)Taskx和Tasky競爭時,Taskint調(diào)用
mprotect(M, ...)修改M的權(quán)限—B點,內(nèi)核先刷新C2的TLB,并向C1發(fā)送function call IPI(因為M對應(yīng)C1的mm_struct->cpu_bitmap已經(jīng)設(shè)置了),如果C1在競爭窗口間收到IPI,就會停止Tasky并調(diào)用native_flush_tlb_one_user()來處理IPI。這樣就增大了Ty競爭窗口。

3.2.2 Memory Barrier IPI
membarrier:多處理器系統(tǒng)中,控制內(nèi)存訪問順序。membarrier需要激活特定線程上的內(nèi)存柵欄,所以要用到IPI機制來通知運行特定線程的核。
用戶層調(diào)用:membarrier(),可以從用戶層直接發(fā)送 memory barrier IPI。
方法:
- (1)Taskx和Tasky位于不同進程,才能擁有不同的mm,Taskx由Tasky fork產(chǎn)生,Taskint由Tasky調(diào)用
pthread_create()產(chǎn)生; - (2)Tasky或Taskint調(diào)用
membarrier(REGISTER)來注冊內(nèi)存柵欄; - (3)Taskx 和Tasky 觸發(fā)競爭,Taskint調(diào)用
membarrier(EXPEDITED),內(nèi)核就會向C1發(fā)送 membarrier IPI(因為C1上的Tasky和Taskint引用了相同的mm_struct); - (4)Tasky收到IPI后,就會調(diào)用
ipi_mb()來處理IPI,這樣就增大了Ty競爭窗口。

3.3 Hardware Interrupts
硬件中斷IRQ:通過IO-APIC從外設(shè)發(fā)送到處理器的電子信號。
原理:
發(fā)出IRQ后,中斷控制器將中斷發(fā)給指定的CPU核(Linux中通過bit掩碼確定目標(biāo)核,Windows通過輪詢確定目標(biāo)核),相應(yīng)的CPU核執(zhí)行中斷服務(wù)程序(ISR)。
可通過讀取procfs確定bit掩碼,確定處理指定外設(shè)請求的目標(biāo)核,例如,默認(rèn)內(nèi)核配置中,enp2s0設(shè)備對應(yīng)IRQ122,由CPU core 11處理。
發(fā)送方式:不能直接從用戶層發(fā)送,先從用戶向設(shè)備發(fā)送請求,設(shè)備再向內(nèi)核發(fā)送IRQ。兩種方式——一是發(fā)送TCP請求到以太設(shè)備,設(shè)備再向內(nèi)核發(fā)送IRQ來處理包;二是采用文件讀寫發(fā)送disk請求,disk控制器設(shè)備(如AHCI設(shè)備)向內(nèi)核發(fā)送IRQ,表示disk請求已完成。
方法:
- (1)Taskx 和Taskint 可以跟Tasky 是不同線程或進程,硬件中斷與進程線程無關(guān),先讀取
/proc/irq/#/smp_affinity獲得CPU核號(處理以太設(shè)備的IRQ的核號,暫設(shè)為C1); - (2)Taskx和Tasky 觸發(fā)競爭時,Taskint向自身發(fā)送TCP請求(外部IP—局部機器)—對應(yīng)B點;
- (3)以太設(shè)備向C1發(fā)送IRQ—C點;
- (4)若C1在Ty中間收到IRQ,就會調(diào)用相應(yīng)的ISR來處理—D點,這樣就增大了Ty競爭窗口。

4.其他操作系統(tǒng)上的中斷利用
說明:membarrier是Linux獨有,所以不用研究。這些中斷在Windows中都可用,Mac OS中Reschedule IRQ不可用,HW中斷沒測試。
Windows:
- Reschedule IPI:Windows中,優(yōu)先級比當(dāng)前線程更高的線程才能被調(diào)度。改進,用戶層使用
SetThreadAffinityMask()調(diào)用替代sched_setaffinity(),并額外調(diào)用SetThreadPriority()來設(shè)置更高的優(yōu)先級。 - TLB Shootdown IPI:需用到
VirtualAlloc()、VirtualProtect()、VirtualFree()來分配、修改、釋放內(nèi)存頁。 - HW Interrupt:不同點是,Windows沒有特定CPU來處理某個外設(shè)的IRQ,而是采用輪詢來確定CPU,所以理論上,k個CPU的話,增大Ty的幾率為Linux的1/k
Mac OS X:
- Reschedule IPI:不可用。
- TLB Shootdown IPI:采用相同的函數(shù)mmap()、mprotect()、munmap()能夠增大Ty。
- HW Interrupt:Mac OS沒有提供足夠的信息來研究其硬件斷點。

5.實驗評估
實驗設(shè)計:選取10個非包含式多變量競爭漏洞進行測試,選取的漏洞見Table 1,其中CVE-2019-1999 和 CVE-2019-2025 含有公開exp。每個漏洞最多嘗試?yán)?4h。

實驗結(jié)果:結(jié)果見Table 4。不用ExpRace的話,10個CVE在24h內(nèi)全部利用失敗。
- Reschedule成功3個,都在66s內(nèi)。
- membarrier IPI成功3個。CVE-2019-6974, CVE-2019-1999, 11eb85ec, 1a6084f8, e20a2e9c是由于Ty'太小。
- TLB shootdown成功7個。
- hardware interrupts成功10個。
說明:membarrier 和 TLB shootdown 不能用于 CVE-2019-6974 和 da1b9564,因為這兩個漏洞要求兩個導(dǎo)致競爭的syscall位于同一進程。
