House of Apple2 libc-2.35
参考资料:https://xz.aliyun.com/news/16212
House of Apple 一共有 apple1,apple2,apple3 三种攻击方式,我们这里先学习 apple2 的攻击调用。
House of Apple 攻击的调用背景:
Libc-2.35 及以后,glibc 将许多的 hook 都移除了,包括但不限于malloc_hook,free_hook等,这也是为什么2.35称为pwn的寒冬,低版本的许多利用方式几乎都失效了。到了2.35及以后,堆利用便离不开对 _IO_FILE 的伪造和对 IO 流的劫持。 安全研究员(如 Roderick)提出了一系列被称为 House of Apple 的攻击链,利用 _IO_FILE 结构体中的 Wide Data 机制来劫持程序流程。
什么是 house of Apple2
House of Apple 2 的本质是 FSOP (File Stream Oriented Programming)。它通过伪造或篡改 _IO_FILE 结构体,诱导程序在执行 IO 操作(如程序退出、调用 exit、abort 或手动刷新流)时,跳转到我们控制的恶意代码。
与 House of Apple 1(利用 _IO_wfile_underflow)不同,House of Apple 2 主要利用 _IO_wfile_overflow 函数实现 RCE。
利用条件:
house of apple2可以说是高版本中所需利用条件最少的攻击方式
- 能够刷新IO流,能控制程序执行 IO 操作,包括但不限于:从 main 函数返回、调用 exit 函数、通过__malloc_assert 触发
- 能够泄露libc基址和heap地址
- 能控制_IO_FILE 的 vtable 和_wide_data,一般使用 largebin attack 去控制
只用一次largebin attack在高版本的利用中是相当少见的,这也意味着house of apple2可以在更多的限制下来展开攻击
利用思路:
1 | pwndbg> p *_IO_list_all |
house of apple2主要是通过伪造FILE结构体来完成攻击,而在2.24以后的glibc中,FILE结构体中的Vtable指针不能被劫持到任意地址,会有一个IO_validate_vtable函数对其指向的地址进行检测,下面是它的源码
1 | /* 执行 vtable 指针合法性校验。如果校验失败,直接终止进程。 */ |
而house of apple2主要针对的是_IO_FILE中的__wide_data成员,_IO_wide_data指向的结构体是一个和FILE结构体十分相像的 _IO_wide_data结构体,下面是他的内容
1 | pwndbg> p _IO_wide_data_2 |
_IO_wide_data同样具有一个vtable指针去指向一个虚表,而这个vtable指针所指向的内容是没有检测的,这意味着我们可以把它劫持到我们伪造的虚表,从而控制执行流。
调用链介绍-_IO_wfile_overflow
下面是我们可以开展攻击的一条调用链:
1 | _IO_wfile_overflow --> _IO_wdoallocbuf --> _IO_WDOALLOCATE |
我们可以去了解一下这条调用链:
当程序执行 exit 退出时,会刷新 FILE 结构体里面的所有内容:

在刷新 FILE 结构体的时候会执行 _IO_flush_all_lockp 函数,
在这个过程中会调用到 _IO_wfile_overflow 函数

而在调用_IO_wfile_overflow函数的时候,会调用到IO_wdoallocbuf函数,我们来看看这个函数的源码
1 | void |
里面就会执行到_IO_wdoallocbuf 函数,这个函数就是我们要劫持的函数。
总结
- 利用largebin attack向IO_list_all里面写入一个可控的堆地址
- 在这个堆块里面同时伪造一个_IO_list_all结构体和IO_wide_data结构体,以及他们对应的vtable指针
- _IO_list_all结构体的vtable指针指向 _IO_wfile_jumps来绕过检查,而 _wide_data的结构体指向我们伪造的虚表即可
利用原理:
stdin/stdout/stderr 这三个_IO_FILE 结构体使用的是_IO_file_jumps 这个 vtable,而当需要调用到 vtable 里面的函数指针时,会使用宏去调用。以_IO_file_overflow 调用为例,glibc 中调用的代码片段分析如下:
1 |
其中 _IO_validate_vtable 函数负责检查 vtable 的合法性,会判断 vtable 的地址是不是在一个合法的区间。如果 vtable 的地址不合法,程序将会异常终止。
我们前面说了:struct _IO_wide_data 结构体有一个 _wide_vatble 成员:
1 | struct _IO_wide_data |
在调用_wide_vtable 虚表里面的函数时,同样是使用宏去调用,仍然以 vtable->_overflow 调用为例,所用到的宏依次为:
1 |
可以看到,在调用 _wide_vtable 里面的成员函数指针时,没有关于 vtable 的合法性检查。
因此我们可以劫持 _IO_FILE 的 vtable 为 _IO_wfile_jumps,控制 _wide_data 为可控的堆地址空间,进而控制 _wide_data -> _wide_vtable 为可控的堆地址空间。
更新: 2026-04-26 11:49:31
原文: https://www.yuque.com/idcm/wnemg9/wp1op5a1r0xovm9d