IO FILE 之任意读写
字数 1975 2025-08-03 16:44:31

IO FILE 之任意读写技术详解

概述

本文详细分析如何利用IO FILE结构体中的缓冲区指针进行任意内存读写操作。主要内容包括:

  1. 使用stdin标准输入缓冲区进行任意地址写
  2. 使用stdout标准输出缓冲区进行任意地址读写

前置知识

在阅读本文前,建议先了解以下内容:

  • IO FILE系列基础函数(fopen/fread/fwrite/fclose)的实现原理
  • IO FILE的vtable劫持及FSOP技术
  • 基本的堆利用知识

stdin标准输入缓冲区任意地址写

原理分析

通过控制输入缓冲区指针,使得输入缓冲区指向目标地址,在调用系统调用读取数据时实现任意地址写。

关键函数调用链:
fread -> _IO_file_xsgetn -> __underflow -> _IO_SYSREAD

必要条件

  1. 设置_IO_read_end等于_IO_read_ptr
  2. 设置_flag &~ _IO_NO_READS(即_flag &~ 0x4
  3. 设置_fileno为0(标准输入)
  4. 设置_IO_buf_base为write_start,_IO_buf_end为write_end
  5. 确保_IO_buf_end - _IO_buf_base大于fread要读的数据

实践案例:whctf2017 stackoverflow

  1. 漏洞利用

    • 通过未初始化的name变量泄露libc地址
    • 存在溢出写'\x00'的漏洞
  2. 利用步骤

    • 利用溢出漏洞修改stdin结构体中的_IO_buf_base指针
    • 通过输入操作覆盖_IO_buf_end__malloc_hook+0x8
    • 控制输入数据覆盖__malloc_hook
  3. 关键点

    • IO_getc函数用于刷新_IO_read_ptr
    • 使用ROP绕过one gadget限制

stdout标准输出缓冲区任意地址读写

任意写实现

原理

通过控制输出缓冲区指针,将可控数据拷贝至目标地址。

关键函数调用链:
fwrite -> _IO_new_file_xsputn -> memcpy

必要条件

  • 设置_IO_write_ptr指向write_start
  • 设置_IO_write_end指向write_end

任意读实现

原理

控制输出缓冲区指针指向目标地址,构造条件使缓冲区"满",从而输出目标地址数据。

关键函数调用链:
fwrite -> _IO_new_file_xsputn -> _IO_OVERFLOW -> _IO_do_write -> _IO_SYSWRITE

必要条件

  1. 设置_flag &~ _IO_NO_WRITES(即_flag &~ 0x8
  2. 设置_flag & _IO_CURRENTLY_PUTTING(即_flag | 0x800
  3. 设置_fileno为1(标准输出)
  4. 设置_IO_write_base指向read_start,_IO_write_ptr指向read_end
  5. 设置_IO_read_end等于_IO_write_base或设置_flag & _IO_IS_APPENDING(即_flag | 0x1000
  6. (可选)设置_IO_write_end等于_IO_write_ptr

实践案例:hctf2018 babyprintf_ver2

  1. 漏洞利用

    • 存在溢出可以覆盖stdout结构体
    • 无法覆盖vtable(有修正机制)
  2. 利用步骤

    • 构造stdout结构体实现任意读泄露libc地址
    • 构造stdout结构体实现任意写修改__malloc_hook
    • 通过%n触发malloc调用one gadget
  3. 关键点

    • 需要设置_flag | 0x8000_IO_USER_LOCK)避免死锁
    • %n可以触发malloc(通过__readonly_area调用fopen

构造技巧与注意事项

  1. flags字段构造

    • 最容易出错的部分
    • 建议直接拷贝原始stdout/stdin结构体的flags进行修改
  2. 调试工具

    • 使用pwn_debug的IO_FILE_plus模块
    • 使用arbitrary_write_checkarbitrary_read_check进行验证
  3. 常见问题

    • 忘记设置_fileno
    • flags设置不正确导致流程分支错误
    • 缓冲区大小计算错误

总结

IO FILE的任意读写技术是一种强大的利用方式,关键在于:

  1. 精确控制缓冲区指针
  2. 正确设置flags标志位
  3. 理解IO操作的内在流程

通过深入分析源码可以更好地掌握这些技术,建议读者结合具体案例进行实践练习。

IO FILE 之任意读写技术详解 概述 本文详细分析如何利用IO FILE结构体中的缓冲区指针进行任意内存读写操作。主要内容包括: 使用stdin标准输入缓冲区进行任意地址写 使用stdout标准输出缓冲区进行任意地址读写 前置知识 在阅读本文前,建议先了解以下内容: IO FILE系列基础函数(fopen/fread/fwrite/fclose)的实现原理 IO FILE的vtable劫持及FSOP技术 基本的堆利用知识 stdin标准输入缓冲区任意地址写 原理分析 通过控制输入缓冲区指针,使得输入缓冲区指向目标地址,在调用系统调用读取数据时实现任意地址写。 关键函数调用链: fread -> _IO_file_xsgetn -> __underflow -> _IO_SYSREAD 必要条件 设置 _IO_read_end 等于 _IO_read_ptr 设置 _flag &~ _IO_NO_READS (即 _flag &~ 0x4 ) 设置 _fileno 为0(标准输入) 设置 _IO_buf_base 为write_ start, _IO_buf_end 为write_ end 确保 _IO_buf_end - _IO_buf_base 大于fread要读的数据 实践案例:whctf2017 stackoverflow 漏洞利用 : 通过未初始化的name变量泄露libc地址 存在溢出写'\x00'的漏洞 利用步骤 : 利用溢出漏洞修改stdin结构体中的 _IO_buf_base 指针 通过输入操作覆盖 _IO_buf_end 为 __malloc_hook+0x8 控制输入数据覆盖 __malloc_hook 关键点 : IO_getc 函数用于刷新 _IO_read_ptr 使用ROP绕过one gadget限制 stdout标准输出缓冲区任意地址读写 任意写实现 原理 通过控制输出缓冲区指针,将可控数据拷贝至目标地址。 关键函数调用链: fwrite -> _IO_new_file_xsputn -> memcpy 必要条件 设置 _IO_write_ptr 指向write_ start 设置 _IO_write_end 指向write_ end 任意读实现 原理 控制输出缓冲区指针指向目标地址,构造条件使缓冲区"满",从而输出目标地址数据。 关键函数调用链: fwrite -> _IO_new_file_xsputn -> _IO_OVERFLOW -> _IO_do_write -> _IO_SYSWRITE 必要条件 设置 _flag &~ _IO_NO_WRITES (即 _flag &~ 0x8 ) 设置 _flag & _IO_CURRENTLY_PUTTING (即 _flag | 0x800 ) 设置 _fileno 为1(标准输出) 设置 _IO_write_base 指向read_ start, _IO_write_ptr 指向read_ end 设置 _IO_read_end 等于 _IO_write_base 或设置 _flag & _IO_IS_APPENDING (即 _flag | 0x1000 ) (可选)设置 _IO_write_end 等于 _IO_write_ptr 实践案例:hctf2018 babyprintf_ ver2 漏洞利用 : 存在溢出可以覆盖stdout结构体 无法覆盖vtable(有修正机制) 利用步骤 : 构造stdout结构体实现任意读泄露libc地址 构造stdout结构体实现任意写修改 __malloc_hook 通过 %n 触发malloc调用one gadget 关键点 : 需要设置 _flag | 0x8000 ( _IO_USER_LOCK )避免死锁 %n 可以触发malloc(通过 __readonly_area 调用 fopen ) 构造技巧与注意事项 flags字段构造 : 最容易出错的部分 建议直接拷贝原始stdout/stdin结构体的flags进行修改 调试工具 : 使用pwn_ debug的 IO_FILE_plus 模块 使用 arbitrary_write_check 和 arbitrary_read_check 进行验证 常见问题 : 忘记设置 _fileno flags设置不正确导致流程分支错误 缓冲区大小计算错误 总结 IO FILE的任意读写技术是一种强大的利用方式,关键在于: 精确控制缓冲区指针 正确设置flags标志位 理解IO操作的内在流程 通过深入分析源码可以更好地掌握这些技术,建议读者结合具体案例进行实践练习。