Всем привет! В этой серии райтапов мы разберем известные задания по эксплуатации бинарных уязвимостей с Exploit Exercises (там их больше нет, поэтому я их перекомпилировал под Win). О том, что такое Buffer Overflow прекрасно и с примерами написано ТУТ (отличная статья от @Mogen). Кстати говоря, много крутых задачек на тему PWN есть на Codeby Games, так что рекомендую :)
Итак... мы будем решать наши задачки, используя статический (Ghidra) и динамический анализ (x64dbg). И самое главное, мы будем делать это без исходников уязвимой программы, в отличие от того, как это сделано ТУТ и ТУТ.
Stack0
Для решения этой задачки я буду использовать свою «песко‑реверс‑лабораторию», где у меня уже все «стоит» :)
Сначала будем делать «статику». Запускаем гидру и закидываем программу, которую будем «пывнить».
Запускаем анализ:
Получаем дизасм-листинг и декомпилированный код:
Переименовываем переменные (тут кода мало — не сильно нужно, но привычка хорошая):
Итак, видим, что у нас есть массив buf на 64 символа и «плохая» функция gets(). Также у нас есть условие: если переменная var1 = 0, то мы получим «Try again». Соответственно, чтобы попасть в ветку else, нам нужно как‑то модифицировать переменную var1. Выделяем код и запоминаем инструкцию «test eax, eax», она нам пригодится чуть позже.
Переходим к «динамике». Открываем программу в x64dbg, смотрим strings и находим тот самый «Try again», проваливаемся по инструкции, видим «test eax, eax» и «test al, al». Ставим на любой из этих инструкций точку останова.
«Подаем на вход» 10 символов.
Видим, что регистр EAX никак не поменялся (равен 0). P. S. Инструкция «test eax, eax» в языке ассемблера x86/x64 выполняет битовый логический оператор AND между регистром eax и самим собой. Инструкция «test» не изменяет значение в регистре eax, но влияет на флаги процессора, в частности, на флаг нуля (ZF), который устанавливается, если результат операции AND равен нулю. Это означает, что если eax содержит нуль, то ZF будет установлен, а если eax не равен нулю, то ZF будет сброшен. Это позволяет использовать инструкцию je (jump if equal) или jne (jump if not equal) для перехода к определенным меткам в зависимости от значения EAX.
Делаем условный переход (je) и получаем «Try again».
Здорово! А что будет, если мы отправим не 64 символа, а 65?
Видим, что в EAX находится единичка. Таким образом, 64 байта «привели» нас к началу измененной переменной (var1), и уже следующий байт будет помещен в измененную переменную. Другими словами, 65 байт данных изменят 0×00 000 000 на 0×00 000 041 (65-й символ «A»). Таким образом, небезопасная функция gets() позволила нам переполнить buf и изменить переменную var1, что привело нас к следующему результату:
Всем спасибо за внимание :)