Skip to content
Open
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
92 changes: 92 additions & 0 deletions src/binary-exploitation/common-exploiting-problems.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,97 @@ so the combined residue uniquely equals the 64-bit pointer to `kCFNull`. The Pro

No memory safety bug is required—simply observing serialization order of pointer-keyed structures yields a remote ASLR bypass primitive.

## Kernel waiter cleanup confusion, stack-UAF reclaim & constrained tree-writes

Some kernel bugs become exploitable because a cleanup helper assumes that **`current` owns the object being cleaned up**, but a later proxy path reuses that helper **on behalf of another task**. If rollback removes the waiter from a lock/tree but clears the blocked state on the wrong task, the real owner can keep a **dangling pointer to a stack object** that belonged to a previous syscall frame.

### Proxy cleanup confusion in PI/futex style paths

Look for code with this shape:

- a slow-path helper was originally written for self-blocking tasks
- a proxy/requeue path later reuses the same helper for another sleeping task
- rollback dequeues the waiter/object but clears the blocked pointer/state on `current`
- later priority-chain / wait-chain / lock-owner walks dereference the stale pointer

This is especially interesting when the stale object was allocated in a **kernel stack frame** (not heap), because the owner task can often reclaim the same stack region itself.

### Deterministic rollback by creating a dependency cycle

If exploitation needs a rare rollback path, try to force a **dependency cycle** instead of racing for a tiny window. In the futex PI case, a reliable pattern is:

1. Thread A holds `f_pi_chain` and sleeps in `FUTEX_WAIT_REQUEUE_PI(f_wait -> f_pi_target)`.
2. Thread B holds `f_pi_target` and blocks on `f_pi_chain`.
3. Thread C calls `FUTEX_CMP_REQUEUE_PI(f_wait -> f_pi_target)`.

The kernel observes:

```text
waiter -> f_pi_target -> owner -> f_pi_chain -> waiter
```

and the chain walk returns `-EDEADLK`, driving cleanup/rollback code without needing privileges.

### Reclaiming a freed kernel stack frame with same-thread syscall locals

When a stale pointer targets a **previous syscall frame on a task's own kernel stack**, the same task can often reclaim it by immediately entering another syscall whose **controlled local buffer lands at a similar stack depth**.

Useful reclaim candidates are syscalls that copy attacker-controlled data into large stack locals, such as:

- `prctl(PR_SET_MM, PR_SET_MM_MAP, ...)`
- `clone`
- `setsockopt`
- `pselect`
- `keyctl`

If the copy source is user-controlled, page-boundary placement plus concurrent invalidation (for example a backing file hole-punch race) may stretch the `copy_from_user()` window long enough for another thread to consume the forged frame.

### Turning tree erase into a constrained pointer write

If the forged stale object is later passed to a tree-removal primitive such as `rb_erase()`, shape it as a **single-child root** so removal promotes the chosen child into the root slot. If the surrounding memory is reinterpreted as:

```text
target - 8 -> lock / metadata / spinlock fields
target -> tree root pointer
target + 8 -> sibling / leftmost metadata
target + 16 -> owner / state
```

the primitive often becomes:

```c
*(uint64_t *)target = controlled_child;
```

This is usually **not** a fully arbitrary write. Typical constraints are:

- the qword before the target must look like an unlocked lock
- metadata after the target must not force unsafe dereferences
- the written pointer must reference a self-consistent fake object that survives the remaining walk

### Small controlled kernel staging areas

If the constrained write only lets you redirect an existing function/object pointer, a **small but stable kernel buffer** is often enough. On x86, the **CPU Entry Area (CEA)** can be reused as a compact staging area for:

- fake objects that survive sanity checks
- safe dereference targets
- pivot data
- a very short ROP/JOP chain

If the virtual CEA mapping is randomized, the **direct-map alias** can still be useful once `physmap` is leaked.

### One-write privilege flips (DirtyMode style)

When the hijack gives only a **very short** control-flow window, do not spend it on a full credential overwrite. A more reliable pattern is a **single kernel write** that weakens a permissions gate and then completes privilege escalation from userspace.

A representative target is a writable `ctl_table` entry such as `core_pattern`:

- flip the mode field so `/proc/sys/kernel/core_pattern` becomes writable by an unprivileged user
- write a pipe handler such as `|/proc/%P/fd/666 %P`
- crash a helper process so the kernel executes the attacker-controlled handler as root

This pattern is useful whenever the initial primitive can reach writable policy bits more easily than `cred` or a long in-kernel ROP chain.

## Related pages

{{#ref}}
Expand All @@ -235,6 +326,7 @@ common-exploiting-problems-unsafe-relocation-fixups.md
- [FD duplication exploit example](https://ir0nstone.gitbook.io/notes/types/stack/exploiting-over-sockets/exploit)
- [Socat delete-character behaviour](https://ir0nstone.gitbook.io/hackthebox/challenges/pwn/dream-diary-chapter-1/unlink-exploit)
- [FuzzMe – Reverse Engineering and Fuzzing an Android Shared Library](https://hackmd.io/@sal/fuzzme-mobilehackinglab-ctf-writeup)
- [IonStack Part II: GhostLock, a stack-UAF that has existed in ALL Linux distributions for 15 years](https://nebusec.ai/research/ionstack-part-2)
- [Pointer leaks through pointer-keyed data structures (Project Zero)](https://projectzero.google/2025/09/pointer-leaks-through-pointer-keyed.html)

{{#include ../banners/hacktricks-training.md}}