# Heap Overflow {{#include ../../banners/hacktricks-training.md}} ## Basic Information Переповнення купи - це як [**переповнення стеку**](../stack-overflow/index.html), але в купі. В основному це означає, що деякий простір було зарезервовано в купі для зберігання деяких даних, і **збережені дані були більшими за зарезервований простір.** У випадку переповнень стеку ми знаємо, що деякі регістри, такі як вказівник інструкцій або стековий фрейм, будуть відновлені зі стеку, і це може бути можливим для зловживання. У випадку переповнень купи **немає жодної чутливої інформації, що зберігається за замовчуванням** в частині купи, яка може бути переповнена. Однак це можуть бути чутливі дані або вказівники, тому **критичність** цієї вразливості **залежить** від **того, які дані можуть бути перезаписані** і як зловмисник може цим зловживати. > [!TIP] > Щоб знайти зсуви переповнення, ви можете використовувати ті ж шаблони, що й у [**переповненнях стеку**](../stack-overflow/index.html#finding-stack-overflows-offsets). ### Stack Overflows vs Heap Overflows У переповненнях стеку розташування та дані, які будуть присутні в стеку в момент, коли вразливість може бути активована, є досить надійними. Це пов'язано з тим, що стек є лінійним, завжди збільшується в зіткненні пам'яті, у **конкретних місцях виконання програми стекова пам'ять зазвичай зберігає подібні дані** і має певну структуру з деякими вказівниками в кінці частини стеку, що використовується кожною функцією. Однак у випадку переповнення купи використана пам'ять не є лінійною, а **використані частини зазвичай знаходяться в окремих позиціях пам'яті** (не одна біля одної) через **контейнери та зони**, які розділяють алокації за розміром, і через те, що **попередньо звільнена пам'ять використовується** перед алокацією нових частин. Це **ускладнює визначення об'єкта, який буде зіткненням з вразливим** до переповнення купи. Тому, коли виявляється переповнення купи, потрібно знайти **надійний спосіб зробити так, щоб бажаний об'єкт був наступним у пам'яті** після того, що може бути переповнене. Одна з технік, що використовується для цього, - це **Heap Grooming**, яка використовується, наприклад, [**в цьому пості**](https://azeria-labs.com/grooming-the-ios-kernel-heap/). У пості пояснюється, як у ядрі iOS, коли зона вичерпується пам'яттю для зберігання частин пам'яті, вона розширюється на сторінку ядра, і ця сторінка ділиться на частини очікуваних розмірів, які будуть використовуватися в порядку (до версії iOS 9.2, потім ці частини використовуються випадковим чином, щоб ускладнити експлуатацію цих атак). Отже, у попередньому пості, де відбувається переповнення купи, щоб примусити переповнений об'єкт зіткнутися з об'єктом жертви, кілька **`kallocs` примушуються кількома потоками, щоб спробувати забезпечити заповненість усіх вільних частин і створення нової сторінки**. Щоб примусити це заповнення об'єктами певного розміру, **алокація поза лінією, пов'язана з iOS mach port**, є ідеальним кандидатом. Шляхом формування розміру повідомлення можна точно вказати розмір алокації `kalloc`, і коли відповідний mach port знищується, відповідна алокація буде негайно звільнена назад до `kfree`. Тоді деякі з цих заповнювачів можуть бути **звільнені**. **Список вільних `kalloc.4096` звільняє елементи в порядку останній прийшов - перший вийшов**, що в основному означає, що якщо деякі заповнювачі звільнені, і експлуатація намагається алокувати кілька об'єктів жертви, намагаючись алокувати об'єкт, вразливий до переповнення, ймовірно, що цей об'єкт буде слідувати за об'єктом жертви. ### Example libc [**На цій сторінці**](https://guyinatuxedo.github.io/27-edit_free_chunk/heap_consolidation_explanation/index.html) можна знайти базову емуляцію переповнення купи, яка показує, як перезаписуючи біт prev in use наступної частини та позицію prev size, можна **консолідувати використану частину** (зробивши її такою, що вона не використовується) і **потім знову алокувати її**, маючи можливість перезаписати дані, які використовуються в іншому вказівнику. Ще один приклад з [**protostar heap 0**](https://guyinatuxedo.github.io/24-heap_overflow/protostar_heap0/index.html) показує дуже базовий приклад CTF, де **переповнення купи** може бути використано для виклику функції переможця, щоб **отримати прапор**. У прикладі [**protostar heap 1**](https://guyinatuxedo.github.io/24-heap_overflow/protostar_heap1/index.html) можна побачити, як зловживаючи переповненням буфера, можна **перезаписати в сусідній частині адресу**, куди **будуть записані довільні дані від користувача**. ### Example ARM64 На сторінці [https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/](https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/) ви можете знайти приклад переповнення купи, де команда, яка буде виконана, зберігається в наступній частині від переповненої частини. Таким чином, можна змінити виконувану команду, перезаписавши її простим експлойтом, таким як: ```bash python3 -c 'print("/"*0x400+"/bin/ls\x00")' > hax.txt ``` ### Інші приклади - [**Auth-or-out. Hack The Box**](https://7rocky.github.io/en/ctf/htb-challenges/pwn/auth-or-out/) - Ми використовуємо вразливість цілочисельного переповнення, щоб отримати переповнення купи. - Ми корумпуємо вказівники на функцію всередині `struct` переповненого блоку, щоб встановити функцію, таку як `system`, і отримати виконання коду. ### Приклад з реального світу: CVE-2025-40597 – Неправильне використання `__sprintf_chk` У прошивці SonicWall SMA100 версії 10.2.1.15 модуль зворотного проксі `mod_httprp.so` виділяє **0x80-байтовий** блок купи, а потім конкатенує кілька рядків у нього за допомогою `__sprintf_chk`: ```c char *buf = calloc(0x80, 1); /* … */ __sprintf_chk(buf, /* destination (0x80-byte chunk) */ -1, /* <-- size argument !!! */ 0, /* flags */ "%s%s%s%s", /* format */ "/", "https://", path, host); ``` `__sprintf_chk` є частиною **_FORTIFY_SOURCE**. Коли він отримує **позитивний** параметр `size`, він перевіряє, що отриманий рядок поміщається в буфер призначення. Передаючи **`-1` (0xFFFFFFFFFFFFFFFF)**, розробники фактично **відключили перевірку меж**, перетворивши укріплений виклик назад у класичний, небезпечний `sprintf`. Отже, надання надто довгого **`Host:`** заголовка дозволяє зловмиснику **переповнити 0x80-байтовий шматок і знищити метадані наступного шматка купи** (tcache / fast-bin / small-bin в залежності від аллокатора). Збій можна відтворити за допомогою: ```python import requests, warnings warnings.filterwarnings('ignore') requests.get( 'https://TARGET/__api__/', headers={'Host': 'A'*750}, verify=False ) ``` Практична експлуатація вимагатиме **heap grooming** для розміщення контрольованого об'єкта безпосередньо після вразливого блоку, але корінна причина підкреслює два важливі висновки: 1. **_FORTIFY_SOURCE не є панацеєю** – неправильне використання може знищити захист. 2. Завжди передавайте **правильний розмір буфера** до сімейства `_chk` (або, ще краще, використовуйте `snprintf`). ## References * [watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)](https://labs.watchtowr.com/stack-overflows-heap-overflows-and-existential-dread-sonicwall-sma100-cve-2025-40596-cve-2025-40597-and-cve-2025-40598/) {{#include ../../banners/hacktricks-training.md}}