Эксплуатация бинарных уязвимостей или учимся «пывнить» (Часть 1 / Stack0)

Всем привет! В этой серии райтапов мы разберем известные задания по эксплуатации бинарных уязвимостей с Exploit Exercises (там их больше нет, поэтому я их перекомпилировал под Win). О том, что такое Buffer Overflow прекрасно и с примерами написано ТУТ (отличная статья от @Mogen). Кстати говоря, много крутых задачек на тему PWN есть на Codeby Games, так что рекомендую :)


Итак… мы будем решать наши задачки, используя статический (Ghidra) и динамический анализ (x64dbg). И самое главное, мы будем делать это без исходников уязвимой программы, в отличие от того, как это сделано ТУТ и ТУТ.

Stack0

Для решения этой задачки я буду использовать свою «песко-реверс-лабораторию», где у меня уже все «стоит» :)

ae2ce1c574e89bbde49c225699c6b08e.jpg

Сначала будем делать «статику». Запускаем гидру и закидываем программу, которую будем «пывнить».

ade86d1ca44a676f71d94243d3dd8b27.jpg

Запускаем анализ:

8b635ddc15ebe14096e99c3b7ead30a8.jpg

Получаем дизасм-листинг и декомпилированный код:

4e30480fca0cc6a61675b4b1b707f7e6.jpg

Переименовываем переменные (тут кода мало — не сильно нужно, но привычка хорошая):

7dd05f98c555ca149897f83dcd58adea.jpg

Итак, видим, что у нас есть массив buf на 64 символа и «плохая» функция gets (). Также у нас есть условие: если переменная var1 = 0, то мы получим «Try again». Соответственно, чтобы попасть в ветку else, нам нужно как-то модифицировать переменную var1. Выделяем код и запоминаем инструкцию «test eax, eax», она нам пригодится чуть позже.

88d6f7cabd4cd3ad3a3d783f01f9b749.jpg

Переходим к «динамике». Открываем программу в x64dbg, смотрим strings и находим тот самый «Try again», проваливаемся по инструкции, видим «test eax, eax» и «test al, al». Ставим на любой из этих инструкций точку останова.

41449cf2fbc18baf8ac98c11907786bc.jpg1ccd4265accb0b40c270dab7b3bff6b7.jpg

«Подаем на вход» 10 символов.

753d9b964d2bf508892c9882a9d5ddf4.jpg

Видим, что регистр 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».

8cd808ddc3b790c607e282d3b590d583.jpgf09d04d47183355ebe45eba5f7353fc1.jpg

Здорово! А что будет, если мы отправим не 64 символа, а 65?

f1498ee60b50ff758f01d485234a036f.jpg

Видим, что в EAX находится единичка. Таким образом, 64 байта «привели» нас к началу измененной переменной (var1), и уже следующий байт будет помещен в измененную переменную. Другими словами, 65 байт данных изменят 0×00000000 на 0×00000041 (65-ый символ «A»). Таким образом, небезопасная функция gets () позволила нам переполнить buf и изменить переменную var1, что привело нас к следующему результату:

280ef565ab1d674e2399c37455f73dac.jpg

Всем спасибо за внимание :)

© Habrahabr.ru