hacktricks/binary-exploitation/stack-overflow/README.md

7.9 KiB

Stack Overflow

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Cos'è uno Stack Overflow

Un stack overflow è una vulnerabilità che si verifica quando un programma scrive più dati nello stack di quanti ne siano allocati per contenerli. Questi dati in eccesso sovrascriveranno lo spazio di memoria adiacente, portando alla corruzione di dati validi, interruzione del flusso di controllo e potenzialmente all'esecuzione di codice malevolo. Questo problema si verifica spesso a causa dell'uso di funzioni non sicure che non eseguono controlli sui limiti dell'input.

Il problema principale di questa sovrascrittura è che il puntatore di istruzione salvato (EIP/RIP) e il puntatore di base salvato (EBP/RBP) per tornare alla funzione precedente sono memorizzati nello stack. Pertanto, un attaccante sarà in grado di sovrascrivere questi e controllare il flusso di esecuzione del programma.

La vulnerabilità di solito si verifica perché una funzione copia nello stack più byte della quantità allocata per essa, riuscendo quindi a sovrascrivere altre parti dello stack.

Alcune funzioni comuni vulnerabili a questo sono: strcpy, strcat, sprintf, gets... Inoltre, funzioni come fgets, read & memcpy che prendono un argomento di lunghezza, potrebbero essere utilizzate in modo vulnerabile se la lunghezza specificata è maggiore di quella allocata.

Ad esempio, le seguenti funzioni potrebbero essere vulnerabili:

void vulnerable() {
char buffer[128];
printf("Enter some text: ");
gets(buffer); // This is where the vulnerability lies
printf("You entered: %s\n", buffer);
}

Trovare gli offset degli Stack Overflow

Il modo più comune per trovare gli stack overflow è fornire un input molto grande di As (ad esempio python3 -c 'print("A"*1000)') e aspettarsi un Segmentation Fault che indica che l'indirizzo 0x41414141 è stato tentato di essere accesso.

Inoltre, una volta trovata la vulnerabilità di Stack Overflow, sarà necessario trovare l'offset fino a quando non sarà possibile sovrascrivere l'indirizzo di ritorno, per questo di solito si utilizza una sequenza di De Bruijn. Che per un dato alfabeto di dimensione k e sottosequenze di lunghezza n è una sequenza ciclica in cui ogni possibile sottosequenza di lunghezza _n_** appare esattamente una volta** come sottosequenza contigua.

In questo modo, invece di dover capire manualmente quale offset è necessario per controllare l'EIP, è possibile utilizzare come padding una di queste sequenze e poi trovare l'offset dei byte che hanno finito per sovrascriverlo.

È possibile utilizzare pwntools per questo:

from pwn import *

# Generate a De Bruijn sequence of length 1000 with an alphabet size of 256 (byte values)
pattern = cyclic(1000)

# This is an example value that you'd have found in the EIP/IP register upon crash
eip_value = p32(0x6161616c)
offset = cyclic_find(eip_value)  # Finds the offset of the sequence in the De Bruijn pattern
print(f"The offset is: {offset}")

o GEF:

#Patterns
pattern create 200 #Generate length 200 pattern
pattern search "avaaawaa" #Search for the offset of that substring
pattern search $rsp #Search the offset given the content of $rsp

Sfruttare gli Overflow dello Stack

Durante un overflow (supponendo che la dimensione dell'overflow sia abbastanza grande) sarai in grado di sovrascrivere i valori delle variabili locali all'interno dello stack fino a raggiungere il EBP/RBP e EIP/RIP salvati (o anche di più).
Il modo più comune per abusare di questo tipo di vulnerabilità è modificare l'indirizzo di ritorno in modo che, quando la funzione termina, il flusso di controllo venga reindirizzato ovunque l'utente abbia specificato in questo puntatore.

Tuttavia, in altri scenari, potrebbe essere sufficiente sovrascrivere alcuni valori delle variabili nello stack per l'exploitation (come in facili sfide CTF).

Ret2win

In questo tipo di sfide CTF, c'è una funzione all'interno del binario che non viene mai chiamata e che devi chiamare per vincere. Per queste sfide devi solo trovare l'offset per sovrascrivere l'indirizzo di ritorno e trovare l'indirizzo della funzione da chiamare (di solito ASLR sarebbe disabilitato) in modo che, quando la funzione vulnerabile restituisce, la funzione nascosta venga chiamata:

{% content-ref url="ret2win/" %} ret2win {% endcontent-ref %}

Stack Shellcode

In questo scenario, l'attaccante potrebbe posizionare uno shellcode nello stack e abusare dell'EIP/RIP controllato per saltare allo shellcode ed eseguire codice arbitrario:

{% content-ref url="stack-shellcode/" %} stack-shellcode {% endcontent-ref %}

Tecniche ROP & Ret2...

Questa tecnica è il framework fondamentale per bypassare la principale protezione della tecnica precedente: Stack non eseguibile (NX). E consente di eseguire diverse altre tecniche (ret2lib, ret2syscall...) che termineranno eseguendo comandi arbitrari abusando delle istruzioni esistenti nel binario:

{% content-ref url="../rop-return-oriented-programing/" %} rop-return-oriented-programing {% endcontent-ref %}

Heap Overflows

Un overflow non sarà sempre nello stack, potrebbe anche essere nell'heap, per esempio:

{% content-ref url="../libc-heap/heap-overflow.md" %} heap-overflow.md {% endcontent-ref %}

Tipi di protezioni

Ci sono diverse protezioni che cercano di prevenire l'exploitation delle vulnerabilità, controllale in:

{% content-ref url="../common-binary-protections-and-bypasses/" %} common-binary-protections-and-bypasses {% endcontent-ref %}

{% hint style="success" %} Impara e pratica Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks
{% endhint %}