BROP - Blind Return Oriented Programming

Support HackTricks

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

Мета цієї атаки полягає в тому, щоб зловживати ROP через переповнення буфера без будь-якої інформації про вразливий бінарний файл. Ця атака базується на наступному сценарії:

  • Вразливість стека та знання про те, як її викликати.

  • Серверний додаток, який перезапускається після збою.

Атака

1. Знайти вразливий офсет відправляючи ще один символ, поки не буде виявлено збій сервера

2. Брутфорс канарейку для її витоку

3. Брутфорс збережених адрес RBP та RIP у стеку для їх витоку

Ви можете знайти більше інформації про ці процеси тут (BF Forked & Threaded Stack Canaries) та тут (BF Addresses in the Stack).

4. Знайти стоп-гаджет

Цей гаджет в основному дозволяє підтвердити, що щось цікаве було виконано гаджетом ROP, оскільки виконання не призвело до збою. Зазвичай цей гаджет буде чимось, що зупиняє виконання і розташоване в кінці ланцюга ROP, коли шукають гаджети ROP, щоб підтвердити, що конкретний гаджет ROP був виконаний.

5. Знайти гаджет BROP

Ця техніка використовує гаджет ret2csu. І це тому, що якщо ви отримуєте доступ до цього гаджета посеред деяких інструкцій, ви отримуєте гаджети для контролю rsi та rdi:

Це будуть гаджети:

  • pop rsi; pop r15; ret

  • pop rdi; ret

Зверніть увагу, як з цими гаджетами можна контролювати 2 аргументи функції для виклику.

Також зверніть увагу, що гаджет ret2csu має дуже унікальний підпис, оскільки він буде витягувати 6 регістрів зі стеку. Тому відправляючи ланцюг, як:

'A' * offset + canary + rbp + ADDR + 0xdead * 6 + STOP

Якщо STOP виконується, це в основному означає, що адреса, яка витягує 6 регістрів зі стеку, була використана. Або що використана адреса також була адресою STOP.

Щоб усунути цю останню опцію, виконується новий ланцюг, як наступний, і він не повинен виконувати гаджет STOP, щоб підтвердити, що попередній дійсно витягнув 6 регістрів:

'A' * offset + canary + rbp + ADDR

Знаючи адресу гаджета ret2csu, можна вивести адресу гаджетів для контролю rsi та rdi.

6. Знайти PLT

Таблицю PLT можна шукати з 0x400000 або з витягнутої адреси RIP зі стеку (якщо PIE використовується). Записи таблиці відокремлені на 16B (0x10B), і коли викликається одна функція, сервер не зривається, навіть якщо аргументи неправильні. Також перевірка адреси запису в PLT + 6B також не призводить до збою, оскільки це перший код, що виконується.

Отже, можна знайти таблицю PLT, перевіряючи наступні поведінки:

  • 'A' * offset + canary + rbp + ADDR + STOP -> немає збою

  • 'A' * offset + canary + rbp + (ADDR + 0x6) + STOP -> немає збою

  • 'A' * offset + canary + rbp + (ADDR + 0x10) + STOP -> немає збою

7. Знайти strcmp

Функція strcmp встановлює регістр rdx на довжину рядка, що порівнюється. Зверніть увагу, що rdx є третім аргументом, і нам потрібно, щоб він був більшим за 0, щоб пізніше використовувати write для витоку програми.

Можна знайти місце розташування strcmp в PLT на основі його поведінки, використовуючи той факт, що ми тепер можемо контролювати 2 перші аргументи функцій:

  • strcmp(<не читати addr>, <не читати addr>) -> збій

  • strcmp(<не читати addr>, <читати addr>) -> збій

  • strcmp(<читати addr>, <не читати addr>) -> збій

  • strcmp(<читати addr>, <читати addr>) -> немає збою

Це можна перевірити, викликавши кожен запис таблиці PLT або використовуючи PLT повільний шлях, який в основному полягає в виклику запису в таблиці PLT + 0xb (який викликає dlresolve) з наступним у стеку номер запису, який потрібно перевірити (починаючи з нуля), щоб просканувати всі записи PLT з першого:

  • strcmp(<не читати addr>, <читати addr>) -> збій

  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP -> Виникне збій

  • strcmp(<читати addr>, <не читати addr>) -> збій

  • b'A' * offset + canary + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

  • strcmp(<читати addr>, <читати addr>) -> немає збою

  • b'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

Пам'ятайте, що:

  • BROP + 0x7 вказує на pop RSI; pop R15; ret;

  • BROP + 0x9 вказує на pop RDI; ret;

  • PLT + 0xb вказує на виклик dl_resolve.

Знайшовши strcmp, можна встановити rdx на значення більше 0.

Зверніть увагу, що зазвичай rdx вже міститиме значення більше 0, тому цей крок може бути не обов'язковим.

8. Знайти Write або еквівалент

Нарешті, потрібен гаджет, який ексфільтрує дані, щоб ексфільтрувати бінарний файл. І в цей момент можна контролювати 2 аргументи та встановити rdx більше 0.

Є 3 загальні функції, які можна зловживати для цього:

  • puts(data)

  • dprintf(fd, data)

  • write(fd, data, len(data)

Однак оригінальна стаття згадує лише про write, тому давайте поговоримо про це:

Поточна проблема полягає в тому, що ми не знаємо, де функція write знаходиться всередині PLT і ми не знаємо номер fd, щоб надіслати дані до нашого сокету.

Однак ми знаємо, де знаходиться таблиця PLT, і можна знайти write на основі його поведінки. І ми можемо створити кілька з'єднань з сервером і використовувати високий FD, сподіваючись, що він відповідає деяким з наших з'єднань.

Поведінкові сигнатури для знаходження цих функцій:

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP -> Якщо дані надруковані, тоді знайдено puts

  • 'A' * offset + canary + rbp + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> Якщо дані надруковані, тоді знайдено dprintf

  • 'A' * offset + canary + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + (RIP + 0x1) + p64(0x0) + (PLT + 0xb ) + p64(STRCMP ENTRY) + (BROP + 0x9) + FD + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb) + p64(ENTRY) + STOP -> Якщо дані надруковані, тоді знайдено write

Автоматичне експлуатація

Посилання

Support HackTricks

Last updated