Learn & practice AWS Hacking:<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">\
Learn & practice GCP Hacking: <imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">](https://training.hacktricks.xyz/courses/grte)
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
Щоб перевірити, що системні виклики виконуються правильно, потрібно скомпілювати попередню програму, і системні виклики повинні з'явитися в **strace ./PROGRAMA\_COMPILADO**
При створенні shellcode можна використати трюк. Перша інструкція - це перехід до виклику. Виклик звертається до оригінального коду і, крім того, поміщає EIP в стек. Після інструкції виклику ми помістили потрібний рядок, тому з цим EIP ми можемо вказати на рядок і продовжити виконання коду.
Складається з невеликого коду, який проходить через сторінки пам'яті, асоційовані з процесом, у пошуках shellcode, що там зберігається (шукає якусь підпис, розміщену в shellcode). Корисно в тих випадках, коли є лише невеликий простір для ін'єкції коду.
Складаються з зашифрованих shell, які мають невеликий код, що їх розшифровує і переходить до нього, використовуючи трюк Call-Pop, це був би **приклад зашифрованого цезаря**:
Досліджуючи, як будується стек нового процесу в Linux, можна розробити експлойт таким чином, щоб програма запускалася в середовищі, єдиною змінною якого є shellcode. Адресу цієї змінної можна обчислити як: addr = 0xbfffffff - 4 - strlen(ІМ'Я\_виконуваного\_файлу) - strlen(shellcode)
**sprintf moves** форматований рядок **в****змінну.** Тому ви можете зловживати **форматуванням** рядка, щоб викликати **переповнення буфера в змінній**, куди копіюється вміст.\
Наприклад, корисне навантаження `%.44xAAAA`**запише 44B+"AAAA" у змінну**, що може викликати переповнення буфера.
**`atexit()`** — це функція, якій **передаються інші функції як параметри.** Ці **функції** будуть **виконані** під час виконання **`exit()`** або**повернення** з **main**.\
Якщо ви можете **змінити****адресу** будь-якої з цих **функцій**, щоб вказати на shellcode, наприклад, ви **отримаєте контроль** над **процесом**, але це наразі складніше.\
Наразі **адреси функцій**, які потрібно виконати, **сховані** за кількома структурами, і врешті-решт адреси, на які вони вказують, не є адресами функцій, а**зашифровані за допомогою XOR** та зсувів з **випадковим ключем**. Тому наразі цей вектор атаки **не дуже корисний, принаймні на x86** та **x64\_86**.\
**Функція шифрування** — це **`PTR_MANGLE`**. **Інші архітектури**, такі як m68k, mips32, mips64, aarch64, arm, hppa... **не реалізують функцію шифрування**, оскільки вона **повертає те ж саме**, що отримала на вхід. Тому ці архітектури можуть бути атаковані за цим вектором.
Однак, з того, що я читав, інші реєстри не захищені, **тому якщо є `call ebx`, `call esi` або `call edi`** всередині викликаної функції, контроль може бути захоплений. Або ви також можете змінити EBP, щоб змінити ESP.
Кожен об'єкт класу має **VPtr**, який є **вказівником** на масив свого класу. VPtr є частиною заголовка кожного об'єкта, тому, якщо досягнуто **перезапису****VPtr**, його можна **змінити**, щоб **вказувати** на фальшивий метод, так що виконання функції призведе до shellcode.
Перехоплюються виклики до деяких небезпечних функцій іншими безпечними. Не стандартизовано. (тільки для x86, не для компіляцій з -fomit-frame-pointer, не статичних компіляцій, не всі вразливі функції стають безпечними, і LD\_PRELOAD не працює в бінарних файлах з suid).
Складається з завантаження спільних бібліотек з 0x00000000 до 0x00ffffff, щоб завжди був байт 0x00. Однак це насправді не зупиняє майже жодну атаку, і тим більше в little endian.
Складається в тому, щоб виконати ROP так, щоб викликалася функція strcpy@plt (з plt) і вказувалася на вхід GOT, і копіювався перший байт функції, яку потрібно викликати (system()). Потім робиться те ж саме, вказуючи на GOT+1, і копіюється 2-й байт system()… Врешті-решт викликається адреса, збережена в GOT, яка буде system().
У “size” є біти для вказівки: чи використовується попередній шматок, чи був шматок виділений за допомогою mmap() і чи належить шматок до первинної арени.
Якщо при звільненні шматка один з сусідніх виявляється вільним, вони об'єднуються за допомогою макросу unlink() і новий більший шматок передається frontlink() для вставки в відповідний bin.
BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Це викликає запис 4 байтів, починаючи з 8-го байта shellcode, тому перша інструкція shellcode повинна бути jmp, щоб пропустити це і потрапити в nops, які ведуть до решти shellcode.
Після shellcode ми вставляємо заповнювач до досягнення поля 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)**
**payload += prev\_size + fake\_size + got\_free + addr\_sc #Модифікується 2-й шматок, got\_free вказує на те, куди ми будемо зберігати адресу addr\_sc + 12**
Таким чином, програма подумає, що “a” вільний і в bin, тому викликатиме 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 другого шматка, який стрибне до 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 буде в звільненому шматку, на початку потрібно буде вставити jump, який впаде в nops).
Через нулі \_DTOR\_END\_ і небагато адрес у GOT жодна з адрес цих секцій не підходить для переписування, тому давайте подивимося, як застосувати fastbin для атаки на стек.
Крім того, потрібно, щоб сусідній шматок до звільненого був більшим за 8 -> Оскільки ми сказали, що розмір звільненого шматка дорівнює 16, в цьому фальшивому шматку нам потрібно лише вказати розмір більший за 8 (оскільки shellcode буде в звільненому шматку, на початку потрібно буде вставити jump, який впаде в 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(), який потрібно зарезервувати, тому призначає цю адресу.
Спочатку переписується size шматка 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, так що до першого можна було б зробити переповнення після того, як другий був звільнений і введений у свій bin (тобто, був зарезервований malloc, більший за другий шматок перед переповненням).
Мета полягає в тому, що, якщо ми можемо зробити переповнення в купі, яка має під собою вже звільнений шматок і в його bin, ми можемо змінити його вказівник bk. Якщо ми змінимо його вказівник bk, і цей шматок стане першим у списку bin і буде зарезервований, bin буде обмануто, і йому буде сказано, що останній шматок списку (наступний, що пропонується) знаходиться за фальшивою адресою, яку ми вставили (на стек або GOT, наприклад). Тому, якщо буде знову зарезервовано інший шматок, і атакуючий має до нього доступ, йому буде надано шматок у бажаній позиції, і він зможе записати в нього.
Після звільнення зміненого шматка необхідно зарезервувати шматок, більший за звільнений, так що змінений шматок вийде з unsorted bins і потрапить у свій bin.
Як тільки він потрапить у свій bin, настав час змінити його вказівник bk за допомогою переповнення, щоб він вказував на адресу, яку ми хочемо переписати.
Таким чином, bin повинен чекати, поки не буде викликано достатньо разів malloc(), щоб знову використовувати змінений bin і обманути bin, змусивши його повірити, що наступний шматок знаходиться за фальшивою адресою. А потім буде надано шматок, який нас цікавить.
Щоб вразливість спрацювала якомога швидше, ідеально було б: резервування вразливого шматка, резервування шматка, який буде змінено, звільнення цього шматка, резервування шматка, більшего за той, що буде змінено, зміна шматка (вразливість), резервування шматка такого ж розміру, як вразливий, і резервування другого шматка такого ж розміру, і він буде вказувати на вибрану адресу.
Щоб захистити цю атаку, було використано типову перевірку, що шматок “не” є фальшивим: перевіряється, чи bck->fd вказує на victim. Тобто, в нашому випадку, чи вказує вказівник fd\* фальшивого шматка, вказаного в стеку, на victim. Щоб обійти цю перевірку, атакуючий повинен мати можливість якимось чином (напевно, через стек) записати у відповідну адресу адресу victim. Щоб так виглядало, як справжній шматок.
Атака така ж, як і попередня, тобто потрібно змінити вказівник bk, і потрібні всі ці виклики malloc(), але також потрібно змінити size зміненого шматка так, щоб цей size - nb був <MINSIZE.
В основному, це полягає в резервуванні всієї можливої пам'яті для куп і заповненні їх матрацом з nops, закінчених shellcode. Крім того, як матрац використовується 0x0c. Оскільки буде спроба стрибнути до адреси 0x0c0c0c0c, і так, якщо буде переписано якусь адресу, до якої буде викликано, з цим матрацом, буде стрибок туди. В основному, тактика полягає в резервуванні якомога більшої кількості, щоб перевірити, чи буде переписано якийсь вказівник і стрибнути до 0x0c0c0c0c, сподіваючись, що там будуть nops.
Складається в тому, щоб за допомогою резервувань і звільнень засіяти пам'ять так, щоб між вільними шматками залишалися зарезервовані. Буфер для переповнення буде розташований в одному з яєць.
**objdump -d виконуваний файл** —> Дисас функції\
**objdump -d ./PROGRAMA | grep FUNCION** —> Отримати адресу функції\
**objdump -d -Mintel ./shellcodeout** —> Щоб перевірити, що це дійсно наша shellcode і отримати OpCodes\
**objdump -t ./exec | grep varBss** —> Таблиця символів, щоб отримати адреси змінних і функцій\
**objdump -TR ./exec | grep exit(func lib)** —> Щоб отримати адреси функцій бібліотек (GOT)\
**objdump -d ./exec | grep funcCode**\
**objdump -s -j .dtors /exec**\
**objdump -s -j .got ./exec**\
**objdump -t --dynamic-relo ./exec | grep puts** —> Отримує адресу puts для переписування в GOT\
**objdump -D ./exec** —> Дисас ВСЕ до входів plt\
**objdump -p -/exec**\
**Info functions strncmp —>** Інформація про функцію в gdb
Вчіться та практикуйте AWS Hacking:<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">\
Вчіться та практикуйте GCP Hacking: <imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">](https://training.hacktricks.xyz/courses/grte)