Week 3 - Buddy System Exploit
本文章涵蓋基礎 Buddy system 相關的攻擊。
Cross cache overflow
若一個 object 在寫入資料時有 heap overflow,則可以覆蓋同一個 cache 的其他 object,由於 kernel heap 結構相對複雜難控制,因此常使用 heap spray 提升成功率。
然而,若危險的 object 和目標不在同一個 cache 呢?仍可透過 cross cache overflow 覆寫內容。
Linux 的 heap 管理主要由 buddy system 和 slub allocator 組成,buddy system 儲存連續的物理記憶體 page,slub allocator 將其分成更小的 object 分配給使用者。
buddy system 背後用 2 的冪次管理記憶體區塊大小,若無
由於當初
大多數 overflow 在 > 0 階比較好實現,因為系統內較少要求相應大小的 object。但系統噪聲較低的情況下,0 階可獲得更多好用的結構,例如 cred
,只要覆蓋前 6 bytes,就能改變 process 權限。
cat /proc/slabinfo
可查看每個 cache 使用哪個 order,512 大小的用 1 page,也就是 order 0。
corCTF2022 - cache-of-castaways
初始化時創建一個 kmem_cache,表示與其他 kernel 結構隔離,大小為 512 bytes。
module 內只透過 ioctl 互動, 0xCAFEBABE 要一塊記憶體,0xF00DBABE 修改內容。
寫入使用者輸入到 buf+6 的位置,儘管寫入前有檢查長度,仍會造成 6 bytes 的 overflow。
攻擊流程如下:
- 分配大量 cred 結構,使其消耗較低 order 的 page
- 分配大量 page,確保從較高 order 分配,它們之間連續
- free 掉奇數 page,替換成 cred 結構
- free 掉偶數 page,替換成題目分配的結構,透過 overflow 嘗試修改先前的 cred
之所以分奇數和偶數 page,是為了提升 overflow 的成功率,並避免被放回較高的 order。
但實際 exploit 更加複雜,首先每個結構大小不足一個 page,需要分配更多結構,而官方解是利用 setsockopt
分配大量 page。
在 packet_setsockopt
原始碼,若 optname == PACKET_TX_RING
且 tp_version 版本是 V1 或 V2,就會呼叫到 packet_set_ring->alloc_pg_vec
,分配 block_nr
大小的記憶體。
1 | static struct pgv *alloc_pg_vec(struct tpacket_req *req, int order) |
所以說,只要這樣設定,就能分配一塊 page。
1 | struct tpacket_req req; |
然而,只有設定 SOCK_RAW
才能進入 packet_setsockopt
,而 SOCK_RAW
需要 CAP_NET_RAW
, 只有 root 才有此權限,因此 exploit 需設定 namespace,且在此 namespace 作為 root。
設定新的 user, network, mount namespace,改其 uid 和 gid,若缺少任一步驟,都會導致 error。
1 | void unshare_setup() { |
由於往後需檢查是否成功提權,而在此 namespace 是無法用 getuid 檢查的,所以必須開新的 process。
原本的用來觸發 shell,新的用來分配 page。兩個 process 可透過 IPC 或 pipe 溝通。
1 | if (!fork()) { |
最後是 clone 的問題,process 在 clone 時會相應創建一些結構,要到 buddy system 的記憶體,從而降低成功率,以下設定可最小化結構數量。
1 | clone(&wait_and_check, stack, CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND, NULL); |
wait_and_check
負責在一定時間檢查 uid,成功提權則開啟 shell。stack 只是傳進去防止 error,實際上 wait_and_check
不會用到 stack。
總 exploit 如下,目前還在 debug 中。
1 |
|
Cross cache attack
除了 heap overflow,也可跨 cache 利用 UAF 漏洞。
思路是在一個 cache 分配 UAF 的 object,使它放回 buddy system,再被分配到另一個 cache。
符合四項條件,就能使 cache 從 buddy system 要 page:
kmem_cache_cpu->freelist
追蹤 CPU 正在用的 page 中,下一個能用的 freed object。page->freelist
page 下一個能用的 freed object,若此 page 正被 CPU 用到,page->freelist
和kmem_cache_cpu->freelist
類似。kmem_cache_cpu->partial
追蹤 CPU 旗下擁有 freed object 的 page,這些 page 用page->lru
串起來。
正被用來 allocate 的 page 會記在kmem_cache_cpu->page
,不會在 partial 裡面。kmem_cache_node->partial
整個 node 沒有其他可分配的 page。
也就是說,當我們要足夠多 object,就能觸發 new_slab
,向 buddy system 要一塊 page。
而讓 page 放回 buddy system,需符合以下條件:
kmem_cache_cpu->partial
已滿- freeing object 屬於的 page 無任何正在使用的 object
也就是說,現在要把 page 放進 partial list,但 partial list 已經滿了,所以呼叫 free_slab
將當前 page 直接 free 掉。
之後再讓另一個 cache 分配,就能夠跨 cache 透過 UAF 影響資料。
Lag and Crash 4.0 - Cheminventory 即用到此項技術,由於最近比較忙,還沒辦法實際下去做 QAQ
參考資料
- https://www.willsroot.io/2022/08/reviving-exploits-against-cred-struct.html
- https://bsauce.github.io/2022/11/07/castaways/
- https://ctf-wiki.org/pwn/linux/kernel-mode/exploitation/heap/buddy/cross-cache/
- https://blog.xmcve.com/2023/10/12/Kernel-Heap---Cross-Cache-Overflow/
- https://kaligulaarmblessed.github.io/post/cheminventory