Я клянусь своей честью, что я не оказывал и не получал никакой несанкционированной помощи в выполнении этого задания. — Ян Далтон

Часть 1

Файл: onebyone

Я начал свой анализ этой функции с запуска строк с помощью grep на выходе для поиска флага с заданным форматом. Я не нашел ничего, кроме строки с ошибкой использования «Usage: onebyone ‹flag›».

Я перешел к дизассемблированию файла с objdump . Мне удалось собрать некоторую информацию, но она выглядела не очень хорошо, и я знаю, что есть лучшие инструменты, которые я могу использовать, чтобы лучше отображать путь выполнения программы и делать скриншоты более крутыми. Я скачал бесплатную версию Binary Ninja, которая позволяет удобно дизассемблировать 32-битные исполняемые файлы x86.

Как только я открыл onebyone в Binary Ninja, я смог увидеть множество сравнений в регистре al с переходами сразу после этого в раздел кода, который печатает «нет, извините». Принимая это во внимание вместе со строкой ошибки использования, которую я нашел ранее, я понял, что функция проверяла каждый байт параметра на соответствие байтам флага, жестко запрограммированным в исполняемом файле.

Я записал все жестко закодированные шестнадцатеричные значения, которые используются в cmp инструкциях. Я распознал символы ASCII и перечислил их ниже в том порядке, в котором выполняются cmp instructions:

hex:
0x30,0x65,0x63,0x33,0x7d,0x2d,0x39,0x6e,0x69,0x52,0x7b
ASCII:
0ec3}-9niR{

Буквы кажутся не по порядку. Я еще раз взглянул на дизассемблирование и увидел, что перед инструкциями cmp исполняемый файл добавляет непосредственное значение в регистр eax. Я предположил, что это индекс в строке. Я подтвердил это, найдя одну инструкцию cmp, в которой не было добавлено смещение, и инструкцию со смещением, равным 1. Соответствующие шестнадцатеричные значения, используемые в инструкции cmp, соответствовали символам «3» и «8», первым двум символам в формате флага.

используя эти знания, я переставил символы на основе индекса, предоставленного смещением, добавленным в сборку, чтобы получить следующий флаг:

389R-{n0ice}

Затем я запустил программу, чтобы проверить свою работу:

Файл: stackexchange

Чтобы запустить этот файл, я загрузил его в Binary Ninja. На первый взгляд я не увидел никаких функций вывода и не увидел никаких инструкций, в которых команда сравнивала значения. Я пришел к выводу, что программа не принимала/использовала какой-либо внешний ввод, как в предыдущем файле. Приглядевшись повнимательнее, я заметил, что функция много манипулирует данными, выделяя место для локальной переменной, устанавливая ее в ноль и загружая значения в локальную переменную.

Я подумал, что будет проще просто открыть программу в отладчике, дать ей запуститься и проверить стек и локальные переменные после завершения основной функции, но до ее выхода.

Как только я открыл программу в gdb (и нашел шпаргалку), я набрал start, чтобы начать выполнение, а затем s, чтобы пройти через функцию. Поскольку функция не была написана на C, команда s выполнила все основные, а последующая команда s завершила бы программу, поэтому я начал печатать значения из стека с помощью таких команд:

x/100xw $esp-50
info registers
info locals
backtrace

Я не нашел ничего ценного, поэтому я еще раз посмотрел на инструкции. Я решил, что мне нужно пошагово выполнять инструкцию за инструкцией с помощью команды si и чаще смотреть на кадр стека, чтобы лучше понять, что делает программа.

Я сделал скриншот своего рабочего процесса, чтобы продемонстрировать, что я делаю.

Я знал, что локальная переменная будет сразу после esp, поэтому после каждой инструкции я печатал десять 32-битных слов, начиная с регистра esp с помощью команды x/10 $esp gdb.

Все это время я ищу значения ASCII, соответствующие формату флага, описанному в вызове, в частности, 0x33, 0x38 и 0x39, которые соответствуют «389».

Пройдясь по всем инструкциям, остановившись на инструкции leave, я увидел во фрейме стека шестнадцатеричные значения, которые соответствовали флагу:

Значения стека имеют прямой порядок следования байтов, поэтому для получения ASCII требуется небольшая перестановка.

0x33000000 -> "3" 
0x2d523938 -> "89R-"
0x7d65797b -> "{ye}"

Сдача флага:

389R-{ye}

Примечание:

Я хотел упомянуть, что Binary Ninja выполняет операцию xor за вас. Если бы я навел курсор на «var_#» на снимке экрана Binary Ninja, я бы увидел результирующее шестнадцатеричное значение и таким образом смог бы обнаружить строку. Однако я не осознавал этого до тех пор, пока не нашел строку с gdb (и не сделал запись).

Часть 2

Цель этой задачи — изменить значение логической переменной valid в программе outofbounds.c, не зная пароля. Мы можем добиться этого, используя уязвимость переполнения буфера, которая есть в программе.

Программа outofbounds.c имеет символьный буфер buffer[BUFFER_SIZE] размером 21 байт, однако она имеет вызов чтения, который считывает 22 байта из STDIN в buffer, что на один байт больше выделенного размера.

Дополнительный байт, который считывается, затирает первый байт в стеке после buffer . Для удобства другой локальной переменной в функции main является valid char длиной один байт. Из-за того, как будет настроен стек, этот лишний байт будет затирать значение valid в стеке.

Вот примерный рисунок, показывающий расположение стека при выполнении команды чтения:

Оператор «if», который позже проверяет valid char для «Аутентификации», не проверяет, является ли char определенным значением, а только проверяет, не равно ли это значение нулю. Таким образом, пока значение не перезаписывается нулем, мы должны иметь возможность заставить программу печатать флаг, переполняя буфер избыточным ненулевым вводом. Я делаю это из терминала Kali ниже, используя только кучу «1».

Бинго, распечатанный флаг:

CMSC389R-{wat_r_u_doing}