BROP - Blind Return Oriented Programming

Підтримайте 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' * зсув + канарейка + rbp + ADDR + 0xdead * 6 + STOP

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

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

'A' * зсув + канарейка + rbp + ADDR

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

6. Знайдіть PLT

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

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

  • 'A' * зсув + канарейка + rbp + ADDR + STOP -> без аварії

  • 'A' * зсув + канарейка + rbp + (ADDR + 0x6) + STOP -> без аварії

  • 'A' * зсув + канарейка + rbp + (ADDR + 0x10) + STOP -> без аварії

7. Пошук strcmp

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

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

  • strcmp(<непрочитана адреса>, <непрочитана адреса>) -> аварія

  • strcmp(<непрочитана адреса>, <прочитана адреса>) -> аварія

  • strcmp(<прочитана адреса>, <непрочитана адреса>) -> аварія

  • strcmp(<прочитана адреса>, <прочитана адреса>) -> без аварії

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

  • strcmp(<непрочитана адреса>, <прочитана адреса>) -> аварія

  • b'A' * зсув + канарейка + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0x300) + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP -> Завершиться аварією

  • strcmp(<прочитана адреса>, <непрочитана адреса>) -> аварія

  • b'A' * зсув + канарейка + rbp + (BROP + 0x9) + p64(0x300) + (BROP + 0x7) + RIP + p64(0x0) + (PLT + 0xb ) + p64(ENTRY) + STOP

  • strcmp(<прочитана адреса>, <прочитана адреса>) -> без аварії

  • b'A' * зсув + канарейка + 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' * зміщення + канарейка + rbp + (BROP + 0x9) + RIP + (BROP + 0x7) + p64(0) + p64(0) + (PLT + 0xb) + p64(ENTRY) + STOP -> Якщо дані виведені, то було знайдено puts

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

  • 'A' * зміщення + канарейка + 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

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

Посилання

Last updated