---
title: "Dissecting House of Apple 2 on modern glibc"
slug: dissecting-house-of-apple-2-on-modern-glibc
url: https://listedarticles.com/articles/dissecting-house-of-apple-2-on-modern-glibc
canonical_url: https://jazho76.github.io/house_of_apple_2/
content_type: tutorial
language: en
published_at: 2026-09-26T12:00:00.000Z
updated_at: 2026-09-27T09:13:23.839Z
author: "jazho76"
author_url: https://jazho76.github.io
authored_by: human
publisher: "jazho76"
publisher_url: https://jazho76.github.io
topics: ["Security", "Linux", "Systems Programming", "Tutorials"]
license: all-rights-reserved
word_count: 383
reading_minutes: 2
citation: "jazho76, jazho76. \"Dissecting House of Apple 2 on modern glibc.\" 26 Sept 2026. https://jazho76.github.io/house_of_apple_2/ (all-rights-reserved)"
# The full text follows. The web page shows an extract and sends readers
# to the source above; quote the citation and link the canonical URL.
---

# Dissecting House of Apple 2 on modern glibc

> An interactive GDB walkthrough of House of Apple 2 on glibc 2.43: FSOP past vtable checks, wide-stream arbitrary call, stack pivot, and ROP—with a follow-along lab.

# Dissecting House of Apple 2 on modern glibc

**Author:** jazho76  
**Source:** [jazho76.github.io](https://jazho76.github.io/house_of_apple_2/)  
**Lab:** [github.com/jazho76/house_of_apple_2](https://github.com/jazho76/house_of_apple_2)

An interactive GDB walkthrough of House of Apple 2, from FSOP to stack pivot and ROP on glibc 2.43 (Ubuntu 26.04 / Fedora 44 packaging at time of writing).

File Stream Oriented Programming (FSOP) manipulates glibc file stream structures to hijack control flow. Modern glibc validates `_IO_FILE_plus` vtables, so replacing the vtable with an arbitrary address aborts via `_IO_vtable_check` / `IO_validate_vtable` (vtable must fall in `[__io_vtables, __io_vtables + IO_VTABLES_LEN)`).

## House of Apple 2

Originally introduced by Roderick, House of Apple 2 works around this by using a *valid* `_IO_FILE_plus` vtable to reach the wide-character stream machinery, where a secondary `_wide_vtable` is dispatched **without** range validation — yielding an arbitrary-call primitive that escalates into a stack pivot and ROP.

### Prerequisites

Overwrite a `FILE` structure; heap leak and libc leak. Sandbox provides interactive `fopen`/`fread`/`fwrite`/`fclose` with GDB/pwndbg.

### Wide-character path

`_wide_data` → `_IO_wide_data` with its own `_wide_vtable`. Path through `_IO_wfile_overflow` → `_IO_wdoallocbuf` → `_IO_WDOALLOCATE` dispatches `_wide_vtable + 0x68` with no validation.

Conditions to reach `_IO_wdoallocbuf`:

- `_flags` must not contain `_IO_NO_WRITES` (`0x0008`) or `_IO_UNBUFFERED` (`0x0002`)
- `_wide_data->_IO_write_base` and `_IO_buf_base` must be `NULL`
- `_lock` must point to a zero-initialized writable 0x10-byte region

### Compact overlapping payload

Fake `_IO_wide_data` starts at offset `0x08` inside the fake `_IO_FILE_plus`. Key layout:

| Offset | Role |
| --- | --- |
| `0x00` | `_flags` (bit constraints) |
| `0x20` / `0x38` | write/buf bases NULL |
| `0x88` | `_lock` |
| `0xa0` | `_wide_data` → `base+0x08` |
| `0xd8` | outer vtable → `_IO_wfile_overflow` slot |
| `0xe0` | arbitrary function pointer (`wide_vtable+0x68`) |

At the call site, `RDI` and `RDX` point to the controlled `FILE`.

## Stack pivot and ROP

`mov rsp, rdx; ret` in `__push___start_context+63` pivots into the fake structure. First qword (`_flags`) needs LSB bits clear of `0x2`/`0x8` — use a mid-instruction `ret` gadget. NULL holes at write/buf bases consumed via `pop` gadgets. `_lock` at `0x88` cannot be overwritten — ~17 qwords remain for the chain (demo: `execve("/bin/sh", NULL, …)`).

## Conclusion

House of Apple 2 remains reproducible on glibc 2.43: a valid vtable reaches unvalidated wide-stream dispatch. Offsets/gadgets vary by build; the control-flow idea still applies.
