CFI技術新探索,struct_san今日登場

雲鼎實驗室發表於2021-10-27

一、背景

C/C++開發的應用程式,長久以來存在記憶體破壞類的安全問題。當攻擊者掌握了目標程式的漏洞後,就可以開發漏洞利用程式劫持目標程式的控制流。早期的漏洞利用是採用程式碼注入的方式,透過在緩衝區置入一段程式碼(shellcode),然後控制pc暫存器跳入到緩衝執行這段程式碼。為了阻止此類攻擊,後來計算機系統部署了DEP(Data Execution Prevention)機制,透過配置記憶體頁屬性,將緩衝區設定成不可執行,起到了很好的防禦效果。為了繞過DEP,攻擊者探索出了程式碼重用技術,在目標程式中搜尋出一些攻擊者期望的操作的程式碼片段,透過組織這些片段最終完成實現對目標機器的控制。這類攻擊技術有Return-to-libc、ROP(Return OrientedProgramming)、JOP(Jump OrientedProgramming)等。如下圖所示,程式碼有兩條動態路徑,在路徑1存一個含有漏洞的節點。當攻擊者透過漏洞修改這個節點的跳轉邏輯,如果沒有可靠的合法性驗證機制,那麼攻擊者最終可以完全控制目標機器。


CFI技術新探索,struct_san今日登場


為了抵禦上面的程式碼複用攻擊,加州大學和微軟公司於2005年提出了控制流完整性(Control-Flow-Integrity, CFI)的防禦機制。Control-Flow-Integrity (CFI) 是一種確保軟體必須在先前確定的控制流圖上執行的安全策略。其核心思想就是在函式在發生不確定的跳轉時,驗證跳轉的合法性。

CFI分為Forward Edges CFI和Backward Edges CFI。前者是在間接呼叫前驗證控制流,而後者是在函式返回時驗證返回地址是否屬於呼叫者。下面羅列了Linux下相關實現,如下:


CFI技術新探索,struct_san今日登場


目前還有硬體實現的Backward CFI

* Intel CET 基於硬體的只讀影子呼叫棧

* ARM V8.3a Pointer Authentication("signed return address")

二、struct sanitizer

我們透過分析一些常見的核心漏洞POC,發現這些POC對控制流的修改都集中在幾種結構體內建函式指標的修改上。而上面的CFI的方案需要對所有程式碼進行插樁驗證控制流,這樣勢必會帶來明顯的效能下降問題。所以我們提出了struct-sanitizer(struct_san)這種新的控制流完整性檢測機制。

struct_san與上面的CFI方案相比,struct_san在對結構體指標的驗證要比已有的CFI技術更嚴苛。當前主流的CFI技術主要是驗證函式指標的型別,而struct_san在此基礎上還要驗證此函式指標是否還屬於當前結構體例項。struct_san還可以做到非全量插樁,以減少一些非不必要的效能損耗。

三、實現原理

struct_san工作原理如下:

CFI技術新探索,struct_san今日登場


struct san 透過對在結構體裡的函式呼叫前加入校驗函式
__sanitizer_struct_guard__(),來驗證此函式指標是否屬於當前結構體例項,如果驗證合法則繼續執行下面的間接呼叫函式,否則丟擲ud2。

四、使用方法

struct_san為了避免非全量插樁,新增一個GNU Attributes __attribute__ ((sanitize_struct)) 。

使用方法是在想要保護的結構體型別宣告處和呼叫此結構體的函式指標的函式前加入此關鍵字,例如想要保護核心中的pipe_buf_release()程式碼中的pipe_buf_operations->release()函式。

  1. 在結構體型別宣告時加入此關鍵字

CFI技術新探索,struct_san今日登場

在型別宣告完成以後,struct_san會將此型別的所有結構體例項儲存到.sanitize_struct段內。


2.在需要保護的函式中也要加入上面的關鍵字。例如在pipe_buf_release()函式的宣告和定義處加關鍵字,加入關鍵字後會在呼叫pipe_buf_operations->release()前插入校驗函函式__sanitizer_struct_guard__()

下面是插樁前後在gcc的gimple IR中的不同表示:

CFI技術新探索,struct_san今日登場

插樁前

CFI技術新探索,struct_san今日登場

插樁後

五、檢測演算法

struct_san目前只在核心中完成了相關實現。其演算法是在核心中開闢一個128M大小shadow memory用來儲存結構體和結構指標的對應關係。
__sanitizer_struct_guard__()在呼叫時會檢測傳入的struct和函式指標是否在shadow memory中,如果不在則丟擲一個ud2異常,否則返回函式指標。實現方案如下:

CFI技術新探索,struct_san今日登場


這個演算法參考了AddressSanitizer的實現,兼顧了效果和效率。

六、效果

以漏洞CVE-2021-22555的攻擊程式碼為例,在啟用struct_san的情況下,CFI阻斷了攻擊程式碼的執行,起到了有效的防禦。

CFI技術新探索,struct_san今日登場


七、開源地址

我們對struct_san進行了開源,期望和業界一起探討CFI技術的改進。後續我們也會推出一些其它的漏洞緩解技術。

https://github.com/YunDingLab/struct_sanitizer


相關文章