diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 2874e392e6d..5901e8d4821 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -843,6 +843,7 @@ - [Use After Free](binary-exploitation/libc-heap/use-after-free/README.md) - [First Fit](binary-exploitation/libc-heap/use-after-free/first-fit.md) - [Double Free](binary-exploitation/libc-heap/double-free.md) + - [Gnu Obstack Function Pointer Hijack](binary-exploitation/libc-heap/gnu-obstack-function-pointer-hijack.md) - [Overwriting a freed chunk](binary-exploitation/libc-heap/overwriting-a-freed-chunk.md) - [Heap Overflow](binary-exploitation/libc-heap/heap-overflow.md) - [Unlink Attack](binary-exploitation/libc-heap/unlink-attack.md) diff --git a/src/binary-exploitation/libc-heap/README.md b/src/binary-exploitation/libc-heap/README.md index 8606e46d5f5..70b470e9088 100644 --- a/src/binary-exploitation/libc-heap/README.md +++ b/src/binary-exploitation/libc-heap/README.md @@ -535,6 +535,10 @@ Study allocator-specific primitives derived from real-world bugs: virtualbox-slirp-nat-packet-heap-exploitation.md {{#endref}} +{{#ref}} +gnu-obstack-function-pointer-hijack.md +{{#endref}} + ## References - [https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/](https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/) diff --git a/src/binary-exploitation/libc-heap/gnu-obstack-function-pointer-hijack.md b/src/binary-exploitation/libc-heap/gnu-obstack-function-pointer-hijack.md new file mode 100644 index 00000000000..7e3425d47c0 --- /dev/null +++ b/src/binary-exploitation/libc-heap/gnu-obstack-function-pointer-hijack.md @@ -0,0 +1,67 @@ +# GNU obstack function-pointer hijack + +{{#include ../../banners/hacktricks-training.md}} + +## Overview + +GNU obstacks embed allocator state together with two indirect call targets: + +- `chunkfun` (offset `+0x38`) with signature `void *(*chunkfun)(void *, size_t)` +- `freefun` (offset `+0x40`) with signature `void (*freefun)(void *, void *)` +- `extra_arg` and a `use_extra_arg` flag select whether `_obstack_newchunk` calls `chunkfun(new_size)` or `chunkfun(extra_arg, new_size)` + +If an attacker can corrupt an application-owned `struct obstack *` or its fields, the next growth of the obstack (when `next_free == chunk_limit`) triggers an indirect call through `chunkfun`, enabling code execution primitives. + +## Primitive: size_t desync → 0-byte allocation → pointer OOB write + +A common bug pattern is using a **32-bit register** to compute `sizeof(ptr) * count` while storing the logical length in a 64-bit `size_t`. + +- Example: `elements = obstack_alloc(obs, sizeof(void *) * size);` is compiled as `SHL EAX,0x3` for `size << 3`. +- With `size = 0x20000000` and `sizeof(void *) = 8`, the multiplication wraps to `0x0` in 32-bit, so the pointer array is **0 bytes**, but the recorded `size` remains `0x20000000`. +- Subsequent `elements[curr++] = ptr;` writes perform **8-byte OOB pointer stores** into adjacent heap objects, giving a controlled cross-object overwrite primitive. + +## Leaking libc via `obstack.chunkfun` + +1. Place two heap objects adjacent (e.g., two stacks built with separate obstacks). +2. Use the pointer-array OOB write from object A to overwrite object B’s `elements` pointer so that a `pop`/read from B dereferences an address inside object A’s obstack. +3. Read `chunkfun` (`malloc` by default) at offset `0x38` to disclose a libc function pointer, then compute `libc_base = leak - malloc_offset` and derive other symbols (e.g., `system`, `"/bin/sh"`). + +## Hijacking `chunkfun` with a fake obstack + +Overwrite a victim’s stored `struct obstack *` to point at attacker-controlled data that mimics the obstack header. Minimal fields needed: + +- `next_free == chunk_limit` to force `_obstack_newchunk` on next push +- `chunkfun = system_addr` +- `extra_arg = binsh_addr`, `use_extra_arg = 1` to select the two-argument call form + +Then trigger an allocation on the victim obstack to execute `system("/bin/sh")` through the indirect call. + +Example fake obstack layout (glibc 2.42 offsets): + +```python +fake = b"" +fake += p64(0x1000) # chunk_size +fake += p64(heap_leak) # chunk +fake += p64(heap_leak) # object_base +fake += p64(heap_leak) # next_free == chunk_limit +fake += p64(heap_leak) # chunk_limit +fake += p64(0xF) # alignment_mask +fake += p64(0) # temp +fake += p64(system_addr) # chunkfun +fake += p64(0) # freefun +fake += p64(binsh_addr) # extra_arg +fake += p64(1) # use_extra_arg flag set +``` + +## Attack recipe + +1. **Trigger size wrap** to create a 0-byte pointer array with a huge logical length. +2. **Groom adjacency** so an OOB pointer store reaches a neighbor object containing an obstack pointer. +3. **Leak libc** by redirecting a victim pointer to the neighbor obstack’s `chunkfun` and reading the function pointer. +4. **Forge obstack** data with controlled `chunkfun`/`extra_arg` and force `_obstack_newchunk` to land in the forged header, yielding a function-pointer call of the attacker’s choice. + +## References + +- [Flagvent 2025 FV25.08 obstack exploit (0xdf)](https://0xdf.gitlab.io/flagvent2025/hard) + +{{#include ../../banners/hacktricks-training.md}}