保護(hù)函數(shù)返回的利器——Linux Shadow Call Stack
寫在前面
提到內(nèi)核棧溢出的漏洞緩解,許多朋友首先想到的是棧內(nèi)金絲雀(Stack Canary)。今天向大家介紹一項(xiàng)在近年,于Android設(shè)備中新增,且默默生效的安全機(jī)制——影子調(diào)用棧:SCS(Shadow Call Stack)。
功能介紹
在通常的函數(shù)調(diào)用中,被調(diào)用函數(shù)的返回地址存儲在棧上,攻擊者可以通過篡改棧上返回地址劫持程序的執(zhí)行流,常見的攻擊方式如通過溢出覆蓋返回地址、ROP(Return Oriented Programming)攻擊等。
SCS是一項(xiàng)基于LLVM(Low Level Virtual Machine)的安全功能,它通過在函數(shù)調(diào)用時(shí)使用一種安全棧幀來解決這個(gè)問題。SCS開啟后,這個(gè)安全棧幀上存儲了函數(shù)調(diào)用的返回地址,而不是直接存儲在常規(guī)棧上。函數(shù)在使用返回地址時(shí),將直接從安全棧幀中讀取。從而避免了傳統(tǒng)的、針對棧上返回地址的攻擊方法。
Google要求

自Android R開始,AOSP(Android Open Source Project)的CDD(Compatibility Definition Document)要求中,就已強(qiáng)烈建議使能CFI, SCS, IntSan。而在kernel 5.4的版本AOSP的kernel版本默認(rèn)開啟。
依賴條件
SCS需要硬件支持,目前只在特定處理器架構(gòu)上受到支持,如x86_64架構(gòu)中的Intel CET或aarch64架構(gòu)中的Pointer Authentication。這些硬件提供了必要的指令和功能來支持SCS的運(yùn)行。
SCS目前僅支持aarch64架構(gòu),有數(shù)據(jù)表明在X86_64架構(gòu)上具有嚴(yán)重性能和安全缺陷,LLVM 在9.0中已將其刪除。

開啟方法
根據(jù)Google介紹,可以為整個(gè)內(nèi)核或者單獨(dú)為某個(gè)用戶空間的進(jìn)程、服務(wù)開啟SCS,開啟方法請參考:


或在編譯時(shí)將-fsanitize=shadow-call-stack 標(biāo)志傳遞給鏈接命令行。如果當(dāng)前代碼不需要應(yīng)用SCS,可以使用__attribute__((no_sanitize("shadow-call-stack")))對函數(shù)聲明。
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ?


安全原理
SCS旨在補(bǔ)充?-fstack-protector,以構(gòu)建安全縱深防御的目標(biāo),防止非線性溢出等任意攻擊棧上返回地址的手段。
在 Aarch64 架構(gòu)上,SCS使用 X18 寄存器來引用影子調(diào)用棧,這意味著不必再將 SCS的引用存儲在內(nèi)存中。以此避免將影子調(diào)用堆棧的地址暴露給可以讀取任意內(nèi)存的攻擊者。SCS不需要錯(cuò)誤處理,因?yàn)樗鼰o條件地信任、使用影子堆棧里的返回地址。
以下面這段demo為例:
Int ?foo () {
??? Return bar()+1;
}
Demo轉(zhuǎn)換成匯編:
push?? %rax
callq? bar
add??? $0x1,%eax
pop??? %rcx
Retq
當(dāng)這段代碼在開啟SCS后,匯編代碼變化為:
str???? x30, [x18], #8?
stp???? x29, x30, [sp, #-16]!
mov???? x29, sp
bl????? bar
add???? w0, w0, #1
ldp???? x29, x30, [sp], #16
ldr???? x30, [x18, #-8]!
Ret
在Aarch64架構(gòu)上通用寄存器如下,X18目前被大多數(shù)ARM預(yù)置且未使用。而X30是LR,用于存放函數(shù)返回值。

上述代碼框中標(biāo)紅指令就是SCS的核心邏輯,分段解釋:
str???? x30, [x18], #8?
首先將寄存器 X30 中的數(shù)據(jù)存儲到 X18 指向的內(nèi)存地址處,然后將 X18 中的地址值增加 8 字節(jié)。
ldr???? x30, [x18, #-8]!?
將 X18 寄存器中存儲的地址值減去 8 字節(jié),然后將存儲在該地址處的數(shù)據(jù)加載到 X30 寄存器中,同時(shí)更新 X18 的值。
功能風(fēng)險(xiǎn)
SCS需要使用Aarch64的X18寄存器來存儲影子調(diào)用棧的地址,需要默認(rèn)該寄存器不會用于任何其他目的。雖然當(dāng)前所有系統(tǒng)庫都編譯為預(yù)留 X18 寄存器,但如果存在第三方庫、早期庫時(shí),需要確定第三方應(yīng)用沒有使用X18寄存器。
并且OPPO在2020年已率先同Qualcomm和MediTeK兩大芯片廠商溝通,率先開通SCS,能有效緩解劫持函數(shù)返回地址的危害,無兼容性,功能性上的風(fēng)險(xiǎn)。
功能結(jié)果
在OPPO FIND X3項(xiàng)目上,使用IDA逆向bootimage,檢測任意內(nèi)核地址的返回值,確認(rèn)具備SCS的明顯特征,如下截圖。


需要注意的是,雖然SCS是一種有效提升設(shè)備安全性的漏洞緩解技術(shù),但并不能防御所有的攻擊。因此,在Android開發(fā)時(shí),還應(yīng)該采取其他安全措施,如輸入驗(yàn)證、內(nèi)存安全編程、權(quán)限管理等來全面保護(hù)應(yīng)用程序的安全性。
原文作者:OPPO安珀實(shí)驗(yàn)室
