BROP - Blind Return Oriented Programming
Основна інформація
Метою цього нападу є зловживання 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
Автоматизована експлуатація
Посилання
Оригінальна стаття: https://www.scs.stanford.edu/brop/bittau-brop.pdf
Last updated