mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/binary-exploitation/libc-heap/unsorted-bin-attack.md']
This commit is contained in:
parent
816a920f22
commit
c66ac3c3e9
@ -2,54 +2,111 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
## Основна інформація
|
||||
|
||||
Для детальнішої інформації про те, що таке unsorted bin див. цю сторінку:
|
||||
|
||||
Для отримання додаткової інформації про те, що таке unsorted bin, перегляньте цю сторінку:
|
||||
|
||||
{{#ref}}
|
||||
bins-and-memory-allocations.md
|
||||
{{#endref}}
|
||||
|
||||
Unsorted lists можуть записувати адресу в `unsorted_chunks (av)` у адресі `bk` частини. Тому, якщо зловмисник може **модифікувати адресу вказівника `bk`** у частині всередині unsorted bin, він може **записати цю адресу в довільну адресу**, що може бути корисним для витоку адрес Glibc або обходу деяких захистів.
|
||||
Unsorted lists можуть записати адресу в `unsorted_chunks (av)` у полі `bk` чанку. Тому, якщо атакуючий може **змінити адресу вказівника `bk`** у чанку всередині unsorted bin, він зможе **записати цю адресу за довільною адресою**, що може допомогти leak адреси Glibc або обійти деякі захисти.
|
||||
|
||||
Отже, в основному, ця атака дозволяє **встановити велике число за довільною адресою**. Це велике число є адресою, яка може бути адресою купи або адресою Glibc. Типовою мішенню є **`global_max_fast`**, щоб дозволити створювати fast bin bins з більшими розмірами (і перейти від атаки unsorted bin до атаки fast bin).
|
||||
Отже, по суті ця атака дозволяє **встановити велике число за довільною адресою**. Це «велике число» — адреса, яка може бути heap-адресою або адресою Glibc. Традиційною ціллю був **`global_max_fast`**, щоб дозволити створювати fast bin з більшими розмірами (та перейти від unsorted bin attack до fast bin attack).
|
||||
|
||||
- Modern note (glibc ≥ 2.39): `global_max_fast` became an 8‑bit global. Blindly writing a pointer there via an unsorted-bin write will clobber adjacent libc data and will not reliably raise the fastbin limit anymore. Prefer other targets or other primitives when running against glibc 2.39+. See "Modern constraints" below and consider combining with other techniques like a [large bin attack](large-bin-attack.md) or a [fast bin attack](fast-bin-attack.md) once you have a stable primitive.
|
||||
|
||||
> [!TIP]
|
||||
> T> акісуючи приклад, наведений у [https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle) і використовуючи 0x4000 і 0x5000 замість 0x400 і 0x500 як розміри частин (щоб уникнути Tcache), можна побачити, що **сьогодні** помилка **`malloc(): unsorted double linked list corrupted`** викликається.
|
||||
> T> aking a look to the example provided in [https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle) and using 0x4000 and 0x5000 instead of 0x400 and 0x500 as chunk sizes (to avoid Tcache) it's possible to see that **nowadays** the error **`malloc(): unsorted double linked list corrupted`** is triggered.
|
||||
>
|
||||
> Отже, ця атака unsorted bin тепер (серед інших перевірок) також вимагає можливості виправити подвійну зв'язку, щоб це було обійдено `victim->bk->fd == victim` або не `victim->fd == av (arena)`, що означає, що адреса, куди ми хочемо записати, повинна мати адресу фальшивої частини в її позиції `fd`, а фальшива частина `fd` вказує на арену.
|
||||
> Therefore, this unsorted bin attack now (among other checks) also requires to be able to fix the doubled linked list so this is bypassed `victim->bk->fd == victim` or not `victim->fd == av (arena)`, which means that the address where we want to write must have the address of the fake chunk in its `fd` position and that the fake chunk `fd` is pointing to the arena.
|
||||
|
||||
> [!CAUTION]
|
||||
> Зверніть увагу, що ця атака пошкоджує unsorted bin (отже, маленькі та великі також). Тому ми можемо лише **використовувати алокації з fast bin зараз** (більш складна програма може виконувати інші алокації та аварійно завершитися), і щоб викликати це, ми повинні **алокувати той же розмір, інакше програма аварійно завершиться.**
|
||||
> Зверніть увагу, що ця атака корумпує unsorted bin (а отже і small та large). Тому тепер ми можемо **використовувати лише алокації з fast bin** (складніша програма може робити інші алокації і впасти), і щоб тригернути це ми повинні **розподілити пам'ять того самого розміру, інакше програма впаде.**
|
||||
>
|
||||
> Зверніть увагу, що перезапис **`global_max_fast`** може допомогти в цьому випадку, довіряючи, що fast bin зможе впоратися з усіма іншими алокаціями, поки експлуатація не буде завершена.
|
||||
> Зверніть увагу, що перезапис **`global_max_fast`** може допомогти в цьому випадку, довіряючи, що fast bin зможе обслужити всі інші алокації, поки експлойт не буде завершений.
|
||||
|
||||
Код від [**guyinatuxedo**](https://guyinatuxedo.github.io/31-unsortedbin_attack/unsorted_explanation/index.html) дуже добре пояснює це, хоча якщо ви модифікуєте malloc для алокації пам'яті достатнього розміру, щоб не закінчити в Tcache, ви можете побачити, що раніше згадана помилка з'являється, запобігаючи цій техніці: **`malloc(): unsorted double linked list corrupted`**
|
||||
Код від [**guyinatuxedo**](https://guyinatuxedo.github.io/31-unsortedbin_attack/unsorted_explanation/index.html) добре це пояснює, хоча якщо змінити malloc-и так, щоб вони виділяли пам'ять достатню велику щоб не потрапляти в Tcache, можна побачити раніше згадану помилку, яка заважає цій техніці: **`malloc(): unsorted double linked list corrupted`**
|
||||
|
||||
### Як фактично відбувається запис
|
||||
|
||||
- The unsorted-bin write is triggered on `free` when the freed chunk is inserted at the head of the unsorted list.
|
||||
- During insertion, the allocator performs `bck = unsorted_chunks(av); fwd = bck->fd; victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;`
|
||||
- If you can set `victim->bk` to `(mchunkptr)(TARGET - 0x10)` before calling `free(victim)`, the final statement will perform the write: `*(TARGET) = victim`.
|
||||
- Later, when the allocator processes the unsorted bin, integrity checks will verify (among other things) that `bck->fd == victim` and `victim->fd == unsorted_chunks(av)` before unlinking. Because the insertion already wrote `victim` into `bck->fd` (our `TARGET`), these checks can be satisfied if the write succeeded.
|
||||
|
||||
## Сучасні обмеження (glibc ≥ 2.33)
|
||||
|
||||
Щоб надійно використовувати unsorted‑bin writes у сучасних glibc:
|
||||
|
||||
- Tcache interference: для розмірів, які потрапляють у tcache, frees перенаправляються туди і не торкаються unsorted bin. Варіанти:
|
||||
- робити запити з розмірами > MAX_TCACHE_SIZE (≥ 0x410 на 64‑біт за замовчуванням), або
|
||||
- заповнити відповідний tcache bin (7 записів), щоб додаткові frees діставалися до глобальних бінів, або
|
||||
- якщо середовище контрольоване, відключити tcache (наприклад, GLIBC_TUNABLES glibc.malloc.tcache_count=0).
|
||||
- Integrity checks on the unsorted list: на наступному шляху алокації, який перевіряє unsorted bin, glibc перевіряє (спрощено):
|
||||
- `bck->fd == victim` and `victim->fd == unsorted_chunks(av)`; інакше воно завершується з `malloc(): unsorted double linked list corrupted`.
|
||||
- Це означає, що адреса, яку ви цілите, має терпляче переносити два записи: спочатку `*(TARGET) = victim` під час free; пізніше, коли чанк буде видалятися, `*(TARGET) = unsorted_chunks(av)` (аллокатор перезаписує `bck->fd` назад на голову біну). Обирайте цілі, де просте примусове встановлення великого ненульового значення корисне.
|
||||
- Типові стабільні цілі в сучасних експлоїтах
|
||||
- Стан застосунку або глобальний стан, який трактує «великі» значення як прапори/ліміти.
|
||||
- Побічні примітиви (наприклад, підготовка для наступного [fast bin attack]({{#ref}}fast-bin-attack.md{{#endref}}) або для переключення на пізніший write‑what‑where).
|
||||
- Уникайте `__malloc_hook`/`__free_hook` у нових glibc: вони були видалені в 2.34. Уникайте `global_max_fast` на ≥ 2.39 (див. попередню примітку).
|
||||
- Про `global_max_fast` у нещодавніх glibc
|
||||
- У glibc 2.39+ `global_max_fast` став 8‑бітною глобальною змінною. Класичний трюк записати туди heap‑вказівник більше не працює коректно і, ймовірно, корумпуватиме сусідній стан аллокатора. Краще обирати інші стратегії.
|
||||
|
||||
## Minimal exploitation recipe (modern glibc)
|
||||
|
||||
Мета: досягти одного довільного запису heap‑вказівника за довільною адресою, використовуючи примітив вставки в unsorted‑bin, без аварійного завершення.
|
||||
|
||||
- Layout/grooming
|
||||
- Виділіть A, B, C з розмірами, достатніми, щоб обійти tcache (наприклад, 0x5000). C запобігає консолідації з top chunk.
|
||||
- Corruption
|
||||
- Переповнення з A у заголовок B, щоб встановити `B->bk = (mchunkptr)(TARGET - 0x10)`.
|
||||
- Trigger
|
||||
- `free(B)`. Під час вставки аллокатор виконує `bck->fd = B`, отже `*(TARGET) = B`.
|
||||
- Continuation
|
||||
- Якщо ви плануєте продовжувати алокації й програма використовує unsorted bin, очікуйте, що аллокатор пізніше встановить `*(TARGET) = unsorted_chunks(av)`. Обидва значення зазвичай великі і можуть бути достатніми, щоб змінити семантику розмірів/лімітів у цілях, які лише перевіряють «велике».
|
||||
|
||||
Pseudocode skeleton:
|
||||
```c
|
||||
// 64-bit glibc 2.35–2.38 style layout (tcache bypass via large sizes)
|
||||
void *A = malloc(0x5000);
|
||||
void *B = malloc(0x5000);
|
||||
void *C = malloc(0x5000); // guard
|
||||
|
||||
// overflow from A into B’s metadata (prev_size/size/.../bk). You must control B->bk.
|
||||
*(size_t *)((char*)B - 0x8) = (size_t)(TARGET - 0x10); // write fake bk
|
||||
|
||||
free(B); // triggers *(TARGET) = B (unsorted-bin insertion write)
|
||||
```
|
||||
> [!NOTE]
|
||||
> • Якщо ви не можете обійти tcache за допомогою size, заповніть tcache bin для обраного size (7 frees) перед тим як звільнити пошкоджений chunk, щоб free перейшов в unsorted.
|
||||
> • Якщо програма одразу припиняє роботу при наступному виклику allocation через перевірки unsorted-bin, повторно перевірте, що `victim->fd` все ще дорівнює голові біну і що ваш `TARGET` містить точний вказівник на `victim` після першого запису.
|
||||
|
||||
## Unsorted Bin Infoleak Attack
|
||||
|
||||
Це насправді дуже базова концепція. Частини в unsorted bin будуть мати вказівники. Перша частина в unsorted bin насправді буде мати **`fd`** та **`bk`** посилання **вказуючи на частину основної арени (Glibc)**.\
|
||||
Отже, якщо ви можете **помістити частину всередину unsorted bin і прочитати її** (використання після звільнення) або **знову алокувати її, не перезаписуючи принаймні 1 з вказівників**, щоб потім **прочитати** її, ви можете отримати **витік інформації Glibc**.
|
||||
Насправді це дуже базова концепція. Чанки в unsorted bin будуть містити вказівники. Перший chunk в unsorted bin фактично має **`fd`** і **`bk`** посилання, **що вказують на частину main arena (Glibc)**.\
|
||||
Тому, якщо ви можете **помістити chunk в unsorted bin і прочитати його** (use after free) або **виділити його знову без перезапису принаймні одного з вказівників**, щоб потім **прочитати** його, ви можете отримати **Glibc info leak**.
|
||||
|
||||
Схожа [**атака, використана в цьому описі**](https://guyinatuxedo.github.io/33-custom_misc_heap/csaw18_alienVSsamurai/index.html), полягала в зловживанні структурою з 4 частин (A, B, C та D - D лише для запобігання консолідації з верхньою частиною), тому для переповнення нульовим байтом у B було використано, щоб C вказувала, що B не використовується. Також у B дані `prev_size` були модифіковані, тому розмір замість розміру B був A+B.\
|
||||
Потім C була звільнена і консолідована з A+B (але B все ще використовувалася). Була алокована нова частина розміру A, а потім адреси libc були записані в B, звідки вони були витіковані.
|
||||
A similar [**attack used in this writeup**](https://guyinatuxedo.github.io/33-custom_misc_heap/csaw18_alienVSsamurai/index.html), was to abuse a 4 chunks structure (A, B, C and D - D is only to prevent consolidation with top chunk) so a null byte overflow in B was used to make C indicate that B was unused. Also, in B the `prev_size` data was modified so the size instead of being the size of B was A+B.\
|
||||
Then C was deallocated, and consolidated with A+B (but B was still in used). A new chunk of size A was allocated and then the libc leaked addresses was written into B from where they were leaked.
|
||||
|
||||
## References & Other examples
|
||||
|
||||
- [**https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#hitcon-training-lab14-magic-heap**](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#hitcon-training-lab14-magic-heap)
|
||||
- Мета полягає в тому, щоб перезаписати глобальну змінну значенням, більшим за 4869, щоб було можливим отримати прапор, і PIE не увімкнено.
|
||||
- Можна генерувати частини довільних розмірів, і є переповнення купи з бажаним розміром.
|
||||
- Атака починається зі створення 3 частин: chunk0 для зловживання переповненням, chunk1 для переповнення та chunk2, щоб верхня частина не консолідувала попередні.
|
||||
- Потім chunk1 звільняється, а chunk0 переповнюється, щоб вказівник `bk` частини 1 вказував на: `bk = magic - 0x10`
|
||||
- Потім chunk3 алокуються з таким же розміром, як chunk1, що викликає атаку unsorted bin і змінює значення глобальної змінної, що робить можливим отримати прапор.
|
||||
- Мета — перезаписати глобальну змінну значенням більше ніж 4869, щоб отримати flag; PIE не увімкнено.
|
||||
- Можна згенерувати chunks довільних розмірів і існує heap overflow потрібного розміру.
|
||||
- Атака починається зі створення 3 чанків: chunk0 для зловживання overflow, chunk1 — який буде переповнений, і chunk2, щоб top chunk не консолідував попередні.
|
||||
- Потім chunk1 звільняють, і chunk0 переповнюють так, щоб `bk` вказував на: `bk = magic - 0x10`
|
||||
- Потім виділяють chunk3 того ж розміру, що й chunk1, що спрацьовує unsorted bin attack і змінює значення глобальної змінної, дозволяючи отримати flag.
|
||||
- [**https://guyinatuxedo.github.io/31-unsortedbin_attack/0ctf16_zerostorage/index.html**](https://guyinatuxedo.github.io/31-unsortedbin_attack/0ctf16_zerostorage/index.html)
|
||||
- Функція злиття вразлива, оскільки якщо обидва передані індекси однакові, вона перерозподілить їх, а потім звільнить, але повертаючи вказівник на цю звільнену область, яку можна використовувати.
|
||||
- Отже, **створюються 2 частини**: **chunk0**, яка буде злитою сама з собою, і chunk1, щоб запобігти консолідації з верхньою частиною. Потім **функція злиття викликається з chunk0** двічі, що викличе використання після звільнення.
|
||||
- Потім **функція `view`** викликається з індексом 2 (який є індексом частини після звільнення), що **викликає витік адреси libc**.
|
||||
- Оскільки бінарний файл має захисти, щоб лише malloc розміри більші за **`global_max_fast`**, тому жоден fastbin не використовується, буде використана атака unsorted bin для перезапису глобальної змінної `global_max_fast`.
|
||||
- Потім можна викликати функцію редагування з індексом 2 (вказівник після звільнення) і перезаписати вказівник `bk`, щоб вказувати на `p64(global_max_fast-0x10)`. Потім, створюючи нову частину, буде використана раніше скомпрометована адреса (0x20), що **викличе атаку unsorted bin**, перезаписуючи `global_max_fast`, що є дуже великим значенням, що дозволяє тепер створювати частини в fast bins.
|
||||
- Тепер виконується **атака fast bin**:
|
||||
- Перш за все, виявляється, що можливо працювати з fast **частинами розміру 200** в місці **`__free_hook`**:
|
||||
- Функція merge вразлива, бо якщо передані індекси однакові, вона зробить realloc на ньому, потім free, але поверне вказівник на звільнену область, який можна використовувати.
|
||||
- Тому створюються **2 чанки**: **chunk0**, який буде merged з самим собою, і chunk1, щоб запобігти консолідації з top chunk. Потім **merge function викликається для chunk0** двічі, що призводить до use after free.
|
||||
- Потім викликається функція **`view`** з індексом 2 (індекс use after free chunk), яка **leaks a libc address**.
|
||||
- Оскільки бінар має захисти, що дозволяють malloc лише розміри більші за **`global_max_fast`**, тому fastbin не використовується, застосовується unsorted bin attack для перезапису глобальної змінної `global_max_fast`.
|
||||
- Потім можна викликати edit з індексом 2 (вказівник use after free) і перезаписати вказівник `bk`, щоб він вказував на `p64(global_max_fast-0x10)`. Створення нового chunk використає попередньо скомпрометовану адресу free (0x20) і спровокує unsorted bin attack, перезаписавши `global_max_fast` на дуже велике значення, що дозволить тепер створювати chunks у fast bins.
|
||||
- Тепер виконується **fast bin attack**:
|
||||
- Насамперед з'ясовано, що можна працювати з fast **chunks розміру 200** у розташуванні **`__free_hook`**:
|
||||
- <pre class="language-c"><code class="lang-c">gef➤ p &__free_hook
|
||||
$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook>
|
||||
gef➤ x/60gx 0x7ff1e9e607a8 - 0x59
|
||||
@ -58,16 +115,20 @@ gef➤ x/60gx 0x7ff1e9e607a8 - 0x59
|
||||
0x7ff1e9e6076f <list_all_lock+15>: 0x0000000000000000 0x0000000000000000
|
||||
0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000
|
||||
</code></pre>
|
||||
- Якщо нам вдасться отримати fast chunk розміру 0x200 у цьому місці, буде можливим перезаписати вказівник функції, яка буде виконана.
|
||||
- Для цього створюється нова частина розміру `0xfc`, і функція злиття викликається з цим вказівником двічі, таким чином ми отримуємо вказівник на звільнену частину розміру `0xfc*2 = 0x1f8` у fast bin.
|
||||
- Потім функція редагування викликається в цій частині, щоб змінити адресу **`fd`** цього fast bin, щоб вказувати на попередню функцію **`__free_hook`**.
|
||||
- Потім створюється частина розміру `0x1f8`, щоб отримати з fast bin попередню непотрібну частину, тому створюється ще одна частина розміру `0x1f8`, щоб отримати fast bin chunk у **`__free_hook`**, який перезаписується адресою функції **`system`**.
|
||||
- І нарешті, частина, що містить рядок `/bin/sh\x00`, звільняється, викликаючи функцію видалення, що викликає функцію **`__free_hook`**, яка вказує на system з `/bin/sh\x00` як параметром.
|
||||
- Якщо вдасться отримати fast chunk розміру 0x200 у цьому місці, стане можливим перезаписати вказівник на функцію, яка буде виконана.
|
||||
- Для цього створюється новий chunk розміру `0xfc` і merge function викликається з тим вказівником двічі, таким чином ми отримуємо вказівник на звільнений chunk розміру `0xfc*2 = 0x1f8` у fast bin.
|
||||
- Потім у цьому chunk викликається edit, щоб змінити адресу **`fd`** цього fast bin так, щоб вона вказувала на попередню функцію **`__free_hook`**.
|
||||
- Потім створюється chunk розміру `0x1f8`, щоб вибрати з fast bin попередній марний chunk; після цього створюється ще один chunk розміру `0x1f8`, щоб отримати fast bin chunk у **`__free_hook`**, який перезаписується адресою функції **`system`**.
|
||||
- І нарешті chunk, що містить рядок `/bin/sh\x00`, звільняється викликом delete, що спричиняє виклик **`__free_hook`**, яка тепер вказує на system з `/bin/sh\x00` як параметром.
|
||||
- **CTF** [**https://guyinatuxedo.github.io/33-custom_misc_heap/csaw19_traveller/index.html**](https://guyinatuxedo.github.io/33-custom_misc_heap/csaw19_traveller/index.html)
|
||||
- Ще один приклад зловживання переповненням на 1B для консолідації частин в unsorted bin і отримання витоку інформації libc, а потім виконання атаки fast bin для перезапису malloc hook з адресою одного гаджета.
|
||||
- Інший приклад використання 1B overflow для консолідації чанків в unsorted bin і отримання libc infoleak, а потім виконання fast bin attack для перезапису malloc hook на адресу one gadget.
|
||||
- [**Robot Factory. BlackHat MEA CTF 2022**](https://7rocky.github.io/en/ctf/other/blackhat-ctf/robot-factory/)
|
||||
- Ми можемо алокувати лише частини розміру більше `0x100`.
|
||||
- Перезаписати `global_max_fast`, використовуючи атаку Unsorted Bin (працює 1/16 разів через ASLR, оскільки нам потрібно модифікувати 12 біт, але ми повинні модифікувати 16 біт).
|
||||
- Атака Fast Bin для модифікації глобального масиву частин. Це дає примітив довільного читання/запису, що дозволяє модифікувати GOT і вказувати деякі функції на `system`.
|
||||
- Можна виділяти лише чанки розміру більше `0x100`.
|
||||
- Перезаписати `global_max_fast` за допомогою Unsorted Bin attack (працює 1/16 разів через ASLR, бо потрібно змінити 12 бітів, але нам потрібно змінити 16).
|
||||
- Fast Bin attack для модифікації глобального масиву чанків. Це дає примітив довільного читання/запису, що дозволяє змінювати GOT і вказати деяку функцію на `system`.
|
||||
|
||||
## References
|
||||
|
||||
- Glibc malloc unsorted-bin integrity checks (example in 2.33 source): https://elixir.bootlin.com/glibc/glibc-2.33/source/malloc/malloc.c
|
||||
- `global_max_fast` and related definitions in modern glibc (2.39): https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user