ROP - Return Oriented Programing

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Основна інформація

Return-Oriented Programming (ROP) - це високорівнева техніка експлуатації, яка використовується для обходу заходів безпеки, таких як No-Execute (NX) або Data Execution Prevention (DEP). Замість впровадження та виконання shellcode, атакуючий використовує шматки коду, які вже присутні в бінарному файлі або завантажених бібліотеках, відомі як "гаджети". Кожен гаджет зазвичай закінчується інструкцією ret та виконує невелику операцію, таку як переміщення даних між регістрами або виконання арифметичних операцій. З'єднавши ці гаджети, атакуючий може скласти навантаження для виконання довільних операцій, ефективно обходячи захист NX/DEP.

Конвенції виклику

Розуміння конвенцій виклику є важливим для побудови ефективних ланцюжків ROP, особливо при виклику функцій або маніпулюванні даними:

x86 (32-біт)

  • cdecl: Викликаючий очищає стек. Аргументи функцій виталюються на стек у зворотньому порядку (справа наліво). Аргументи виталюються на стек зправа наліво.

  • stdcall: Схоже на cdecl, але викликається відповідальність за очищення стеку.

x64 (64-біт)

  • Використовує конвенцію виклику System V AMD64 ABI на системах, подібних до Unix, де перші шість цілих або вказівникових аргументів передаються в регістри RDI, RSI, RDX, RCX, R8 та R9. Додаткові аргументи передаються на стеку. Результат повертається в RAX.

  • Конвенція виклику Windows x64 використовує RCX, RDX, R8 та R9 для перших чотирьох цілих або вказівникових аргументів, додаткові аргументи передаються на стеку. Результат повертається в RAX.

  • Регістри: 64-бітні регістри включають RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP та R8 до R15.

З цих конвенцій виклику можна побачити, що в 32 біта аргументи до функцій передаються через стек, тоді як в x64 вони розміщуються в конкретних регістрах.

Як працює ROP

  1. Перехоплення потоку керування: Спочатку атакуючому потрібно перехопити потік керування програми, зазвичай використовуючи переповнення буфера для перезапису збереженої адреси повернення на стеку.

  2. Ланцюжок гаджетів: Потім атакуючий уважно вибирає та з'єднує гаджети для виконання бажаних дій. Це може включати підготовку аргументів для виклику функції, виклик функції (наприклад, system("/bin/sh")), та обробку будь-яких необхідних завершальних або додаткових операцій.

  3. Виконання навантаження: Коли вразлива функція повертається, замість повернення до законного місця вона починає виконувати ланцюжок гаджетів.

Ланцюжок ROP в x86

Припустимо гіпотетичний сценарій, де ми хочемо викликати system("/bin/sh") за допомогою ROP у 32-бітному бінарному файлі:

  1. Пошук гаджетів: Припустимо, що ми знайшли наступні гаджети в бінарному файлі або завантажених бібліотеках:

  • pop eax; ret: Видаляє верхній елемент стеку в EAX та повертається.

  • pop ebx; ret: Видаляє верхній елемент стеку в EAX та повертається.

  • mov [ebx], eax; ret: Переміщує значення з EAX в місце, на яке вказує EBX.

  • Адреса system.

  1. Підготовка ланцюжка: Нам потрібно підготувати стек, який виглядає так:

  • Адреса гаджета, який встановлює EBX.

  • Адреса гаджета pop eax; ret.

  • Адреса рядка "/bin/sh" в пам'яті (або там, де ми плануємо записати його).

  • Адреса гаджета mov [ebx], eax; ret, щоб перемістити "/bin/sh" в місце, на яке вказує EBX.

  • Адреса функції system, з EBX вказуючи наш рядок.

  1. Виконання: Коли вразлива функція повертається, вона починає виконувати наш ланцюжок гаджетів, в кінцевому підсумку викликаючи system("/bin/sh") та відкриваючи оболонку.

ROP в x64

Розглянемо гіпотетичний сценарій, де ви хочете викликати execve("/bin/sh", NULL, NULL) на системі x64 за допомогою конвенції виклику System V AMD64 ABI:

  1. Пошук гаджетів: Спочатку вам потрібно знайти гаджети, які дозволять вам контролювати регістри RDI, RSI та RDX, оскільки вони будуть містити аргументи для execve.

  2. Побудова ланцюжка:

  • Встановити RDI для вказівки на рядок "/bin/sh": Зазвичай це робиться за допомогою гаджета pop RDI; ret, за яким слідує адреса рядка (який може бути розміщений у навантаженні або знайдений в пам'яті).

  • Обнулити RSI та RDX: Оскільки другий і третій аргументи для execve є NULL, вам потрібні гаджети для обнулення цих регістрів, наприклад xor RSI, RSI; ret та xor RDX, RDX; ret.

  • Викликати execve: Нарешті, потрібен гаджет, який переходить до execve (або викликає його опосередковано).

  1. Виконання навантаження: Після побудови та відправлення цього навантаження до вразливої програми, ланцюжок ROP виконується, створюючи оболонку.

Оскільки x64 використовує регістри для перших кількох аргументів, часто він потребує менше гаджетів, ніж x86 для простих викликів функцій, але пошук та з'єднання правильних гаджетів може бути складнішим через збільшену кількість регістрів та більший адресний простір. Збільшена кількість регістрів та більший адресний простір у x64 архітектурі надають як можливості, так і виклики для розробки експлойтів, особливо в контексті Return-Oriented Programming (ROP).

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Last updated