D3CTF-d3kshrm(预期&非预期)题解
字数 2131 2025-08-29 22:41:24
D3CTF-d3kshrm 漏洞分析与利用教学
题目概述
d3kshrm 是 D3CTF 中的一道内核题目,涉及 Linux 内核模块中的内存管理漏洞。题目主要考察对内核内存管理机制的理解,特别是 mmap、mremap 等系统调用的实现细节,以及如何利用这些机制中的漏洞实现权限提升。
关键数据结构
ChunkManager 结构
逆向分析得到的关键数据结构:
struct ChunkManager {
spinlock_t lock; // 位于偏移 0x90
atomic_t refcount; // 引用计数
struct page **pages; // 位于偏移 0x88,指向 page 指针数组
unsigned int page_num; // page 数量
// 其他成员...
};
- 总大小为 0xA0(实际分配 0xC0 大小)
pages成员从自定义的 kmem_cache 中分配 0x1000 大小的堆块- 可以基于传入参数申请 1-0x200 个 page
功能分析
1. add 功能
- 为 ChunkManager 结构体申请 0xC0 大小的堆块
- 为
pages成员从自定义 kmem_cache 中获取 0x1000 的堆块 - 基于传入参数申请 n 个 page (1-0x200)
- 将 ChunkManager 存入
d3kshrm_arr数组
2. bind/unbind 功能
- bind: 将指定
d3kshrm_arr[idx]绑定到当前 file->private_data- 增加引用计数
- unbind: 与 bind 相反,减少引用计数并删除 file->private_data
3. delete 功能
- 当 ChunkManager 不被引用时,可以释放相关资源
- 整个过程有 owner 检查和引用计数操作,并上锁
漏洞分析
1. mmap 相关漏洞
mmap 会检查映射的区间大小是否小于等于 page_num,但在 vm_fault 处理函数中存在边界检查问题:
static vm_fault_t d3kshrm_fault(struct vm_fault *vmf) {
unsigned long pgoff = vmf->pgoff; // 访问的 page 偏移
struct ChunkManager *cm = vmf->vma->vm_private_data;
if (pgoff >= cm->page_num) // 错误检查,应为 pgoff > cm->page_num
return VM_FAULT_SIGBUS;
// 分配 page...
}
漏洞点:当 pgoff == cm->page_num 时也会进行分配,导致越界访问 pages 数组。
2. vm_close 非预期漏洞
vm_close 函数会在每次调用时对 ChunkManager 进行减引用:
static void d3kshrm_close(struct vm_area_struct *vma) {
struct ChunkManager *cm = vma->vm_private_data;
atomic_dec(&cm->refcount); // 无条件减引用
}
利用方法:
- 申请 0x2000 的内存
- 执行两次 munmap:
munmap(addr, 0x1000)和munmap(addr+0x1000, 0x1000) - 这会触发两次
vm_close,导致引用计数多减 1 - 然后可以通过 delete 释放 ChunkManager,而 mmap 还保留着指针,造成 UAF
利用方法
非预期解法
限制:在开启 KASLR 的情况下难以利用,需要其他方法泄露 page 指针。
利用步骤:
- add 一个只有 3 个 page 的 ChunkManager
- mmap 申请 0x3000 大小的内存
- munmap 释放前 0x2000 内存(触发两次 vm_close)
- delete 掉 ChunkManager
- 使用 USMA (User Space Mapping Attack) 占位
- mmap 映射 rx 缓冲区,构造 page 指针表
- 触发 vm_fault 分配任意 page
关键点:
- 利用 USMA 喷射 0x12*0x1000 的 rx 缓冲区
- 这会得到一个 0xC0 大小的指针结构体,其中:
- 0x88 偏移处指向可控缓冲区 page
- 0x90 偏移处为 0(满足 spinlock 要求)
预期解法
利用 mremap 触发越界:
mmap(addr, MAX_PGSIZE...)申请最大内存区域mremap(addr, MAX_PGSIZE, MAX_PGSIZE+0x1000)申请更大区域- 由于未注册
vm_mremap,系统默认允许
- 由于未注册
- 访问
addr+MAX_PGSIZE触发vm_fault进行越界访问
堆风水构造:
- 使用 USMA Spray 喷射大量 page 并间隔释放
- 申请 kmem_cache 中所有 slub,迫使从伙伴系统重新获取 page
- 释放剩余间隔 page
- 分配大量 0x1000 大小的 pipe_buffer
难点:
- slub 重新分配有最小 order 要求(至少 0xa*0x1000 的释放区域)
- 成功率约 10%
最终利用:
- 构造
ChunkManager->pages[0x1000]指向目标 page - 使用 splice 绑定 poweroff 的 page 到 pipebuffer
- 实现只读文件写,完成提权
防御建议
-
修复
vm_fault中的边界检查:if (pgoff >= cm->page_num) // 应为 pgoff >= cm->page_num -
在
vm_close中增加引用计数检查:if (atomic_read(&cm->refcount) > 0) atomic_dec(&cm->refcount); -
注册
vm_mremap操作以防止非法扩展映射区域 -
加强 kmem_cache 隔离,防止 Cross Cache Attack
总结
这道题目展示了 Linux 内核中 mmap/mremap 机制的复杂性以及潜在的安全问题。关键点包括:
- mmap 延迟绑定机制与缺页处理的交互
- 引用计数管理不当导致的 UAF
- mremap 在没有适当检查时的危险行为
- 内核堆风水与 Cross Cache Attack 技术
通过这道题目,我们可以深入理解 Linux 内核内存管理子系统的安全边界和攻击面。