<summary><strong>Вивчайте хакінг AWS від нуля до героя з</strong><ahref="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
* Якщо ви хочете побачити **рекламу вашої компанії на HackTricks**або**завантажити HackTricks у форматі PDF**, перевірте [**ПЛАНИ ПІДПИСКИ**](https://github.com/sponsors/carlospolop)!
* Відкрийте для себе [**Сім'ю PEASS**](https://opensea.io/collection/the-peass-family), нашу колекцію ексклюзивних [**NFT**](https://opensea.io/collection/the-peass-family)
* **Приєднуйтесь до** 💬 [**групи Discord**](https://discord.gg/hRep4RUj7f) або [**групи Telegram**](https://t.me/peass) або**слідкуйте** за нами на **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Поділіться своїми хакерськими трюками, надсилайте PR до** [**HackTricks**](https://github.com/carlospolop/hacktricks) **і** [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) **репозиторіїв на GitHub**.
При створенні shellcode можна використовувати хитрість. Перша інструкція - це стрибок до виклику. Виклик викликає оригінальний код і додає EIP до стеку. Після виклику ми додали потрібний рядок, тому з цим EIP ми можемо вказати на рядок і продовжити виконання коду.
Це невеликий код, який переглядає сторінки пам'яті, пов'язані з процесом, у пошуках там збереженого shellcode (шукає якусь підпис, розміщений у shellcode). Корисний у випадках, коли є лише невеликий простір для впровадження коду.
Це зашифровані оболонки, які мають невеликі коди для їх розшифрування та переходу до них, використовуючи трюк Call-Pop, ось **приклад шифрованого шифру Цезаря**:
Таким чином, якщо можна змінити EBP при виході з функції (fvuln), яка була викликана іншою функцією, коли функція, яка викликала fvuln, завершиться, її EIP може бути змінений.
У fvuln можна ввести хибний EBP, який вказує на місце, де знаходиться адреса shellcode + 4 (до неї потрібно додати 4 через pop). Таким чином, при виході з функції, значення &(\&Shellcode)+4 буде поміщено в ESP, з pop від ESP відніметься 4, і він вказуватиме на адресу shellcode під час виконання ret.
Дозволяє змінити лише менш значущий байт EBP. Можна виконати атаку, схожу на попередню, але пам'ять, яка зберігає адресу shellcode, повинна містити тільки перші 3 байти разом з EBP.
ASLR призводить до того, що під час кожного виконання функції завантажуються в різні позиції пам'яті. Тому цей метод може бути неефективним у цьому випадку. Для віддалених серверів, оскільки програма постійно виконується за тією ж адресою, цей метод може бути корисним.
Вказується адреса інструкції system з libc, і їй передається рядок "/bin/sh", зазвичай з змінної середовища. Крім того, використовується адреса функції exit, щоб програма вийшла без проблем після того, як оболонка більше не потрібна (і записує журнали).
“A” \* DISTANCIA EBP + 4 (EBP: можуть бути 4 "A", краще, якщо це реальний EBP, щоб уникнути помилок сегментації) + Адреса **system** (перезапише EIP) + Адреса **exit** (після виклику system(“/bin/sh”) ця функція буде викликана, оскільки перші 4 байти стеку розглядаються як наступна адреса EIP для виконання) + Адреса “**/bin/sh**” (буде параметром, переданим у system)
Можливо, виникне ситуація, коли якийсь байт якоїсь адреси якоїсь функції буде нульовим або пробілом (\x20). У цьому випадку можна розібрати попередні адреси цієї функції, оскільки, ймовірно, там буде кілька NOPs, які дозволять нам викликати один з них замість функції безпосередньо (наприклад, з > x/8i system-4).
Цей метод працює, оскільки, викликаючи функцію, таку як system, за допомогою опкоду **ret** замість **call**, функція розуміє, що перші 4 байти будуть адресою **EIP**, на яку повернутися.
Цікавою технікою з цим методом є виклик **strncpy()** для переміщення навантаження зі стеку до купи і подальше використання **gets()** для виконання цього навантаження.
Ще однією цікавою технікою є використання **mprotect()**, яка дозволяє призначити бажані дозволи для будь-якої частини пам'яті. Вона працює або працювала в BDS, MacOS та OpenBSD, але не в linux (контролює, що не можна одночасно надавати дозволи на запис та виконання). З цим атака може повернути стек як виконуваний.
Таким чином можна ланцюгати функції, які потрібно викликати. Крім того, якщо потрібно використовувати функції з декількома аргументами, можна вказати необхідні аргументи (наприклад, 4) і вказати 4 аргументи та знайти адресу місця з опкодами: pop, pop, pop, pop, ret —> **objdump -d виконуваний_файл**
Корисно, коли не можна вставити адресу стеку в EIP (перевіряється, що EIP не містить 0xbf) або коли не можна обчислити розташування shellcode. Проте вразлива функція приймає параметр (сюди буде вставлена shellcode).
Таким чином, змінивши EIP на адресу **ret**, завантажиться наступна адреса (яка є адресою першого аргумента функції). Іншими словами, завантажиться shellcode.
Exploit виглядатиме так: SHELLCODE + Заповнювач (до EIP) + **\&ret** (наступні байти стеку вказують на початок shellcode, оскільки в стек вставляється адреса переданого параметра)
Здається, що функції, такі як **strncpy**, після завершення видаляють зі стеку адресу, де була збережена shellcode, ускладнюючи цю техніку. Іншими словами, адреса, яку передають функції як аргумент (яка зберігає shellcode), змінюється на 0x00, тому коли викликається другий **ret**, він зустрічає 0x00 і програма припиняє роботу.
Якщо у нас немає контролю над першим аргументом, але є контроль над другим або третім, ми можемо перезаписати EIP адресою pop-ret або pop-pop-ret, в залежності від потреби.
Розглядаючи, як формується стек нового процесу в Linux, можна розробити експлоіт так, щоб програма запускалася в середовищі, де є лише змінна з кодом оболонки. Адресу цієї змінної можна обчислити як: addr = 0xbfffffff - 4 - strlen(NOMBRE\_ejecutable\_completo) - strlen(shellcode)
Оскільки ESP завжди вказує на початок стеку, ця техніка полягає в заміні EIP адресою виклику **jmp esp**або**call esp**. Таким чином, після перезапису EIP код оболонки зберігається, оскільки після виконання **ret** ESP буде вказувати на наступну адресу, де зберігається код оболонки.
У випадку, якщо ASLR не активовано в Windows або Linux, можна викликати **jmp esp**або**call esp**, збережені в якомусь спільному об'єкті. Якщо ASLR активовано, можна шукати в межах самої вразливої програми.
Крім того, можливість розміщення коду оболонки після порушення EIP замість в середині стеку дозволяє уникнути того, що інструкції push або pop, які виконуються в середині функції, доторкнуться коду оболонки (що може статися, якщо вона розміщується в середині стеку функції).
Цей тип переповнень виникає, коли змінна не готова обробляти таке велике число, якому її передають, можливо через плутанину між змінними зі знаком і без, наприклад:
Якщо ми передамо в якості першого параметра від'ємне число, вийде, що len <256,імипройдемоцейфільтр,атакожstrlen(buffer)будеменше,ніжl,оскількиlєбеззнаковимintібудедужевеликим.
Не відомо, яке значення може прийняти невизначена змінна, і це може бути цікаво вивчити. Можливо, вона прийме значення змінної з попередньої функції, яка може бути керована зловмисником.
У мові C **`printf`** - це функція, яку можна використовувати для **виведення** деякого рядка. **Перший параметр**, який очікує ця функція, - це **сирій текст з форматерами**. **Наступні параметри** - це **значення**, які **замінять****форматери**у сирому тексті.
Вразливість виникає, коли **текст зловмисника використовується як перший аргумент** цій функції. Зловмисник зможе створити **спеціальний ввід, зловживаючи** можливостями **рядка форматування printf** для **запису будь-яких даних за будь-якою адресою**. Таким чином, він може **виконати довільний код**.
**`%n`** **записує** кількість **записаних байтів** за **вказаною адресою. Записуючи** стільки **байтів**, скільки **необхідно** в шістнадцятковому форматі, можна **записати будь-які дані**.
У бінарному файлі GOT містить **адреси функцій або** секцію **PLT**, яка завантажить адресу функції. Метою цього експлойту є **перезапис GOT-запису** функції, яка буде виконана пізніше **з** адресою PLT функції **`system`**. Ідеально, ви **перезапишете** GOT **функції**, яка **буде викликана з параметрами, які контролюються вами** (таким чином, ви зможете контролювати параметри, які надсилаються функції системи).
Якщо **`system`** **не використовується** сценарієм, функція системи **не** матиме запису в GOT. У цьому сценарії вам **доведеться витікати адресу** функції `system` спочатку.
**Procedure Linkage Table** - це **таблиця лише для читання** в файлі ELF, яка зберігає всі необхідні **символи, які потребують розв'язання**. Коли викликаються одна з цих функцій, **GOT** буде **перенаправляти****потік** до **PLT**, щоб він міг **розв'язати****адресу** функції та записати її в GOT.\
Потім, **наступного разу**, коли виконується виклик на ту адресу, **функція** викликається без необхідності розв'язувати її.
Як пояснено раніше, метою буде **перезаписати****адресу** функції в таблиці **GOT**, яка буде викликана пізніше. Ідеальною було б встановити **адресу шел-коду**, розташовану в виконувальному розділі, але ймовірно ви не зможете написати шел-код в виконувальному розділі.\
Тому інша опція - **перезаписати функцію**, яка **отримує** свої **аргументи** від **користувача** та **спрямувати** її на функцію **`system`**.
Фактично це структура з **функціями, які будуть викликані** перед завершенням програми. Це цікаво, якщо ви можете викликати свій **шел-код просто перейшовши за адресою**, або в тих випадках, коли вам потрібно повернутися до головної знову, щоб **експлуатувати форматний рядок вдруге**.
Зверніть увагу, що це **не** створить **вічний цикл**, оскільки коли ви повернетеся до головної функції, канарейка помітить, що кінець стеку може бути пошкоджений, і функція більше не буде викликана знову. Таким чином, ви зможете **виконати ще 1 раз** уразливість.
Форматований рядок також може бути використаний для **виведення вмісту** з пам'яті програми.\
Наприклад, у наступній ситуації є **локальна змінна в стеці, що вказує на прапорець**. Якщо ви **знайдете**, де в **пам'яті** знаходиться **вказівник** на **прапорець**, ви можете зробити **printf доступ** до цієї **адреси** та **вивести** прапорець:
Зверніть увагу, що, слідуючи за **попереднім використанням** та реалізацією можливості **витоку вмісту**, ви можете **встановити вказівники** на **`printf`** до розділу, де завантажується **виконуваний файл**, та **вивести** його **повністю**!
Зазвичай ви знайдете розділ **DTOR****між** значеннями `ffffffff` та `00000000`. Так що, якщо ви бачите лише ці значення, це означає, що **жодна функція не зареєстрована**. Тому **перезапишіть****`00000000`** адресою **shellcode**, щоб виконати його.
**`sprintf` переміщує** форматований рядок **у****змінну.** Тому ви можете зловживати **форматуванням рядка**, щоб викликати **переповнення буфера в змінній**, куди копіюється вміст.\
Наприклад, навантаження `%.44xAAAA`**запише 44 байти + "AAAA" у змінну**, що може викликати переповнення буфера.
**`atexit()`** - це функція, до якої **передаються інші функції в якості параметрів.** Ці **функції** будуть **виконані** при виконанні **`exit()`** або**поверненні** до **головної** функції.\
Якщо ви можете **змінити адресу** будь-якої з цих **функцій**, щоб вказувати на shellcode, наприклад, ви **отримаєте контроль** над **процесом**, але це зараз складніше.\
Наразі **адреси функцій**, які мають бути виконані, **приховані** за кількома структурами, і, нарешті, адреси, на які вони вказують, не є адресами функцій, а**шифруються за допомогою XOR** та зсувів з **випадковим ключем**. Тому наразі цей вектор атаки **не дуже корисний принаймні на x86** та **x64\_86**.\
Функція **шифрування** - **`PTR_MANGLE`**. **Інші архітектури**, такі як m68k, mips32, mips64, aarch64, arm, hppa... **не реалізують функцію шифрування**, оскільки вона **повертає те ж**, що отримала на вході. Тому ці архітектури можуть бути піддані атакам через цей вектор.
**`Setjmp()`** дозволяє **зберегти****контекст** (регістри)\
**`longjmp()`** дозволяє **відновити****контекст**.\
Збережені регістри: `EBX, ESI, EDI, ESP, EIP, EBP`\
Справа в тому, що EIP та ESP передаються функцією **`PTR_MANGLE`**, тому **архітектура, яка вразлива на цю атаку, така ж, як вище**.\
Вони корисні для відновлення помилок або переривань.\
Однак, з того, що я прочитав, інші регістри не захищені, **тому якщо є `call ebx`, `call esi` або `call edi`** всередині функції, яка викликається, можна захопити контроль. Або ви також можете змінити EBP для зміни ESP.
Кожен об'єкт **класу** має **VPtr**, який є **вказівником** на масив свого класу. VPtr є частиною заголовка кожного об'єкта, тому якщо досягнуто **переписування** VPtr, його можна **змінити**, щоб вказувати на фіктивний метод, так що виконання функції перейде до shellcode.
Це техніка перетворення переповнення буфера на помилку формату рядка. Полягає в тому, що замінюємо EIP, щоб він вказував на printf функцію та передаємо їй як аргумент змінений рядок формату, щоб отримати значення про стан процесу.
Бібліотеки знаходяться в позиції з 16-бітною випадковістю = 65636 можливих адрес. Якщо вразливий сервер викликає fork(), простір адрес пам'яті копіюється в дочірній процес і залишається незмінним. Тому можна спробувати зробити brute force до функції usleep() з libc, передаючи "16" як аргумент, щоб знайти цю функцію, коли відповідь затримується довше звичайного. Знаючи, де знаходиться ця функція, можна отримати delta\_mmap та розрахувати інші.
**Relro (Read only Relocation)** впливає на дозволи пам'яті подібно до NX. Відмінність полягає в тому, що, якщо з NX стек стає виконуваним, то RELRO робить **певні речі тільки для читання**, тому ми **не можемо писати** в них. Найпоширенішим перешкодою, яку я бачив, є запобігання **перезапису таблиці got**, про що буде розповідатися пізніше. Таблица got містить адреси функцій libc, щоб виконуваний файл знав ці адреси та міг їх викликати. Подивимося, які дозволи пам'яті має запис таблиці got для виконуваного файлу з та без relro.
Для бінарного **без relro** ми бачимо, що адреса запису `got` для `fgets` - `0x404018`. Придивившись до відображення пам'яті, ми бачимо, що вона потрапляє між `0x404000` та `0x405000`, що має **дозволи `rw`**, що означає, що ми можемо читати та писати в неї. Для бінарного **з relro** ми бачимо, що адреса таблиці `got` для запуску бінарного файлу (pie увімкнено, тому ця адреса зміниться) - `0x555555557fd0`. У відображенні пам'яті цього бінарного файлу вона потрапляє між `0x0000555555557000` та `0x0000555555558000`, що має дозвіл на пам'ять **`r`**, що означає, що ми можемо лише читати з неї.
Так як обійти це? Типовий обхід, який я використовую, - просто не писати в області пам'яті, які relro змушує робити доступними тільки для читання, та **знайти інший спосіб виконання коду**.
* Ліниве зв'язування: Адреса функції шукається перший раз, коли функцію викликають. Таким чином, під час виконання потрібно мати дозвіл на запис у`GOT`.
* Прив'язка зараз: Адреси функцій вирішуються на початку виконання, після чого чутливим розділам, таким як .got, .dtors, .ctors, .dynamic, .jcr, надаються дозволи тільки для читання. `` `** ``-z relro`**`y`**`-z now\`\*\*
Коли бінарний файл завантажується в пам'ять і функція викликається вперше, відбувається перехід до PLT (Procedure Linkage Table), звідки відбувається стрибок (jmp) до GOT і виявляється, що цей запис не вирішено (містить адресу наступного PLT). Тоді викликається Runtime Linker або rtfd для вирішення адреси і збереження її в GOT.
При виклику функції викликається PLT, яка містить адресу GOT, де зберігається адреса функції, тому вона перенаправляє потік туди, і функція викликається. Однак, якщо це перший виклик функції, в GOT буде наступна інструкція PLT, тому потік слідує коду PLT (rtfd), визначає адресу функції, зберігає її в GOT і викликає.
Ліниве зв'язування —> Адреса функції шукається при першому виклику цієї функції, тому GOT має дозвіл на запис, щоб при пошуку зберегти її там і не потрібно буде шукати знову.
Прив'язка зараз —> Адреси функцій шукаються при завантаженні програми, і дозволи для розділів .got, .dtors, .ctors, .dynamic, .jcr змінюються на тільки для читання. **-z relro** і**-z now**
Друга не дозволяє, щоб **%n** був з розділу з дозволами на запис. Крім того, параметр для прямого доступу до аргументів може бути використаний лише при використанні попередніх, тобто можна використовувати **%3$d** лише після використання **%2$d** і**%1$d**.
Для відображення повідомлення про помилку використовується argv\[0\], тому якщо встановити туди адресу іншого місця (наприклад, глобальної змінної), повідомлення про помилку покаже вміст цієї змінної. Стор. 191
Деякі небезпечні виклики функцій замінюються на безпечні. Не є стандартизованим. (тільки для x86, не для компіляцій з -fomit-frame-pointer, не для статичних компіляцій, не всі небезпечні функції стають безпечними і LD\_PRELOAD не працює для бінарних файлів з suid).
Полягає в завантаженні спільних бібліотек з 0x00000000 по 0x00ffffff, щоб завжди був байт 0x00. Однак це насправді мало захищає від будь-яких атак, особливо в little endian.
Полягає в тому, що виконується ROP так, що викликається функція strcpy@plt (з plt), вказується на запис в GOT і копіюється перший байт функції, яку потрібно викликати (system()). Потім це ж саме робиться, вказуючи на GOT+1 і копіюючи 2-й байт system()... У кінці викликається збережена адреса в GOT, яка буде system().
Для функцій, які використовують EBP як реєстр для вказівки на аргументи, при зміні EIP і вказівки на system() також потрібно змінити EBP, щоб вказував на область пам'яті з будь-якими 2 байтами, а потім на адресу &”/bin/sh”.
У "size" є біти для позначення: чи попередній шматок використовується, чи шматок був виділений за допомогою mmap() і чи шматок належить до основного арени.
Якщо при звільненні шматка який-небудь з сусідніх є вільним, вони об'єднуються за допомогою макроса unlink() і новий найбільший шматок передається frontlink() для вставки відповідного bin.
BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Це призводить до запису 4 байтів починаючи з 8-го байта shellcode, тому перша інструкція shellcode повинна бути jmp, щоб пропустити це і перейти до nops, які ведуть до решти shellcode.
Після shell code вставляємо заповнення до досягнення поля prev\_size та size наступного шматка. В цих місцях ми вставляємо 0xfffffff0 (щоб перезаписати prev\_size, щоб вказати, що він вільний) і “-4“(0xfffffffc) в size (щоб при перевірці в 3-му шматку, чи 2-й був вільний, насправді перейти до зміненого prev\_size, який скаже, що він вільний) -> Таким чином, коли free() досліджує, він перейде до size 3-го, а насправді перейде до 2-го - 4 і вважатиме, що 2-й шматок вільний. І тоді викличе **unlink()**.
При виклику unlink() використовується як P->fd перші дані 2-го шматка, тому туди вставляється адреса, яку потрібно перезаписати - 12 (оскільки в FD->bk він додасть 12 до адреси, збереженої в FD). І в цю адресу вставляється друга адреса з 2-го шматка, яка повинна бути адресою shellcode (фальшивий P->bk).
**fake\_size = pack("\<I”, 0xfffffffc) #-4, щоб вважати, що "size" 3-го шматка знаходиться на 4 байти позаду (вказує на prev\_size), оскільки саме там перевіряється, чи вільний 2-й шматок**
**got\_free = pack("\<I", 0x08048300 - 12) #Адреса free() в plt-12 (це адреса, яка буде перезаписана, щоб запустити shellcode при другому виклику free)**
Отже, програма вважатиме, що "a" вільний і знаходиться в біні, тому вона викличе unlink() для його роз'єднання. Однак, оскільки розмір PREV\_SIZE дорівнює -4, вона вважатиме, що шматок "a" починається на b+4. Іншими словами, вона викличе unlink() для шматка, який починається на b+4, тому в b+12 буде вказівник "fd", а в b+16 буде вказівник "bk".
Frontlink викликається, коли щось вивільняється, і жоден з його сусідніх шматків не є вільним, unlink() не викликається, але безпосередньо викликається frontlink().
Таким чином, здійснюючи перезапис в двох mallocs без контролю і в одному контрольованому, який вивільняється лише один раз, ми можемо створити експлойт.
Якщо потрібно знову використовувати один, його можна призначити без проблем. Якщо потрібно використовувати інший, йому буде призначено той самий простір, тому ми матимемо фальшиві вказівники "fd" і "bk" з даними, які записує попереднє виділення.
Для виклику коду за власним вибором потрібен лише один виклик free(). Цікаво знайти другий шматок, який може бути переповнений попереднім і вивільнений.
У \[1] перевіряється поле size біт NON\_MAIN\_ARENA, яке можна змінити, щоб перевірка повертала true і виконувалася heap\_for\_ptr(), яка виконує and до "mem", змінюючи на 0 2,5 менш важливих байтів (у нашому випадку з 0x0804a000 виходить 0x08000000) і отримує доступ до 0x08000000->ar\_ptr (як до структури heap\_info)
Отже, якщо ми можемо контролювати шматок, наприклад, у 0x0804a000, і шматок у**0x081002a0** буде вивільнений, ми можемо дістатися до адреси 0x08100000 і записати туди все, що завгодно, наприклад **0x0804a000**. Коли цей другий шматок буде вивільнений, він побачить, що heap\_for\_ptr(ptr)->ar\_ptr повертає те, що ми записали в 0x08100000 (оскільки до 0x081002a0 застосовується and, який ми бачили раніше, і звідти витягується значення перших 4 байтів, ar\_ptr)
В адресі, куди падає адреса другого шматка з останніми 5 нулями, потрібно записати адресу цього першого шматка, щоб heap\_for\_ptr() думав, що ar\_ptr знаходиться на початку першого шматка і витягував звідти av->bins\[2\]
Отже, буде викликано \_int\_free(TROZO1, TROZO2), і виконається інструкція для запису в \_\_DTOR\_END\_\_ адреси prev\_size TROZO2, який стрибне до shellcode.
Для застосування цієї техніки потрібно виконати деякі додаткові вимоги, які ускладнюють пейлоуд.
Ця техніка вже не застосовується, оскільки було застосовано майже той самий патч, що і для unlink. Порівнюють, чи нове місце, на яке вказується, також вказує на нього.
Таким чином, якщо в "fb" вказується адреса функції в GOT, на цю адресу буде встановлено адресу перезаписаного шматка. Для цього потрібно, щоб арена була близькою до адрес dtors. Конкретніше, av->max\_fast повинен бути в адресі, яку ми збираємося перезаписати.
Тому, якщо в поле size ми встановимо розмір 8 + NON\_MAIN\_ARENA + PREV\_INUSE —> fastbin\_index() поверне fastbins\[-1\], який вказуватиме на av->max\_fast
Крім того, потрібно, щоб шматок поруч з вивільненим був більшим за 8 -> Оскільки ми сказали, що розмір вивільненого шматка - 8, у цьому фальшивому шматку потрібно лише встановити розмір більше 8 (оскільки shellcode також буде в шматку, який вивільнюється, потрібно встановити спочатку jmp, який призведе до nops).
Через нулі з \_DTOR\_END\_ та обмежену кількість адрес в GOT, жодна з цих секцій не підходить для перезапису, тому давайте подивимося, як застосувати fastbin для атаки на стек.
Крім того, потрібно, щоб шматок поруч з вивільненим був більшим за 8 -> Оскільки ми сказали, що розмір вивільненого шматка - 16, у цьому фальшивому шматку потрібно лише встановити розмір більше 8 (оскільки shellcode також буде в шматку, який вивільнюється, потрібно встановити спочатку jmp, який призведе до nops, які йдуть після поля size нового фальшивого шматка).
У цьому випадку ми шукаємо вказівник на malloc, який може бути змінений зловмисником (наприклад, вказівник знаходиться на стеці під можливим переповненням змінної).
Таким чином, ми могли б зробити цей вказівник вказувати куди завгодно. Однак не будь-яке місце підходить, розмір фальшивого шматка повинен бути меншим за av->max\_fast і, більш конкретно, дорівнювати розміру, запитаному у майбутньому виклику malloc()+8. Тому, якщо ми знаємо, що після цього вразливого вказівника викликається malloc(40), розмір фальшивого шматка повинен дорівнювати 48.
Наприклад, якщо програма запитує користувача про число, ми можемо ввести 48 і вказати вказівник malloc на наступні 4 байти (які можуть належати EBP, з удачею, таким чином, 48 залишається позаду, якщо це буде заголовок size). Крім того, адреса ptr-4+48 повинна відповідати декільком умовам (у цьому випадку ptr=EBP), тобто 8 <ptr-4+48<av->system\_mem.
Якщо це виконується, коли викликається наступний malloc, який ми сказали, що це malloc(40), йому буде призначено адресу EBP. Якщо зловмисник також може контролювати те, що записується в цей malloc, він може перезаписати як EBP, так і EIP будь-якою адресою, яку він хоче.
Це, мабуть, тому, що коли free() вивільняє, він зберігає, що в адресі, яка вказує на EBP стеку, є шматок ідеального розміру для нового malloc(), який потрібно резервувати, тому він призначає цю адресу.
Спочатку розмір шматка wilderness перезаписується значенням дуже великим (0xffffffff), тому будь-яке достатньо велике прохання про пам'ять буде оброблено в \_int\_malloc() без необхідності розширення купи
Victim отримує значення адреси поточного шматка wilderness (поточний av->top), а remainder - це саме сума цієї адреси плюс кількість байтів, запитаних malloc(). Тому, якщо \&EIP-8 знаходиться за адресою 0xbffff224, а av->top містить 0x080c2788, тоді кількість, яку ми повинні зарезервувати в контрольованому malloc, щоб av->top вказував на $EIP-8 для наступного malloc(), буде:
Важливо знати, що розмір нового шматка wilderness повинен бути більшим за запит, зроблений останнім malloc(). Іншими словами, якщо wilderness вказує на \&EIP-8, розмір буде точно в полі EBP стеку.
Вивільнені шматки вводяться в bin в залежності від їх розміру. Але перед тим, як їх вводити, вони зберігаються в unsorted bins. Шматок, який вивільнюється, не вводиться безпосередньо в свій bin, а залишається в unsorted bins. Потім, якщо резервується новий шматок і попередній вивільнений може йому підійти, він повертається, але якщо резервується більший, вивільнений шматок в unsorted bins вводиться в відповідний bin.
Резервування двох malloc, так щоб перший міг переповнитися після того, як другий був звільнений і включений у свій бін (тобто, було зарезервовано malloc, який більше за другий шматок перед переповненням)
Мета полягає в тому, що якщо ми можемо переповнити купу, яка має вже звільнений шматок у своєму біні, ми можемо змінити його вказівник bk. Якщо ми змінимо вказівник bk і цей шматок стане першим у списку бінів і буде зарезервований, бін буде обманутий і вважатиме, що наступний шматок у списку (наступний для надання) знаходиться за фальшивою адресою, яку ми вказали (наприклад, на стеку або GOT). Таким чином, якщо знову зарезервувати інший шматок і зловмисник матиме дозвіл на нього, йому буде надано шматок у бажаному положенні, і він зможе записати в нього.
Після звільнення зміненого шматка необхідно зарезервувати шматок, який більший за звільнений, тоді змінений шматок вийде з несортованих бінів і буде включений у свій бін.
Таким чином, бін повинен зачекати, поки malloc() буде викликано достатню кількість разів, щоб знову використати змінений бін і обманути бін, змушуючи його вважати, що наступний шматок знаходиться за фальшивою адресою. Потім буде надано шматок, який нас цікавить.
Щоб вразити вразливість якнайшвидше, ідеально: резервування вразливого шматка, резервування шматка, який буде змінено, звільнення цього шматка, резервування шматка, який більший за той, який буде змінено, зміна шматка (вразливість), резервування шматка такого ж розміру, як порушений, і резервування другого шматка такого ж розміру, який буде вказувати на обрану адресу.
Для захисту від цього нападу використовується типова перевірка того, що шматок "не" є фальшивим: перевіряється, чи вказує bck->fd на жертву. Іншими словами, у нашому випадку, якщо вказівник fd\* фальшивого шматка, вказаний на стеку, вказує на жертву. Щоб обійти цей захист, зловмисник повинен змогти якимось чином (найімовірніше, через стек) записати в потрібну адресу адресу жертви. Це зробить шматок справжнім.
Атака схожа на попередню, тобто потрібно змінити вказівник bk і викликати всі ці виклики malloc(), але також потрібно змінити розмір зміненого шматка так, щоб цей розмір - nb був <MINSIZE.
Основна ідея полягає в тому, щоб зарезервувати якомога більше пам'яті для куп та заповнити їх матрацем з nops, завершених shellcode. Крім того, як матрац використовується 0x0c. Таким чином, спробуємо перейти за адресу 0x0c0c0c0c, і якщо будь-яка адреса, на яку буде викликано з цим матрацем, буде перезаписана, ми перейдемо туди. Основна тактика полягає в тому, щоб зарезервувати якнайбільше, щоб побачити, чи буде перезаписано якийсь вказівник, і перейти до 0x0c0c0c0c, сподіваючись, що там будуть nops.
Полягає в тому, що за допомогою резервувань та звільнень сементується пам'ять так, що між вільними шматками залишаються зарезервовані шматки. Буфер для переповнення буде розташований в одному з цих шматків.
<summary><strong>Вивчайте хакінг AWS від нуля до героя з</strong><ahref="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
* Якщо ви хочете побачити вашу **компанію рекламовану в HackTricks**або**завантажити HackTricks у PDF**, перевірте [**ПЛАНИ ПІДПИСКИ**](https://github.com/sponsors/carlospolop)!