2025 强网杯S9 pwn - bph 复盘详解:一个任意地址写00到RCE的新技巧
字数 2107 2025-11-13 12:31:11

任意地址写00到RCE利用技术详解:强网杯S9 pwn-bph题目复盘

前言

在CTF PWN题型中,任意地址写零漏洞通常被认为利用难度较高。本文通过强网杯S9的bph题目,详细分析一种将任意地址写00漏洞转化为完整RCE的新技巧,该技巧在glibc-2.39环境下依然有效。

漏洞分析

程序基本情况

  • 环境:glibc 2.39(最新大版本)
  • 保护:全保护开启(FULL RELRO, PIE, NX, Stack Canary)
  • 沙箱:限制系统调用,仅允许openat、read、write
  • 功能:仅实现create和delete功能,其中delete功能存在逻辑缺陷无法正常使用

漏洞点分析

create函数逻辑

size = 读取用户输入大小
ptr = malloc(size)
read(0, ptr, size)  // 读取数据
*(ptr + size - 1) = 0  // 末尾写入00字节

关键问题:当size过大导致malloc失败时,ptr为0,但*(ptr + size - 1) = 0依然执行,等价于*(size - 1) = 0,形成任意地址写00漏洞。

利用条件

  1. 存在任意地址写00漏洞
  2. 程序使用fgets、fread等IO函数获取输入
  3. 能够泄露libc基地址

利用流程

第一阶段:泄露libc地址

利用残留数据泄露libc地址:

  • 通过精心构造size参数,利用程序中的残留数据泄露libc基地址

第二阶段:扩大stdin输入缓冲区

目标:修改stdin结构体的_IO_buf_base末位为00,扩大输入缓冲区范围

步骤

  1. 计算stdin结构体中_IO_buf_base地址(通常位于libc数据段)
  2. 构造size = _IO_buf_base地址 + 1,触发任意地址写00
  3. 写入00后,_IO_buf_base指向更早的内存地址,扩大输入缓冲区

内存变化

  • 修改前:缓冲区范围极小(通常只有1字节)
  • 修改后:缓冲区扩大到可覆盖_IO_buf_base_IO_buf_end字段

第三阶段:重定向输入缓冲区到stdout

目标:通过下一次输入修改stdin的缓冲区指针,使其指向stdout结构体

步骤

  1. 在下一次fgets调用时,输入精心构造的数据
  2. 修改_IO_buf_base_IO_buf_end,使其覆盖stdout结构体区域
  3. 此时后续输入将直接写入stdout结构体

第四阶段:伪造IO_FILE结构

利用原理:puts函数会调用_IO_sputn(vtable+0x38处的函数指针)

House of Emma技术应用

  • 通过调整vtable指针偏移,控制程序执行流
  • 结合House of Cat技术完成利用链构建

payload构造关键点

  1. 重叠结构设计:使+0xa0处指针指向IO_FILE结构本身
  2. 条件绕过
    • +0x8和+0x10值不同(自动满足)
    • +0x18 < +0x20(通过精心设置地址满足)
  3. 寄存器控制:rdx = [fp+0x20],为后续setcontext做准备

第五阶段:栈迁移与ROP构造

setcontext+61利用

mov rsp, [rdx+0xa0]
mov rbx, [rdx+0x80]
mov rbp, [rdx+0x78]
mov r12, [rdx+0x48]
mov r13, [rdx+0x50]
mov r14, [rdx+0x58]
mov r15, [rdx+0x60]
mov rdi, [rdx+0x70]
mov rsi, [rdx+0x68]

栈迁移策略

  1. 第一次setcontext:设置基本寄存器状态
  2. 跳转到read函数,重新写入ROP链
  3. 第二次setcontext:完成完整寄存器控制

第六阶段:绕过沙箱的ORW ROP

挑战:glibc-2.39中缺乏直接的pop rdx gadget

解决方案:使用mov dl, 0x??指令替代

  • 虽然只设置rdx的低8位,但对于ORW操作已足够
  • 第三个参数只要是非零值即可,具体数值不重要

ROP链构造

  1. openat:打开flag文件
  2. read:读取文件内容
  3. write:输出到标准输出

关键技术细节

IO_FILE结构关键偏移

  • _IO_buf_base: +0x38
  • _IO_buf_end: +0x40
  • vtable指针: +0xd8
  • 虚函数_IO_sputn: vtable+0x38

内存布局设计

struct fake_io_file {
    // 标准IO_FILE字段
    // ...
    uint64_t gadget_addr;      // +0x18
    uint64_t fp_addr;          // +0x20
    // ...
    uint64_t self_ptr;         // +0xa0 (指向自身)
    // setcontext需要的寄存器值
    // ...
};

利用流程时序

  1. 泄露libc地址
  2. 写00扩大stdin缓冲区
  3. 修改stdin缓冲区指向stdout
  4. 伪造stdout结构体
  5. 触发puts调用劫持控制流
  6. 栈迁移到可控区域
  7. 执行ORW ROP链

总结与创新点

新技术贡献

  1. 任意地址写00到RCE的完整链:首次系统性地将此类漏洞转化为完整利用链
  2. IO_FILE组合利用技术:结合House of Emma和House of Cat技术
  3. glibc-2.39环境下的利用:适应最新libc版本的防护机制

通用性分析

该技术在以下场景中可复用:

  • 存在任意地址写00漏洞
  • 程序使用标准IO函数
  • 能够泄露地址信息
  • 存在触发IO操作的代码路径

防御建议

  1. 对malloc大小进行严格限制
  2. 检查malloc返回值后再进行内存操作
  3. 使用更安全的字符串处理函数
  4. 加强沙箱限制

参考资料

  1. glibc-2.39源码
  2. House of Cat技术分析文档
  3. IO_FILE结构利用相关研究

通过本技术的详细分析,我们展示了现代PWN技术中如何将看似有限的漏洞转化为完整利用链的思维过程和技术细节,为CTF选手和安全研究人员提供了宝贵的技术参考。

任意地址写00到RCE利用技术详解:强网杯S9 pwn-bph题目复盘 前言 在CTF PWN题型中,任意地址写零漏洞通常被认为利用难度较高。本文通过强网杯S9的bph题目,详细分析一种将任意地址写00漏洞转化为完整RCE的新技巧,该技巧在glibc-2.39环境下依然有效。 漏洞分析 程序基本情况 环境 :glibc 2.39(最新大版本) 保护 :全保护开启(FULL RELRO, PIE, NX, Stack Canary) 沙箱 :限制系统调用,仅允许openat、read、write 功能 :仅实现create和delete功能,其中delete功能存在逻辑缺陷无法正常使用 漏洞点分析 create函数逻辑 : 关键问题 :当size过大导致malloc失败时,ptr为0,但 *(ptr + size - 1) = 0 依然执行,等价于 *(size - 1) = 0 ,形成任意地址写00漏洞。 利用条件 存在任意地址写00漏洞 程序使用fgets、fread等IO函数获取输入 能够泄露libc基地址 利用流程 第一阶段:泄露libc地址 利用残留数据泄露libc地址: 通过精心构造size参数,利用程序中的残留数据泄露libc基地址 第二阶段:扩大stdin输入缓冲区 目标 :修改stdin结构体的 _IO_buf_base 末位为00,扩大输入缓冲区范围 步骤 : 计算stdin结构体中 _IO_buf_base 地址(通常位于libc数据段) 构造size = _IO_buf_base地址 + 1 ,触发任意地址写00 写入00后, _IO_buf_base 指向更早的内存地址,扩大输入缓冲区 内存变化 : 修改前:缓冲区范围极小(通常只有1字节) 修改后:缓冲区扩大到可覆盖 _IO_buf_base 和 _IO_buf_end 字段 第三阶段:重定向输入缓冲区到stdout 目标 :通过下一次输入修改stdin的缓冲区指针,使其指向stdout结构体 步骤 : 在下一次fgets调用时,输入精心构造的数据 修改 _IO_buf_base 和 _IO_buf_end ,使其覆盖stdout结构体区域 此时后续输入将直接写入stdout结构体 第四阶段:伪造IO_ FILE结构 利用原理 :puts函数会调用 _IO_sputn (vtable+0x38处的函数指针) House of Emma技术应用 : 通过调整vtable指针偏移,控制程序执行流 结合House of Cat技术完成利用链构建 payload构造关键点 : 重叠结构设计 :使+0xa0处指针指向IO_ FILE结构本身 条件绕过 : +0x8和+0x10值不同(自动满足) +0x18 < +0x20(通过精心设置地址满足) 寄存器控制 :rdx = [ fp+0x20 ],为后续setcontext做准备 第五阶段:栈迁移与ROP构造 setcontext+61利用 : 栈迁移策略 : 第一次setcontext:设置基本寄存器状态 跳转到read函数,重新写入ROP链 第二次setcontext:完成完整寄存器控制 第六阶段:绕过沙箱的ORW ROP 挑战 :glibc-2.39中缺乏直接的pop rdx gadget 解决方案 :使用mov dl, 0x??指令替代 虽然只设置rdx的低8位,但对于ORW操作已足够 第三个参数只要是非零值即可,具体数值不重要 ROP链构造 : openat :打开flag文件 read :读取文件内容 write :输出到标准输出 关键技术细节 IO_ FILE结构关键偏移 _IO_buf_base : +0x38 _IO_buf_end : +0x40 vtable指针: +0xd8 虚函数 _IO_sputn : vtable+0x38 内存布局设计 利用流程时序 泄露libc地址 写00扩大stdin缓冲区 修改stdin缓冲区指向stdout 伪造stdout结构体 触发puts调用劫持控制流 栈迁移到可控区域 执行ORW ROP链 总结与创新点 新技术贡献 任意地址写00到RCE的完整链 :首次系统性地将此类漏洞转化为完整利用链 IO_ FILE组合利用技术 :结合House of Emma和House of Cat技术 glibc-2.39环境下的利用 :适应最新libc版本的防护机制 通用性分析 该技术在以下场景中可复用: 存在任意地址写00漏洞 程序使用标准IO函数 能够泄露地址信息 存在触发IO操作的代码路径 防御建议 对malloc大小进行严格限制 检查malloc返回值后再进行内存操作 使用更安全的字符串处理函数 加强沙箱限制 参考资料 glibc-2.39源码 House of Cat技术分析文档 IO_ FILE结构利用相关研究 通过本技术的详细分析,我们展示了现代PWN技术中如何将看似有限的漏洞转化为完整利用链的思维过程和技术细节,为CTF选手和安全研究人员提供了宝贵的技术参考。