__randomize_layout
資料整理: jserv
Linux 核心不僅是個作業系統的核心,其涉及的領域之廣,可說是整個資訊科技領域的縮影。Linux 核心原始程式碼的 __randomize_layout
巨集反映出資訊安全和編譯器設計的議題,本文嘗試探討該巨集的緣由及 Linux 核心的 GCC plugin 的整合機制。
Linux 核心已在超級電腦、伺服器、消費性電子產品,和工業控制等等領域獲得巨大成功,伴隨著盛名而來的,是各式資訊安全的挑戰。Kernel Self Protection Project (KSPP) 著眼於從本質層面消弭 Linux 核心的資訊安全疑慮,並彙整 PaX 及 Grsecurity 團隊提出的強化資訊安全成果。
ASLR 全名是 Address Space Layout Randomization,是種安全防禦機制,由 PaX 團隊於 2001 年正式提出,並於 2005 年導入到 Linux 核心之中,其他主流作業系統也採納 ASLR 機制。ASLR 能在每次運作可執行檔案時,藉由基底址隨機對應的方式來為其隨機配置地址空間,從而防止需要瞭解記憶體地址,來利用記憶體崩潰漏洞的攻擊行為。
2009 年,Google 安全團隊在 CanSecWest,以〈Linux ASLR Curiosities〉為題,探討 Linux ASLR 其實沒想像中強健,該演講展示藉由 /proc/[pid]/stat
及 /proc/[pid]/wchan
來獲取標的行程的 IP/SP 等資訊,從而讓攻擊者得以重建標的行程的定址空間佈局。針對 Linux ALSR 的攻擊手法則持續推陳出新,Linux 核心當然也要有所反制,僅在 Linux 核心原始程式碼中,以 find -name 'Kconfig*'' | xargs grep -i randomize
就可找到數十處在各層面強化記憶體地址隨機性的變革,這背後就充斥著 __randomize_layout
巨集的身影。
__randomize_layout
巨集攻擊者可能會利用結構體屬性的記憶體佈局發起攻擊,考慮以下程式碼:
展示用的攻擊者:
為避免這種攻擊,Linux 引入 __randomize_layout
巨集,讓編譯器將結構體內屬性的記憶體佈局隨機打亂,至於如結果如何打亂,由 seed 唯一確定。
__randomize_layout
巨集定義於 Linux 核心原始程式碼的 include/linux/compiler_types.h 標頭檔:
其中 __CHECKER__
巨集是搭配 Linux 核心靜態分析器 (sparse) 使用,我們聚焦在 RANDSTRUCT
巨集,這由 Linux 核心建構系統 Kbuild 解析 security/Kconfig.hardening
的選項,部分內容如下:
從描述中,我們可知,這項安全強化機制需要搭配編譯器,特別是 GCC plugins,後者以可載入的 GCC 模組形式,讓 GCC 得以在編譯過程中調整其行為,這樣可施加靜態分析和置入特定的程式碼,示意如下:
以 RANDSTRUCT
來說,就是藉由 GCC plugins 去變更指定 C 程式結構體的記憶體佈局。最初針對 GCC,後來 LLVM/Clang 也支援該機制,這也是前述 Kernel Self Protection Project 著重從本質上強化 Linux 核心抵禦攻擊的基礎建設。這個 GCC plugin 具備以下三個主要功能:
__randomize_layout
(實際是 __attribute__((randomize_layout))
) 的結構體都將隨機佈局。__no_randomize_layout
關閉隨機佈局你或許會好奇,攻擊者不能用 offsetof 來計算偏移量,從而破解隨機佈局機制嗎?然而 offsetof
是編譯時期的機制,但攻擊者針對編譯後的二進位內容,當然無法使用 offsetof。
使用案例 (取自 include/linux/mount.h):
考慮以下的結構體:
一旦經由 __randomize_layout
巨集和 GCC plugins 的處理後,可能會隨機產生以下 6 種記憶體佈局 (為了比較的便利,省去 struct foo
):
{ u32 a; /* 4-byte */ u64 b; u64 c; };
(原本樣貌){ u32 a; /* 4-byte */ u64 c; u64 b; };
(交換 b 和 c 的地址){ u64 b; u32 a; /* 4-byte */ u64 c; };
(交換 a 和 b 的地址){ u64 b; u64 c; u32 a; /* 4-byte */ };
{ u64 c; u32 a; /* 4-byte */ u64 b; };
{ u64 b; u64 c; u32 a; /* 4-byte */ };
前述 RANDSTRUCT 會隨機變更核心結構體內各個欄位的順序,只對包含函式指標的結構或者那些明確用 __randomize_layout
巨集標記過的結構體有效。
scripts/gcc-plugins/randomize_layout_plugin.c
對應的 GCC plugins 有以下版本:
由於維護核心模組的額外成本 (發行者要公開自己使用的 seed,允許第三方核心模組在開啟的 RANDSTRUCT
核心上運行)、性能和相容性的議題,若干 Linux 系統發行商 (distribution) 不傾向啟用。實際上能夠 RANDSTRUCT
受益的,主要是 Google, Meta, Amazon 這類雲端服務提供商。