Linux 内核漏洞利用 Cheat Sheet

从利用角度看内核结构体(更新中…)

以常见的UAF为例,内核漏洞利用过程中,涉及到的组件比较多。而每个人的理解都不尽相同,导致描述时常常存在歧义或者交流起来比较费劲。所以以我对内核pwn的理解(master wang指导🐶),画了一张图,方便看到这篇文章的人明白我的表述。

image-20230215181840479

这张图从上往下分为用户态和内核态,从左到右分为漏洞侧和利用侧,后两个概念的定义如下:

  • 漏洞侧漏洞ko + UAF堆块。漏洞多出现在 漏洞ko 对已free的内核堆块进行读/写/释放。
  • 利用侧exp + UAF堆块。利用多是让exp通过syscall占用漏洞ko free的堆块(不同结构体),达到控制流劫持/信息泄露的目的。

可见,对内核UAF漏洞的利用是漏洞ko(漏洞侧)和exp(利用侧)之间基于同一个堆块的复杂交互过程。

控制流劫持

漏洞ko 仅有 “UAF写” 功能时,让 exp 通过某个系统调用在内核创建一个某种大小的结构体,并占用住UAF堆块。接下来,通过 “UAF写” 覆盖结构体中的函数指针,就可以达到控制流劫持的目的。

下表列出了当前已知的可以控制流劫持的结构体:

结构体 大小 ko漏洞点 利用效果 写偏移
tty_struct 0x2e0 (kmalloc-1024) UAF写 控制流劫持 0x18指向函数指针列表
seq_operations 0x20 (kmalloc-32) UAF写 控制流劫持 0x0处函数指针
subprocess_info 0x60 (kmalloc-128) UAF写 控制流劫持 0x50处函数指针(race)
pipe_buffer 0x280 (kmalloc-1024) UAF写 控制流劫持 0x10指向函数指针列表
tty_ldisc_ops 0xa8(kmalloc-192) UAF写 控制流劫持 0x38处函数指针,ref

信息泄露

信息泄露有两种情况:

  • 一种是利用 “UAF写” 改变 exp 对应内核结构体的内容(如改变内容指针或内容大小)。从而在 exp 再次读取其内容时达到任意地址读/越界读的效果。
  • 另一种是利用”UAF读” 直接泄露 exp 对应内核结构体中的内容(如堆地址/内核地址)。

UAF写

可用于 “UAF写” 利用的结构体如下:

结构体 大小 ko漏洞点 利用效果 写偏移
ldt_struct 0x10(kmalloc-16) UAF写 越界读/任意地址读 0x0:指向数据区,0x8:大小
msg_msg 非固定 UAF写 越界读/任意地址读 0x30:第一段数据区,0x20:指向下一段数据区;0x18:总大小
user_key_payload 非固定 UAF写 越界读 0x18:开始是数据区,0x10:大小(2byte)

UAF读

可用于 “UAF读” 利用的结构体如下:

结构体 大小 ko漏洞点 利用效果 读偏移
tty_struct 0x2e0 (kmalloc-1024) UAF读 泄露堆/内核地址 堆:0x8/0x10(multiple);内核:0x18/0x2d0
seq_operations 0x20 (kmalloc-32) UAF读 泄露内核地址 内核:0x0/0x8/0x10/0x18
subprocess_info 0x60 (kmalloc-128) UAF读 泄露堆/内核地址 堆:0x8/0x30;内核:0x18/0x48/0x50/
pipe_buffer 0x280 (kmalloc-1024) UAF读 泄露内核地址 内核:0x10
shm_file_data 0x20 (kmalloc-32) UAF读 泄露堆/内核地址 堆:0x10;内核:0x8/0x18
msg_msg 0x31~0x1000 (>= kmalloc-64) UAF读 泄露堆 堆:0x20/0x28
timerfd_ctx 0xf0 (kmalloc-256) UAF读 泄露堆/内核地址 堆:0x30;内核:0x28

特殊结构体

记录一些暂时无法归类的特殊结构体,

(1)cred结构体:只能用于老版本,新版本为cred申请堆的方式变了。暂未确定从哪个版本开始不能用该方法。

结构体 大小 ko漏洞点 利用效果 偏移
cred 0xa8 (kmalloc-192) UAF写 root提权 0x4-0x20全写成0