LOADING

加载过慢请开启缓存 浏览器默认开启

iofile 学习笔记(施工中)

2025/7/1 heap pwn

iofile 之 fopen

自己先写一个程序玩玩:

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

int main() {

    FILE* fp = fopen("test", "wb");
    fprintf(stderr, "fp is at: %p\n", fp);
    char* ptr = malloc(0x20);
    return 0;
}

然后:

gcc -g IO_FILE_fopen.c -o IO_FILE_fopen
gdb IO_FILE_fopen

在 main 下断点并跟踪到 fprintf(stderr, “fp is at: %p\n”, fp); 执行完毕,执行 p *fp 可以看见:

_flags 是文件标志,暂时不知道有什么用。

执行 heap,可以看到分配了一个大小为 0x1e0 的堆:

p fp, 也可以看见它指向了:

看来是和堆很像了,执行malloc(0x20)后:

注意到read\write和buf的系列的值都为0x0,我们给源代码添加相应函数。

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

int main() {

    FILE* fp = fopen("test", "rb");
    fprintf(stderr, "fp is at: %p\n", fp);
    char* ptr = malloc(0x20);
    fread(ptr, 1, 20, fp);
    return 0;
}

执行完 fread 后,可以看见又分配了一个较大的堆:

应该是 buf 的:

但一般很少会有要求读取 file 的 pwn 题,io_file 更多是与堆结合。

iofile 之 house of orange

先看管理 file 的 _IO_list_all 链表:

有几个关键的:

_chain 指向该链表下一个节点:

查看 vtable:

vtable + 3 是 _IO_OVERFLOW 函数的地址,修改它为 system 的地址,在 unsorted bin 报错的时候就可以 getshell。

unsorted bin 中错误的fd/bk指针,会触发 malloc_printer 函数打印错误信息,malloc_printer 调用 __libc_message,_libc_message 调用 abort(),abort() 调用 _IO_flush_all_lockp。

在 _IO_flush_all_lockp 中,通过对链表结构 _IO_list_all 中每个节点进行遍历,找到符合条件的节点,执行 _IO_OVERWRITE 函数,其中特点是 _ IO_FILE_PLUS 类型的结构体,对函数的查找需要通过 vtable 定位函数表。如果我们可以劫持 IO 表中的 _IO_OVERFLOW 就可以 getshell。

可以看见 _IO_FILE_plus 就是在堆上:

打印一波:

计算可知 28 个 p64 构成一个 _IO_FILE_plus 结构体。

一个简单的利用就是伪造一个 _IO_FILE_plus 结构体,然后把它连接在 _IO_list_all 上。在结构体中,把 vtable 指向一个可控的地址,这个地址 +3 写入 system_addr。

获取到 libc 基地址地址就能获取到 _IO_list_all 的地址。一种改写方式是利用 unsorted bin。

在malloc的过程中,unsorted bin 会从链表上卸下来,将其中最后一个 chunk 取出,并把倒数第二个 chunk 的 fd 设置为 unsortedbin_chunk(av) 的地址其实就是 (&main_arena+88),而此时我们将 unsorted bin 中的 chunk 的 bk 改成 _IO_list_all-0x10,这样从 unsorted bin 中取出它时,就可以成功将 _IO_list_all 改写为 &main_arena+88 了。

将 _IO_list_all 改写为 &main_arena + 88 的地址后,而 fd -> chain 的位置是在 偏移 + 68 处,&main_arena + 88 + 68 刚好是 size 为 0x60 的 small_bin 的 bk,这个可以控制;又只含一个 chunk 时,small_bin chunk 的 bk 指向它本身。因此只要在该 chunk 上伪造 fileplus 结构体即可。

_IO_list_all -> main_arena + 88 -> chain (+68) -> (fake file)(size = 0x60)small_bin chunk

这大概就是 house of orange.