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);  // 无条件减引用
}

利用方法

  1. 申请 0x2000 的内存
  2. 执行两次 munmap: munmap(addr, 0x1000)munmap(addr+0x1000, 0x1000)
  3. 这会触发两次 vm_close,导致引用计数多减 1
  4. 然后可以通过 delete 释放 ChunkManager,而 mmap 还保留着指针,造成 UAF

利用方法

非预期解法

限制:在开启 KASLR 的情况下难以利用,需要其他方法泄露 page 指针。

利用步骤

  1. add 一个只有 3 个 page 的 ChunkManager
  2. mmap 申请 0x3000 大小的内存
  3. munmap 释放前 0x2000 内存(触发两次 vm_close)
  4. delete 掉 ChunkManager
  5. 使用 USMA (User Space Mapping Attack) 占位
  6. mmap 映射 rx 缓冲区,构造 page 指针表
  7. 触发 vm_fault 分配任意 page

关键点

  • 利用 USMA 喷射 0x12*0x1000 的 rx 缓冲区
  • 这会得到一个 0xC0 大小的指针结构体,其中:
    • 0x88 偏移处指向可控缓冲区 page
    • 0x90 偏移处为 0(满足 spinlock 要求)

预期解法

利用 mremap 触发越界

  1. mmap(addr, MAX_PGSIZE...) 申请最大内存区域
  2. mremap(addr, MAX_PGSIZE, MAX_PGSIZE+0x1000) 申请更大区域
    • 由于未注册 vm_mremap,系统默认允许
  3. 访问 addr+MAX_PGSIZE 触发 vm_fault 进行越界访问

堆风水构造

  1. 使用 USMA Spray 喷射大量 page 并间隔释放
  2. 申请 kmem_cache 中所有 slub,迫使从伙伴系统重新获取 page
  3. 释放剩余间隔 page
  4. 分配大量 0x1000 大小的 pipe_buffer

难点

  • slub 重新分配有最小 order 要求(至少 0xa*0x1000 的释放区域)
  • 成功率约 10%

最终利用

  1. 构造 ChunkManager->pages[0x1000] 指向目标 page
  2. 使用 splice 绑定 poweroff 的 page 到 pipebuffer
  3. 实现只读文件写,完成提权

防御建议

  1. 修复 vm_fault 中的边界检查:

    if (pgoff >= cm->page_num)  // 应为 pgoff >= cm->page_num
    
  2. vm_close 中增加引用计数检查:

    if (atomic_read(&cm->refcount) > 0)
        atomic_dec(&cm->refcount);
    
  3. 注册 vm_mremap 操作以防止非法扩展映射区域

  4. 加强 kmem_cache 隔离,防止 Cross Cache Attack

总结

这道题目展示了 Linux 内核中 mmap/mremap 机制的复杂性以及潜在的安全问题。关键点包括:

  1. mmap 延迟绑定机制与缺页处理的交互
  2. 引用计数管理不当导致的 UAF
  3. mremap 在没有适当检查时的危险行为
  4. 内核堆风水与 Cross Cache Attack 技术

通过这道题目,我们可以深入理解 Linux 内核内存管理子系统的安全边界和攻击面。

D3CTF-d3kshrm 漏洞分析与利用教学 题目概述 d3kshrm 是 D3CTF 中的一道内核题目,涉及 Linux 内核模块中的内存管理漏洞。题目主要考察对内核内存管理机制的理解,特别是 mmap、mremap 等系统调用的实现细节,以及如何利用这些机制中的漏洞实现权限提升。 关键数据结构 ChunkManager 结构 逆向分析得到的关键数据结构: 总大小为 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 处理函数中存在边界检查问题: 漏洞点 :当 pgoff == cm->page_num 时也会进行分配,导致越界访问 pages 数组。 2. vm_ close 非预期漏洞 vm_close 函数会在每次调用时对 ChunkManager 进行减引用: 利用方法 : 申请 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 中的边界检查: 在 vm_close 中增加引用计数检查: 注册 vm_mremap 操作以防止非法扩展映射区域 加强 kmem_ cache 隔离,防止 Cross Cache Attack 总结 这道题目展示了 Linux 内核中 mmap/mremap 机制的复杂性以及潜在的安全问题。关键点包括: mmap 延迟绑定机制与缺页处理的交互 引用计数管理不当导致的 UAF mremap 在没有适当检查时的危险行为 内核堆风水与 Cross Cache Attack 技术 通过这道题目,我们可以深入理解 Linux 内核内存管理子系统的安全边界和攻击面。