Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/binary-exploitation/libc-heap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/)
Expand Down
Original file line number Diff line number Diff line change
@@ -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}}