Pwn with File结构体(二)
字数 1017 2025-08-25 22:58:56

利用 _IO_FILE 结构体实现任意地址读写攻击技术详解

前言

在最新版 libc 中,由于对 vtable 进行了安全检查,传统的攻击方式受到限制。本文介绍一种通过修改 _IO_FILE 结构体实现任意地址读写的技术方法。

_IO_FILE 结构体基础

_IO_FILE 是 GNU C 库中用于文件 I/O 操作的核心数据结构,包含多个指针控制数据的读写。通过修改这些指针,可以在调用文件读写函数时实现任意地址读写。

任意地址读实现

原理

通过修改 _IO_write_base_IO_write_ptr 指针,控制写入数据的起始和结束地址,结合标准输出实现任意地址读取。

代码示例

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]) {
    FILE *fp;
    char *msg = "hello_file";
    char *buf = malloc(100);
    
    read(0, buf, 100);
    fp = fopen("key.txt", "rw");
    
    // 设置 flag 绕过检查
    fp->_flags &= ~8;
    fp->_flags |= 0x800;
    
    // 设置写入数据的起始和结束地址
    fp->_IO_write_base = msg;
    fp->_IO_write_ptr = msg + 6;
    
    // 绕过检查
    fp->_IO_read_end = fp->_IO_write_base;
    
    // 设置文件描述符为1(标准输出)
    fp->_fileno = 1;
    
    fwrite(buf, 1, 100, fp);
    return 0;
}

关键点

  1. 修改 _flags 绕过安全检查
  2. 设置 _IO_write_base 为想要读取的内存起始地址
  3. 设置 _IO_write_ptr 为结束地址
  4. 通过 fwrite 将指定内存内容输出到标准输出

任意地址写实现

原理

通过修改 _IO_buf_base_IO_buf_end 指针,控制读取数据的缓冲区位置,实现向任意地址写入数据。

代码示例

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]) {
    FILE *fp;
    char msg[100];
    char *buf = malloc(100);
    
    fp = fopen("key.txt", "rw");
    
    // 设置 flag 绕过检查
    fp->_flags &= ~4;
    
    // 设置缓冲区地址范围
    fp->_IO_buf_base = msg;
    fp->_IO_buf_end = msg + 100;
    
    // 设置文件描述符为0(标准输入)
    fp->_fileno = 0;
    
    fread(buf, 1, 6, fp);
    puts(msg);
    puts(buf);
    return 0;
}

关键点

  1. 修改 _flags 绕过安全检查
  2. 设置 _IO_buf_base 为目标写入地址
  3. 设置 _IO_buf_end 为结束地址
  4. 通过 fread 将输入数据写入指定内存区域

利用标准输入/输出实现攻击

stdinstdoutstderr 也是 _IO_FILE 结构体,通过修改这些默认文件流的结构体内容,可以更方便地实现任意地址读写。

修改 stdin 实现任意地址写

#include <stdio.h>
#include <stdlib.h>

int global_val = 0xaabbccdd;

int main(int argc, char * argv[]) {
    FILE *fp;
    int var;
    
    fp = stdin;
    fp->_flags &= ~4;
    
    // 将缓冲区设置为 stdout 结构体
    fp->_IO_buf_base = stdout;
    fp->_IO_buf_end = stdout + 100;
    
    scanf("%d",&var);
    printf("0x%x\n", global_val);
    return 0;
}

修改 stdout 实现任意地址读

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]) {
    FILE *fp;
    char *msg = "hello_stdout";
    char *buf = malloc(100);
    
    fp = stdout;
    
    // 设置 flag 绕过检查
    fp->_flags &= ~8;
    fp->_flags |= 0x800;
    
    // 设置写入地址范围
    fp->_IO_write_base = msg;
    fp->_IO_write_ptr = msg + 12;
    
    // 绕过检查
    fp->_IO_read_end = fp->_IO_write_base;
    
    // 设置文件描述符为1(标准输出)
    fp->_fileno = 1;
    
    puts("this is append on msg");
    return 0;
}

防御措施

  1. 指针完整性检查:验证 _IO_FILE 结构体中的指针是否指向合法内存区域
  2. 权限分离:限制对关键文件结构体的修改权限
  3. 地址随机化:通过 ASLR 增加攻击难度
  4. 结构体保护:对关键文件结构体进行写保护

参考资源

通过深入理解 _IO_FILE 结构体的工作机制和修改方法,安全研究人员可以更好地防御此类攻击,同时也可以在某些特殊场景下利用这些技术进行安全测试。

利用 _ IO_ FILE 结构体实现任意地址读写攻击技术详解 前言 在最新版 libc 中,由于对 vtable 进行了安全检查,传统的攻击方式受到限制。本文介绍一种通过修改 _IO_FILE 结构体实现任意地址读写的技术方法。 _ IO_ FILE 结构体基础 _IO_FILE 是 GNU C 库中用于文件 I/O 操作的核心数据结构,包含多个指针控制数据的读写。通过修改这些指针,可以在调用文件读写函数时实现任意地址读写。 任意地址读实现 原理 通过修改 _IO_write_base 和 _IO_write_ptr 指针,控制写入数据的起始和结束地址,结合标准输出实现任意地址读取。 代码示例 关键点 修改 _flags 绕过安全检查 设置 _IO_write_base 为想要读取的内存起始地址 设置 _IO_write_ptr 为结束地址 通过 fwrite 将指定内存内容输出到标准输出 任意地址写实现 原理 通过修改 _IO_buf_base 和 _IO_buf_end 指针,控制读取数据的缓冲区位置,实现向任意地址写入数据。 代码示例 关键点 修改 _flags 绕过安全检查 设置 _IO_buf_base 为目标写入地址 设置 _IO_buf_end 为结束地址 通过 fread 将输入数据写入指定内存区域 利用标准输入/输出实现攻击 stdin 、 stdout 和 stderr 也是 _IO_FILE 结构体,通过修改这些默认文件流的结构体内容,可以更方便地实现任意地址读写。 修改 stdin 实现任意地址写 修改 stdout 实现任意地址读 防御措施 指针完整性检查:验证 _IO_FILE 结构体中的指针是否指向合法内存区域 权限分离:限制对关键文件结构体的修改权限 地址随机化:通过 ASLR 增加攻击难度 结构体保护:对关键文件结构体进行写保护 参考资源 Play with FILE Structure - Yet Another Binary Exploit Technique GNU C 库源代码中关于 _IO_FILE 的实现 相关安全公告和漏洞报告 通过深入理解 _IO_FILE 结构体的工作机制和修改方法,安全研究人员可以更好地防御此类攻击,同时也可以在某些特殊场景下利用这些技术进行安全测试。