Linux 核心自防護專案 KSPP

2021-09-23 16:27:30 字數 4588 閱讀 1947

核心自防護是針對 linux 核心對抗自身的安全缺陷的設計與實現。這個領域涉足廣泛的問題,包括乾掉一整個型別的 bug,阻止漏洞利用的方法和積極的檢測攻擊行為。這篇文件不討論所有的議題,但這篇文件是整個核心自防護專案的乙個起點和回答一些常見的問題。(歡迎提交補丁)

在最糟糕的場景下,我們假設一名沒有許可權的本地攻擊者擁有對核心記憶體任意讀寫訪問的能力。在很多情況下,被利用的 bug 不會提供這個層面的訪問能力,但在防禦最糟糕的情況的同時我們也會討論更多限制的攻擊場景。root 使用者大大的增加了攻擊平面,而我們應該注意防禦具有許可權的本地攻擊者則是更高的門檻(特別是當攻擊者具有載入任意核心模組的場景)。

自防護系統的目標應該是讓安全防護成為預設配置,不影響效能和除錯以及經過嚴格的測試。要達到所有的目標可能不太容易,但值得我們都把這些問題羅列出來,再去解決或者接受。

針對安全漏洞利用的最基礎的防禦是降低核心可被重定向執行的區域。這包含限制暴露給使用者空間的 api,讓核心自身的 api 更難以不正確的使用,可寫核心記憶體區域的最小化,等等。

當所有的核心記憶體都是可寫,重定向執行流變得容易。降低這些核心攻擊的可用性需要針對核心記憶體更嚴格的許可權。

可執行的**和唯讀資料必須不可寫

所有核心裡可執行的記憶體區域都必須不可寫。這很明顯包含核心**自身,但我們必須考慮所有的區域:核心模組,jit 記憶體,等等。(有一些臨時的例外以支援一些特性比如指令替換指令,斷點,kprobes,等等。如果這些特性必須存在於核心,他們的實現應該在更新時的記憶體變得臨時可寫,然後恢復到原有的許可權。)

要支援這寫特性,config_debug_rodataconfig_debug_set_module_ronx(名字起的太爛)保證**不是可寫,資料不是可執行以及唯讀資料即不能寫也不能執行。

函式指標和敏感變數必須不可寫

大量的記憶體區域都有函式指標用於核心查詢和繼續執行(例如, 描述符/向量表,檔案/網路/etc 操作結構,等等)。這些變數都必須降低到最小。

很多這種變數可以通過設定 」const」 而變成唯讀,所以他們可以存在於.rodata區域而不是.data區域來獲得核心限制記憶體許可權而帶來的保護。

對於那些在__init時初始化的變數可以標記為(新的和正在開發的)__ro_after_init屬性。

剩下的需要更新的變數就比較少見了(比如 gdt)。這些需要基礎架構(類似上面提到的暫時性例外)的支援用於實現在例外更新以後的時間裡為唯讀(比如被更新時,只有 cpu 執行緒執行更新操作會被賦予對記憶體的不可中斷的寫。

從使用者空間記憶體分離出核心記憶體

核心必須永遠不能執行使用者空間的記憶體。核心也必須永遠不能在沒有顯式預期的情況下訪問使用者空間記憶體。這些規則可以被基於硬體的限制(x86 的 smep/smap,arm 的 pxn/pan)或者通過模擬(arm 的記憶體域)。這種方式阻斷了執行和資料不能傳遞到被控制的使用者空間記憶體裡,只能強制攻擊在核心記憶體中進行。

shawn: smep 是 2011 年在 intel sandybridge 中加入的特性,smap 是在 2014 年的 broadwell 中加入,arm 的 pxn 是在 armv7 中加入,pan 會在 armv8.1 中加入。遺憾的是,pax/grsecurity 的 kernexec 和 uderef 均領先廠商數年。anyway,最終 smep/smap 把兵工廠逼上了 kernel rop 的道路 ;-)

乙個簡單的為64位系統消除很多系統呼叫的方式是編譯時不帶config_compat。當然,這是一種很罕見的場景。

「seccomp」 系統為使用者空間提供了減小針對執行的程序對核心入口的可選的功能。這限制了核心 codepath 的寬度從而降低了特定 bug 的攻擊。

構建一些可行的方式只允許信任的程序訪問像 compat,使用者空間,bpf 建立和 perf 這類資源。這將核心入口點的範圍限制到正常可用於非特權使用者空間的更規則的集合中。

shawn: 在 operations 的過程中,使用一些基於 seccomp 的實現的 sandbox 是乙個比較好的選擇,可參考這篇。注:對於有風險的 binary file 建議人工審查相關的 syscall 規則。

核心不應該讓非特權使用者有能力載入核心模組,因為這會增加攻擊平面。(通過自定義子系統的按需載入模組,比如在這裡module_alias_*是可接受的。)比如,通過乙個非特權 socket api 載入乙個檔案系統的模組是不應該的: 只有 root 或者物理的本地使用者才能夠觸發檔案系統的模組載入。(甚至這在一些場景下也是值得商榷的。)

要對抗特權使用者,系統可能需要完全關閉模組載入(比如單核心編譯或者 modules_disabled sysctl),或者提供帶簽名的模組(比如config_module_sig_force或者 dm-crypt 的 loadpin)來保證 root 無法通過模組載入器介面載入任意核心**。

有很多核心的資料結構在攻擊中是可以被濫用於獲得執行控制的,至今最廣為人知的是儲存在棧上的返回位址被修改的棧緩衝區溢位。其他這類攻擊的例子存在,相關用於對抗此類攻擊的保護機制也存在。

經典的棧緩衝區溢位是越界的寫乙個儲存在棧上的變數,最終寫乙個可控制的值到棧幀的儲存返回位址。常用的防禦方案是在棧和返回位址之間放 stack canary (config_cc_stackprotector),以在函式返回前驗證。其他防禦方案包括 shadow stacks。

shawn: 需要注意的是 kernel stack canary 被觸發後系統通常會直接 panic,對於生產環境裡效能和安全風險的 trade-off 由各位自己把握。

這是一種少見的攻擊方式,用乙個 bug 觸發核心通過深度函式呼叫或者大量棧分配消耗掉棧記憶體。這種攻擊可能覆蓋核心預分配棧空間的末端和敏感結構體。為了更好的防護,有兩個重要的改變需要做:把敏感的結構體 thread_info 移到別處,和增加乙個記憶體錯誤機制在棧底用於捕捉這些溢位。

shawn: 常見的漏洞利用會根據 rsp 算出 thread_info 的位址從而去修改 addr_limit,而 pax/grsecurity 的 x86 實現早在 2011 年以前就已經把 thread_info 從 kernel stack 相鄰的位置移走了;-)

用於跟蹤堆的鍊錶的資料結構可以在分配和釋放時做 sanity-check 以保證他們沒有被用於篡改其他記憶體區域。

shawn: 來自以色列的 perception point 公布的針對 cve-2016-0728 的 poc 算是第乙個針對此種型別 bug 的公開漏洞利用,pax_refcount 可以防禦。

類似計數器溢位,整數溢位(通常是大小計算)需要在執行時被檢測到來解決掉這類通常會導致核心緩衝區越界寫的 bug。

有很多防禦是可以被認為是確定性的(比如唯讀記憶體不能被寫入),一些防護在一些必須蒐集足夠資訊的場景只提供統計性防禦。雖然不完美,但也提供了有意義的防禦。

應該注意像之前討論的 stack canary 是技術性的統計性防禦,因為他們依賴於(可洩漏)的秘密值。

致盲像在被使用者空間控制的內容,類似 jit 的逐字逐句的值,也需要乙個類似的秘密值。

關鍵是秘密值必須分離(比如每個 stack 不同的 canary)和高熵(比如,rng 真的工作嗎?)。

核心記憶體的位址幾乎是成功攻擊的重要工具,讓位址變得不確定性會增加漏洞利用的難度。(注意,這反過來會讓洩漏值更高從而可能發現所需的記憶體位址。)

**段和模組基位址

通過在啟動時(config_randomize_base)對核心的物理和虛擬位址的基位址進行重定位,需要核心**的攻擊會受挫。另外,offsetting 模組的載入基位址意味著系統每次啟動以相同的順序載入相同的模組不會共享同樣的基位址。

棧基如果核心棧的基位址對於不同的程序甚至系統呼叫都不一樣,攻擊將變得很困難。

動態記憶體基

根據早期啟動的初始化,太多的核心動態記憶體(比如 kmalloc, vmalloc, etc)都有相對確定性的記憶體布局。如果這些區域的基位址在不同的啟動是不同的,攻擊會受挫,從而需要特定區域的資訊洩漏。

敏感結構體的位址是攻擊的首要目標,很重要的是去防禦對核心記憶體位址和核心記憶體內容(他們包含核心位址或者其他敏感資訊比如 carnary 值)的洩漏。

shawn: 對於敏感結構體最好的方式是 code diversification,但這對生產環境的取證工作會帶來一些麻煩-_-

核心記憶體位址不能作為標示符暴露給使用者空間。相反,應該使用乙個原子計數器,乙個 idr 或者類似唯一的標示符。

記憶體拷貝到使用者空間必須總是完全初始化的。如果沒有顯式的 memset(),這需要修改編譯器確保這些結構體是清空的。

當釋放記憶體是,最好去汙染內容(在 syscall 返回是清空棧,在釋放後清空堆),以防止依賴於舊記憶體內容重用的攻擊。這會受挫很多未初始化變數的攻擊,棧洩漏,堆資訊洩漏和 uaf 攻擊。

為了解決掉核心位址被寫到使用者空間的 bug,寫的目的位址需要被跟蹤。如果緩衝區是使用者空間(比如 seq_file 的 /proc 檔案),就應該檢查敏感值。

原文發布時間為:2016-05-31

Linux基本防護

1 chage 修改賬戶屬性 格式 chage 選項 引數 賬戶名稱 chage l jim 最近一次密碼修改時間 9月 21,2019 密碼過期時間 從不 密碼失效時間 從不 帳戶過期時間 從不 兩次改變密碼之間相距的最小天數 0 兩次改變密碼之間相距的最大天數 99999 在密碼過期之前警告的天...

linux核心中Kconfig及如何加自己的驅動

linux核心中kconfig及如何加自己的驅動 2.6核心的原始碼樹目錄下一般都會有兩個文文 kconfig和makefile。分布在各目錄下的kconfig構成了乙個分布式的核心配置資料庫,每個kconfig分別描述了所屬目錄原始檔相關的核心配置選單。在核心配置make menuconfig 或...

linux的防護機制

1 nx dep 堆疊不可執行 將資料所在的記憶體頁標識為不可執行,當程式試圖在資料頁面執行指令時,cpu就會丟擲異常 繞過方法 rop ret2libc 關閉nx gcc z execstack o pwn pwn.c 2 cannry 棧保護 當啟用棧保護後,函式開始執行的時候會先往棧裡插入co...