Reindirizzamento di puntatori

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Puntatori a stringhe

Se una chiamata di funzione utilizzerà l’indirizzo di una stringa che si trova nello stack, è possibile abusare del buffer overflow per sovrascrivere questo indirizzo e inserire l’indirizzo di una stringa diversa all’interno del binary.

Se ad esempio una chiamata a system sta per usare l’indirizzo di una stringa per eseguire un comando, un attacker potrebbe collocare l’indirizzo di una stringa diversa nello stack, ad esempio export PATH=.:$PATH, e creare nella directory corrente uno script con il nome della prima lettera della nuova stringa, dato che questo verrà eseguito dal binary.

Nei target reali, reindirizzare un puntatore a stringa sullo stack è di solito più interessante che cambiare solo il testo stampato:

  • Reindirizzare un argomento di una successiva chiamata system/popen/execl* verso un "/bin/sh" già presente in memoria o verso una stringa controllata dall’attacker.
  • Reindirizzare un sink di read successivo come puts("%s", ptr) o write(fd, ptr, len) per leakare dati da stack, heap o binary.
  • Reindirizzare un sink di write successivo come strcpy(dst, ...), memcpy(dst, src, len) o l’assegnazione di un campo di struttura tramite ptr->field = value per trasformare il stack overflow in una scrittura arbitraroria di secondo stadio.

Durante l’audit, dai priorità ai locali sullo stack come char *cmd, char *path, char *buf, FILE *fp, o ai puntatori all’interno di strutture temporanee request/response che vengono usati dopo l’overflow ma prima che la funzione ritorni. Questo è particolarmente utile quando l’overflow non può raggiungere in sicurezza l’indirizzo di ritorno salvato a causa di un canary o quando corrompere un puntatore vicino è sufficiente.

Se la corruzione è limitata a una sovrascrittura parziale (per esempio perché il bug appende un 0x00), prova a reindirizzare il puntatore verso:

  • Una stringa vicina nello stesso frame di stack
  • Un altro oggetto nello stesso modulo / immagine non-PIE
  • Una regione controllata i cui byte più alti rimangono invariati

Per il caso correlato orientato a ASLR in cui un NUL finale modifica un puntatore di stack esistente invece di una variabile locale dedicata, vedi Ret2ret & Reo2pop.

Puoi trovare un esempio di questo in:

Function pointers

Stesso concetto dei puntatori a stringa ma applicato alle funzioni: se lo stack contiene l’indirizzo di una funzione che verrà chiamata, è possibile cambiarlo (es. per chiamare system).

I target utili non sono solo variabili callback esplicite come void (*fp)(). In pratica, cerca:

  • Callback memorizzati in struct locali passate poi a helper
  • Destructor / cleanup handlers invocati sui percorsi di errore
  • Parser dispatch tables o handler di macchine a stati copiati sullo stack
  • Struct / oggetti locali che successivamente dispacciano tramite una chiamata indiretta

Nella exploitation moderna, il reindirizzamento di puntatori è spesso il primo/ultimo primitive disponibile prima di toccare il canary. Un writeup del 2024 per CVE-2024-20017 mostra il pattern tipico: l’overflow raggiunge diverse variabili locali prima del stack canary, l’attacker corrompe un puntatore sullo stack insieme al suo length/value associato, e una successiva assegnazione tramite quel puntatore diventa una scrittura arbitraria senza mai dover ritornare attraverso il frame corrotto.

Corruzione di puntatori per primitive di secondo stadio

Se un puntatore vicino viene poi dereferenziato per uno store, l’obiettivo di solito non è saltare direttamente con il primo overflow, ma aggiornare la primitive:

  1. Overflow di un buffer locale e corruzione di un puntatore più qualsiasi length / integer / index associato.
  2. Aspettare che la funzione esegua una dereferenziazione post-overflow come ptr->len = x, memcpy(ptr, src, n) o *ptr = value.
  3. Usare quella scrittura risultante di tipo write-what-where per sovrascrivere una slot GOT, una callback, un puntatore di config o un altro callsite indiretto.

Questa è una buona opzione quando:

  • Il bug si arresta al canary
  • Il puntatore a funzione stesso non è direttamente raggiungibile
  • Una scrittura di dati di 4 o 8 byte è più facile da ottenere rispetto a un immediato hijack del controllo di flusso

La stessa idea funziona anche per primitive di read se il puntatore corrotto viene poi passato a helper di logging, stampa o invio di rete.

Nota moderna AArch64: PAC / BTI

Su target AArch64 attuali, una classica sovrascrittura dell’indirizzo di ritorno salvato può fallire perché l’epilogo autentica x30 con PAC. In questi casi, gli hijack non basati sul return come i puntatori a funzione locali corrotti o i puntatori a callback diventano più attraenti.

Tuttavia, se BTI è abilitato, il target della chiamata indiretta sovrascritta deve comunque atterrare su un valid landing pad (tipicamente un entry di funzione con bti c, o in codice con PAC un prologo che inizia con paciasp/pacibsp). Pertanto, quando si reindirizza un puntatore a funzione sullo stack su AArch64, preferisci:

  • Voci di funzione reali invece di gadget a metà funzione
  • Target il cui prologo soddisfa già BTI
  • Target dove il puntatore alla chiamata indiretta non è ulteriormente autenticato prima dell’uso

Per un contesto AArch64 correlato allo stack-overflow, vedi ret2win-arm64.

Puoi trovare un esempio in:

Riferimenti

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks