D3CTF2022 - Pwn - d3kheap 题解
字数 1798 2025-08-27 12:33:31
D3CTF2022 - d3kheap 内核堆漏洞利用详解
0x00 题目概述
这是一道来自D3CTF2022的内核Pwn题目,考察内核堆漏洞利用技术。题目提供了一个内核模块,其中存在一个引用计数错误导致的UAF(Use-After-Free)漏洞。
0x01 漏洞分析
模块功能分析
内核模块提供了以下ioctl功能:
- 0x1234:分配buf
- 0xdead:释放buf
关键问题:
- 分配时仅检查buf是否为NULL,不检查ref_count
- 释放时仅减少ref_count,不将buf置NULL
- ref_count被错误初始化为1
漏洞本质
这是一个典型的引用计数错误导致的UAF漏洞:
- 可以多次释放同一个buf
- 由于slub_free有double free检查,不能直接double free
- 需要转化为UAF进行利用
0x02 漏洞利用步骤
Pre. 构造UAF
利用链:
- 分配一个1024大小的object
- 释放该object
- 将其分配到别的结构体(victim)上
- 再次释放该object
此时victim结构体同时存在于:
- 正在使用的状态
- slub的free list中
Step.I 堆喷msg_msg
选择msg_msg结构体作为victim:
struct msg_msg {
struct list_head m_list;
long m_type;
size_t m_ts; /* message text size */
struct msg_msgseg *next;
void *security;
/* the actual message follows immediately */
};
堆喷策略:
- 创建多个消息队列
- 每个队列发送两条消息:
- 主消息:大小96
- 辅助消息:大小0x400
内存布局:
- 辅助消息有高概率获取到之前释放的object
- 使用MSG_COPY标志位可以读取消息而不释放
Step.II 构造UAF并定位victim队列
- 释放辅助消息,完成UAF构造
- 使用sk_buff堆喷劫持结构体:
- sk_buff代表内核网络协议栈中的"包"
- 使用socketpair创建socket对
- 通过read/write完成收发包
定位方法:
- 向UAF object写入特定数据
- 使用MSG_COPY读取,通过失败判断命中UAF的队列
Step.III 伪造msg_msg泄露地址
-
伪造msg_msg结构体,设置大m_ts实现越界读
-
泄露相邻辅助消息的header,获取堆地址
-
通过消息队列结构泄露:
- 辅助消息的prev指向主消息
- 辅助消息的next指向msg_queue结构
-
伪造msg_msg->next,指向主消息头部前
-
读取主消息头部,泄露辅助消息地址
-
计算得到UAF对象地址:辅助消息地址 - 0x400
Step.IV 堆喷pipe_buffer泄露内核基址
选择pipe_buffer结构体:
struct pipe_buffer {
struct page *page;
unsigned int offset, len;
const struct pipe_buf_operations *ops;
unsigned int flags;
unsigned long private;
};
利用步骤:
- 修复辅助消息header
- 接收辅助消息,object重回slub
- 堆喷pipe_buffer
- 接收sk_buff数据包,读取pipe_buffer数据
- 从pipe_buf_operations泄露anon_pipe_buf_ops地址
- 计算内核基址
Step.V 伪造pipe_buffer劫持RIP
- 关闭管道两端触发pipe_buffer->ops->release
- 使用sk_buff在UAF object上伪造:
- 函数表
- ROP chain
- 选择合适gadget完成栈迁移
- 劫持RIP提权
ROP chain构造:
- 调用prepare_kernel_cred(0)
- 调用commit_creds
- 返回用户态
- 执行getRootShell
0x03 关键利用技术
- msg_msg堆喷:利用消息队列实现可控大小的堆分配
- sk_buff堆喷:通过socket通信实现内核堆分配
- pipe_buffer利用:泄露内核地址和劫持控制流
- UAF转化:将double free转化为更稳定的UAF
- 地址泄露:通过结构体布局实现任意地址读
- ROP构造:在内核态完成权限提升
0x04 防御措施
- 修复引用计数错误
- 释放后及时置NULL指针
- 使用SLAB/SLUB的安全机制
- 开启KASLR等防护措施
- 限制用户空间与内核的交互
0x05 总结
这道题目展示了内核堆漏洞利用的完整链条:
- 从引用计数错误出发
- 构造稳定的UAF
- 通过堆喷控制内存布局
- 利用内核结构体泄露地址
- 最终劫持控制流提权
利用过程中结合了多种内核结构体(msg_msg, sk_buff, pipe_buffer)的特性,是一道综合性很强的内核Pwn题目。