
Recorrido interactivo de GDB de la técnica FSOP House of Apple 2 en glibc 2.43, con un sandbox reproducible que cubre el bypass de vtable, el stack pivoting y ROP.
Este repositorio es un entorno de pruebas autocontenido inspirado en el módulo de Explotación de Estructuras de Archivos de pwn.college. No introduce una nueva variación de House of Apple 2, solo responde a mi curiosidad sobre cómo se mantiene esta técnica en versiones recientes de glibc y si sigue siendo una ruta de explotación viable. El documento proporciona un recorrido interactivo por GDB que los lectores pueden seguir junto con el sandbox para desarrollar una comprensión más intuitiva de la primitiva. Todos los experimentos utilizan glibc 2.43, tal como se empaqueta en Ubuntu 26.04 y Fedora 44 en el momento de escribir esto.
Programación Orientada a Flujos de Archivo (FSOP). Se trata de manipular las estructuras de flujo de archivo de glibc para secuestrar el flujo de control. Una forma de hacerlo es corrompiendo el mecanismo de despacho de la vtable de _IO_FILE_plus. La glibc moderna valida esta vtable, por lo que el enfoque obvio de reemplazarla con una dirección arbitraria no funciona.
House of Apple 2, introducida originalmente por Roderick, sortea esta restricción utilizando una vtable válida de _IO_FILE_plus para alcanzar la maquinaria de flujos de caracteres anchos, donde se despacha directamente una vtable secundaria sin validación de rango. Esto proporciona una primitiva de llamada arbitraria que podemos escalar a un stack pivot y una cadena ROP.
Esta exploración asume que podemos sobrescribir una estructura FILE y que disponemos tanto de un leak de heap como de un leak de libc. El binario objetivo ya proporciona esto.
El sandbox ejecuta Ubuntu 26.04 LTS, lo que nos brinda un entorno moderno para explorar la técnica.
La imagen incluye GDB, pwndbg, pwntools, ropper y tmux. También contiene un binario objetivo con un menú interactivo para invocar operaciones de flujo de archivo como fopen, fread, fwrite y fclose. Esto nos proporciona una forma conveniente de manipular flujos mientras depuramos y probamos ideas.
Construye y ejecuta el sandbox con:``` ./build.sh ./run.sh
## Exploración
Comencemos inspeccionando las estructuras `_IO_FILE` y `_IO_FILE_plus`:```c
pwndbg> ptype struct _IO_FILE
type = struct _IO_FILE {
int _flags;
char *_IO_read_ptr;
char *_IO_read_end;
char *_IO_read_base;
char *_IO_write_base;
char *_IO_write_ptr;
char *_IO_write_end;
char *_IO_buf_base;
char *_IO_buf_end;
char *_IO_save_base;
char *_IO_backup_base;
char *_IO_save_end;
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
int _flags2 : 24;
char _short_backupbuf[1];
__off_t _old_offset;
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
_IO_lock_t *_lock;
__off64_t _offset;
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
struct _IO_FILE **_prevchain;
int _mode;
int _unused3;
__uint64_t _total_written;
char _unused2[8];
}
pwndbg> ptype struct _IO_FILE_plus
type = struct _IO_FILE_plus {
FILE file;
const struct _IO_jump_t *vtable;
}
En términos prácticos, _IO_FILE_plus es un _IO_FILE con un puntero a vtable. Eso resulta inmediatamente interesante: si podemos controlar este puntero, podríamos redirigir una llamada indirecta y secuestrar el flujo de control.
Para inspeccionar la vtable, examinemos un puntero FILE devuelto por fopen.```c
pwndbg> p *(struct _IO_FILE_plus *)0x37ecf010
$4 = {
file = {
_flags = 0xfbad2480,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7f58a7f4b4a0 <IO_2_1_stderr>,
_fileno = 0x3,
_flags2 = 0x0,
_short_backupbuf = "",
_old_offset = 0x0,
_cur_column = 0x0,
_vtable_offset = 0x0,
_shortbuf = "",
_lock = 0x37ecf0f0,
_offset = 0xffffffffffffffff,
_codecvt = 0x0,
_wide_data = 0x37ecf100,
_freeres_list = 0x0,
_freeres_buf = 0x0,
_prevchain = 0x7f58a7f4b480 <_IO_list_all>,
_mode = 0x0,
_unused3 = 0x0,
_total_written = 0x0,
_unused2 = "\000\000\000\000\000\000\000"
},
vtable = 0x7f58a7f49030 <_IO_file_jumps>
}
El puntero apunta a la tabla `_IO_file_jumps`.

Este es un conjunto de 21 punteros a función. Las operaciones de flujo de archivo se despachan a través de diferentes entradas según la ruta de ejecución.
### Siguiendo la ruta de `fwrite`
Para esta exploración me centraré en la ruta de `fwrite`. Después de colocar puntos de interrupción en cada función y llamar a `fwrite`, el primer punto de interrupción que alcanzamos es `_IO_file_xsputn`.

La llamada ocurre en `fwrite+216`. Esto coincide con el [código fuente de glibc](https://elixir.bootlin.com/glibc/glibc-2.43/source/libio/iofwrite.c#L44): `_IO_sputn` es una macro que se despacha a través de la vtable, resolviéndose a `_IO_file_xsputn` para este flujo.```asm
0x00007fd5181d362a <+202>: mov rdx,rcx
0x00007fd5181d362d <+205>: mov rdi,rbx
0x00007fd5181d3630 <+208>: mov QWORD PTR [rbp-0x30],r8
0x00007fd5181d3634 <+212>: mov QWORD PTR [rbp-0x28],rcx
0x00007fd5181d3638 <+216>: call QWORD PTR [rax+0x38]
Como primer intento, sobrescribamos el puntero de la vtable con desired_func - 0x38 y establezcamos un punto de interrupción en fwrite+216.```c
pwndbg> p &win
$3 = (<text variable, no debug info> *) 0x4019e1
pwndbg> p/x &win - 0x38
$4 = 0x4019a9
pwndbg> set ((struct _IO_FILE_plus *)0x5334010)->vtable = (void *)0x4019a9
pwndbg> b *fwrite+216
Breakpoint 4 at 0x7fd5181d3638: file ./libio/libioP.h, line 1042.

La ejecución se aborta antes de alcanzar el punto de interrupción. El error sugiere que glibc valida el puntero de la vtable antes de realizar la llamada indirecta. Inspeccionemos el backtrace y veamos dónde ocurre esto.
`fwrite` llega a `_IO_vtable_check`, que está rechazando el puntero de vtable falsificado.

La implementación contiene un mecanismo para aceptar vtables externas, pero no está bajo nuestro control. El código relevante está disponible en [`vtables.c`](https://elixir.bootlin.com/glibc/glibc-2.43/source/libio/vtables.c#L504).
### Entendiendo la validación de la vtable