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

关键问题:

  1. 分配时仅检查buf是否为NULL,不检查ref_count
  2. 释放时仅减少ref_count,不将buf置NULL
  3. ref_count被错误初始化为1

漏洞本质

这是一个典型的引用计数错误导致的UAF漏洞:

  • 可以多次释放同一个buf
  • 由于slub_free有double free检查,不能直接double free
  • 需要转化为UAF进行利用

0x02 漏洞利用步骤

Pre. 构造UAF

利用链:

  1. 分配一个1024大小的object
  2. 释放该object
  3. 将其分配到别的结构体(victim)上
  4. 再次释放该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 */
};

堆喷策略:

  1. 创建多个消息队列
  2. 每个队列发送两条消息:
    • 主消息:大小96
    • 辅助消息:大小0x400

内存布局:

  • 辅助消息有高概率获取到之前释放的object
  • 使用MSG_COPY标志位可以读取消息而不释放

Step.II 构造UAF并定位victim队列

  1. 释放辅助消息,完成UAF构造
  2. 使用sk_buff堆喷劫持结构体:
    • sk_buff代表内核网络协议栈中的"包"
    • 使用socketpair创建socket对
    • 通过read/write完成收发包

定位方法:

  • 向UAF object写入特定数据
  • 使用MSG_COPY读取,通过失败判断命中UAF的队列

Step.III 伪造msg_msg泄露地址

  1. 伪造msg_msg结构体,设置大m_ts实现越界读

  2. 泄露相邻辅助消息的header,获取堆地址

  3. 通过消息队列结构泄露:

    • 辅助消息的prev指向主消息
    • 辅助消息的next指向msg_queue结构
  4. 伪造msg_msg->next,指向主消息头部前

  5. 读取主消息头部,泄露辅助消息地址

  6. 计算得到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;
};

利用步骤:

  1. 修复辅助消息header
  2. 接收辅助消息,object重回slub
  3. 堆喷pipe_buffer
  4. 接收sk_buff数据包,读取pipe_buffer数据
  5. 从pipe_buf_operations泄露anon_pipe_buf_ops地址
  6. 计算内核基址

Step.V 伪造pipe_buffer劫持RIP

  1. 关闭管道两端触发pipe_buffer->ops->release
  2. 使用sk_buff在UAF object上伪造:
    • 函数表
    • ROP chain
  3. 选择合适gadget完成栈迁移
  4. 劫持RIP提权

ROP chain构造:

  1. 调用prepare_kernel_cred(0)
  2. 调用commit_creds
  3. 返回用户态
  4. 执行getRootShell

0x03 关键利用技术

  1. msg_msg堆喷:利用消息队列实现可控大小的堆分配
  2. sk_buff堆喷:通过socket通信实现内核堆分配
  3. pipe_buffer利用:泄露内核地址和劫持控制流
  4. UAF转化:将double free转化为更稳定的UAF
  5. 地址泄露:通过结构体布局实现任意地址读
  6. ROP构造:在内核态完成权限提升

0x04 防御措施

  1. 修复引用计数错误
  2. 释放后及时置NULL指针
  3. 使用SLAB/SLUB的安全机制
  4. 开启KASLR等防护措施
  5. 限制用户空间与内核的交互

0x05 总结

这道题目展示了内核堆漏洞利用的完整链条:

  1. 从引用计数错误出发
  2. 构造稳定的UAF
  3. 通过堆喷控制内存布局
  4. 利用内核结构体泄露地址
  5. 最终劫持控制流提权

利用过程中结合了多种内核结构体(msg_msg, sk_buff, pipe_buffer)的特性,是一道综合性很强的内核Pwn题目。

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: 堆喷策略: 创建多个消息队列 每个队列发送两条消息: 主消息:大小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结构体: 利用步骤: 修复辅助消息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题目。