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

6.8 KiB

Stack Overflow

Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Cos'è uno Stack Overflow

Uno stack overflow è una vulnerabilità che si verifica quando un programma scrive più dati nello stack di quanto sia allocato per contenerli. Questi dati in eccesso sovrascriveranno lo spazio di memoria adiacente, portando alla corruzione di dati validi, interruzioni del flusso di controllo e potenzialmente all'esecuzione di codice dannoso. Questo problema spesso si verifica a causa dell'uso di funzioni non sicure che non effettuano il controllo dei limiti sull'input.

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

La vulnerabilità di solito si verifica perché una funzione copia all'interno dello stack più byte di quanti ne siano stati allocati, quindi è in grado di sovrascrivere altre parti dello stack.

Alcune funzioni comuni vulnerabili a questo sono: strcpy, strcat, sprintf, gets... Inoltre, funzioni come fgets, read & memcpy che richiedono 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 overflows

Il modo più comune per trovare gli stack overflows è inserire un input molto grande di A (ad esempio python3 -c 'print("A"*1000)') e aspettarsi un Segmentation Fault che indica che si è cercato di accedere all'indirizzo 0x41414141.

Inoltre, una volta individuata la vulnerabilità dello Stack Overflow, sarà necessario trovare l'offset fino a quando è 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, anziché dover capire manualmente quale offset è necessario per controllare l'EIP, è possibile utilizzare una di queste sequenze come padding e quindi 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 Stack Overflow

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

Tuttavia, in altri scenari forse sovrascrivere alcuni valori delle variabili nello stack potrebbe essere sufficiente per lo sfruttamento (come nei semplici CTF challenges).

Ret2win

In questo tipo di CTF challenges, 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 ritorna, la funzione nascosta verrà chiamata:

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

Shellcode nello Stack

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

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

ROP & Tecniche Ret2...

Questa tecnica è il framework fondamentale per aggirare la principale protezione della tecnica precedente: No executable stack (NX). E permette di eseguire diverse altre tecniche (ret2lib, ret2syscall...) che finiranno per eseguire comandi arbitrari sfruttando istruzioni esistenti nel binario:

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

Heap Overflow

Un overflow non avviene sempre nello stack, potrebbe anche verificarsi nell'heap ad esempio:

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

Tipi di protezioni

Ci sono diverse protezioni che cercano di prevenire lo sfruttamento delle vulnerabilità, controllale in:

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