kernel从小白到大神(三)
字数 1738 2025-08-23 18:31:18
Linux Kernel Exploitation 从入门到精通(三):堆利用与结构体攻击
一、内核内存分配基础
-
核心分配函数
void *kmalloc(size_t size, gfp_t gfp);- 关键参数:
size:分配大小,决定落入的SLUB缓存(如kmalloc-64)。gfp:内存分配标志,控制分配行为:GFP_KERNEL:允许阻塞,可触发内存回收(最常见)。GFP_KERNEL_ACCOUNT:计入内存控制组账单(隔离kmalloc-cg-xx缓存)。GFP_HARDWALL:限制为当前NUMA节点分配。GFP_NOWARN:禁止输出分配失败警告。
- 关键参数:
-
SLUB分配器机制
- 三级缓存结构:
kmem_cache_cpu(每CPU快速通道)→kmem_cache_node(Partial链表)→ Buddy System(底层页分配)。 - 分配流程:
- 优先从当前CPU的
kmem_cache_cpu->freelist获取对象。 - 若为空,从
kmem_cache_node->partial链表补充Slab页。 - 仍不足则向Buddy System申请新页。
- 优先从当前CPU的
- 释放流程:对象按优先级插入CPU本地链表、Partial链表或释放回Buddy。
- 三级缓存结构:
-
多核绑定优化
通过sched_setaffinity绑定进程到单一CPU核心,避免跨核竞争:void bind_cpu(int core) { cpu_set_t cpu_set; CPU_ZERO(&cpu_set); CPU_SET(core, &cpu_set); sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set); }
二、关键结构体利用:tty终端
-
tty结构体概述
- 分配路径:
open("/dev/ptmx")→alloc_tty_struct()(大小固定为0x2e0,位于kmalloc-1k或kmalloc-cg-1k)。 - 关键字段:
struct tty_struct { int magic; // 0x5401(标识符) const struct tty_operations *ops; // 函数指针表(攻击目标) // ...其他字段省略... };
- 分配路径:
-
tty_operations劫持
- 函数表结构:
struct tty_operations { int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg); int (*write)(struct tty_struct *tty, const unsigned char *buf, int count); // ...其他函数指针... }; - 利用步骤:
- UAF漏洞:释放后未清零的tty对象被重新分配。
- 伪造ops表:覆盖
tty->ops指向用户可控内存,填充恶意函数指针(如ioctl)。 - 触发调用:通过
ioctl(tty_fd, cmd, arg)劫持控制流。
- 函数表结构:
-
调试技巧
- 使用
pahole工具分析结构体布局:pahole -C tty_struct /path/to/vmlinux - 若劫持失败,可通过内核报错日志定位调用的函数指针偏移。
- 使用
三、高级利用技术:userfaultfd
-
机制原理
- 流程:
- 注册缺页处理:用户空间监控指定内存区域的页面故障。
- 触发缺页:如
copy_from_user访问未映射的地址。 - 异步处理:内核暂停当前线程,转由用户态处理缺页事件。
- 流程:
-
利用场景
- 条件竞争:在缺页处理期间操作内核对象(如释放堆块并重新占用)。
- 典型流程:
// 1. 注册userfaultfd监控匿名映射区 Register_Userfalutfd(anon_mem, size, handler); // 2. 触发缺页(如copy_from_user写入anon_mem) // 3. 在handler中释放原堆块,并分配目标结构体(如tty) // 4. 恢复缺页处理,覆写目标结构体
-
代码模板
void Userfault_Handler(int uffd) { struct uffdio_copy ufcopy; read(uffd, &msg, sizeof(msg)); // 接收缺页事件 sem_post(&sem[0]); // 同步信号量 // 在缺页期间执行堆操作 ufcopy.src = fake_data; // 伪造数据 ufcopy.dst = fault_addr; // 缺页地址 ioctl(uffd, UFFDIO_COPY, &ufcopy); // 恢复执行并覆写 }
四、实战案例解析
-
CISCN2017-babydriver
- 漏洞:全局变量UAF,无锁的读写操作。
- 利用链:
- 通过双fd触发UAF,控制释放的
kmalloc-1k对象。 - 分配tty结构体占用空洞,伪造
ops->ioctl。 - 执行ROP链提权(
commit_creds(prepare_kernel_cred(0)))。
- 通过双fd触发UAF,控制释放的
-
SCTF2024-kno_puts_revenge
- 关键点:
- 通过CVE泄露内核基址。
- 结合userfaultfd实现精确堆占位。
- 利用
push rdx; pop rsp等gadget构造栈迁移。
- 关键点:
-
2024-akernel
- 地址泄露:通过
cpu_entry_area固定地址绕过KASLR。 - 多阶段利用:
- 泄露驱动基址 → 获取堆地址。
- userfaultfd暂停写入,置换为tty对象。
- 劫持控制流至ROP。
- 地址泄露:通过
五、防御与绕过
- 常见防护:
CONFIG_MEMCG_KMEM:隔离GFP_KERNEL与GFP_KERNEL_ACCOUNT缓存。CONFIG_SLAB_FREELIST_HARDENED:保护SLUB freelist。
- 绕过思路:
- 利用跨缓存类型混淆(如通过
kmalloc-cg污染kmalloc)。 - 结合其他漏洞(如地址泄露)绕过KASLR。
- 利用跨缓存类型混淆(如通过
附录:
- 工具推荐:
crash分析内核转储,slabinfo查看SLUB状态。 - 延伸阅读:Linux内核源码
mm/slub.c、drivers/tty/tty_io.c。
(完)