PicoCTF 2019. Решение всех задания категории Reverse Engineering.
https://t.me/hacker_sanctuary
Этот пост является продолжением решения заданий с picoCTF. Предыдущие посты вы можете найти на канале:
- Первый пост, объяснения для начинающих и решение задач General Skills - https://t.me/hacker_sanctuary/417
Про категорию Reverse Engineering
Задания в данной категории чаще всего представляют собой исполняемый файлы, которые необходимо проанализировать и понять как они работают. Чаще всего даются скомпилированные программы, то есть вам необходимо анализировать не исходный код, а результат дизассемблирования и декомпиляции.
Список заданий (навигация по посту):
- Задание "vault-door-training"
- Задание "vault-door-1"
- Задание "vault-door-3"
- Задание "vault-door-4"
- Задание "asm1"
- Задание "asm2"
- Задание "asm3"
- Задание "reverse_cipher"
- Задание "vault-door-5"
- Задание "droids0"
- Задание "vault-door-6"
- Задание "droids1"
- Задание "Need For Speed"
- Задание "Time's Up"
- Задание "asm4"
- Задание "vault-door-7"
- Задание "droids2"
- Задание "Time's Up, Again!"
- Задание "droids3"
- Задание "vault-door-8"
- Задание "Forky"
- Задание "Time's Up, For the Last Time!"
- Задание "droids4"
- Задание "B1ll_Gat35"
Задание "vault-door-training"

В данном задании нам даётся исходный код на языке Java. Открыв его в текстовом редакторе сразу же можно найти флаг. Если немного посмотреть код, то видно, что наш ввод с клавиатуры попадает в функцию checkPassword, где просто сравнивается с константной строкой.

Ответ: picoCTF{w4rm1ng_Up_w1tH_jAv4_c0b141c5e30}
Задание "vault-door-1"

Задание аналогично предыдущему. Скачиваем файл и смотрим код.

На этот раз проверка немного сложнее. Теперь каждый символ введённого пароля достаётся по индексу и сравнивается с одним константным символом. Таким образом мы можем выписать все индексы и соответствующие им символы и собрать верный пароль.

Ответ: picoCTF{d35cr4mbl3_tH3_cH4r4cT3r5_03f841}
Задание "vault-door-3"

Задание аналогично предыдущему. Скачиваем и открываем код.

Как можно заметить на этот раз индексы рассчитываются внутри циклов. Можно просто взять и переписать данный код на Python.

Запускаем и получаем флаг.

Ответ: picoCTF{jU5t_a_s1mpl3_an4gr4m_4_u_c33f38}
Задание "vault-door-4"

Всё тоже, что и в прошлом задании. Скачиваем исходник и смотрим код проверки пароля.

Ещё проще, чем прошлый. Здесь пароль по сути лежит в открытом виде, просто закодирован по частям в разном виде (десятичные числа, 16-ричные числа, 8-ричные числа и просто символы). Перенесём всё в Python.

Запустим и получим верный флаг.

Ответ: picoCTF{jU5t_4_bUnCh_0f_bYt3s_b9e92f76ac}
Задание "asm1"

Данное задание отличается от прошлых. Здесь нам даётся ассемблерный код под х86, а также говорится, что функция вызывается с аргументом 0x345. Необходимо найти результат работы функции и ввести его в поле сдачи флага. При этом формат флага использовать не нужно. Скачиваем файл и смотрим код.

Сначала наш аргумент сравнивается с числом 0x37a и если аргумент больше, то происходит прыжок, в нашему случае аргумент меньше, значит код выполняется дальше и следующее сравнение происходит с числом 0x345 в результате сравнения происходит прыжок если оба числа не равны, но в нашем случае они равны, а значит выполняется следующая инструкция, которая помещает аргумент в регистр eax, после чего прибавляет к нему 3 и выходит из функции. Значит функция вернёт 0x348, вводим это в поле ответа и это верный ответ.
Ответ: 0x348
Задание "asm2"

Задание аналогично прошлом, на этот раз функция вызывается с двумя аргументами 0x9 и 0x1e. Скачаем и посмотрим код.

В начале функции аргументы копируются в локальные переменные, после чего следует цикл. Смысл цикла заключается в том, что к переменной прибавляется каждый шаг число 0xa9 пока эта переменная меньше или равна числа 0x47a6, при этом вторая переменная просто инкриминируется. Это можно реализовать на Python, осталось понять какие начальные значения у переменных. В [ebp-0x4] копируется второй аргумент, а в [ebp-0x8] первый.

В итоге получаем 139 и переводим в 16-ричное число, чтобы сдать как ответ.
Ответ: 0x8b
Задание "asm3"

Концепция заданий та же самая. Скачиваем и открываем исходник.

В этот раз передаётся 3 аргумента: 0xcdc485c1,0xd6bd5e88,0xe4c1548d. При этом кода даже меньше, чем в прошлый раз. Опишем код по шагам.
- Помещаем в регистр ah младший байт первого аргумента (это 0xc1)
- Сдвигаем регистр ax влево на 0x10, т.к. в al лежит 0, то это операция 0xc100 << 16, которая оставит в регистре ax значение 0x0000
- Вычитаем из регистра al 3 байт второго аргумента и получаем 0x43, т.к. в al был ноль
- Прибавляем к ah младший байт второго аргумента
- Сейчас в ax находится число 0x8843 которое XOR-ится с двумя байтами третьего аргумента
Ответ: 0xdcce
Задание "reverse_cipher"

В этом задании нам наконец-то дают исполняемый файл и необходимо использовать дизассемблер.
Скачиваем бинарник и текстовый файл.

У нас есть некоторое зашифрованное сообщение, необходимо понять как оно было зашифровано с помощью анализа исполняемого файла.
Используем IDA PRO.

Шифрование происходит очень просто. В цикле последовательно перебираются индексы флага и в зависимости от чётности индекса используется соответствующий метод изменения символа.
Реализуем обратную операцию на Python.

Ответ: picoCTF{r3v3rs369806a41}
Задание "vault-door-5"

Опять задача на анализ исходников на Java. На этот раз алгоритм довольно прост. Наш пароль переводится в url-encode, после кодируется в base64 и сравнивается с константой строкой.

Возьмём строку и переведём из base64, а потом сделаем url-decode.

Ответ: picoCTF{c0nv3rt1ng_fr0m_ba5e_64_1177f783}
Задание "droids0"

В данном задании нам предоставляют приложение для Android. Скачиваем файл и распаковываем его с помощью unzip.

После распаковки нас будет интересовать файл classes.dex, т.к. в нём содержится код приложений. Перевод данного файла в формат доступный для анализ осуществляется с помощью утилиты dex2jar (https://github.com/pxb1988/dex2jar). После этого мы получим JAR-файл, который можно загрузить в декомпилятор, например, jd-gui.

Анализируя точку входа, можно понять, что загружается некоторая нативная библиотека, а также вызывается метод getFlag. Посмотри на класс который содержит метод получения флага.

Вызывается некоторая функция "paprika", которой нет в данном исполняемом файле. Посмотрим есть ли эта функция в библиотеке, которая загружается.

В библиотеке функция присутствует, проанализируем её код.

Видим, что происходит вызов ещё одной функции с именем "marjoram". Посмотрим что она делает.

Происходит вызов функции unscramble с аргументов в виде указателя на память и указателя на строчку "notexist".

Функция unscramble просто производит XOR по ключу. Посмотрим на блоб в памяти и поробуем по-XOR-ить его с ключом "notexist".

Реализуем просто скрипт на Python.

Запустим и получим флаг.

Ответ: picoCTF{a.moose.once.bit.my.sister}
Задание "vault-door-6"

Всё тоже самое, качаем и смотрим функцию проверки пароля.

На этот раз используется алгоритм числового преобразования. Наш пароль проходит через XOR с числом 0х55, после чего из результат операции вычитается число из массива и сравнивается с нулём. Т.к. XOR это однозначно обратимая операция, мы можем найти необходимые символы с помощью прогона массива значений myBytes через операцию XOR с числом 0x55.

Запустим скрипт.

Ответ: picoCTF{n0t_mUcH_h4rD3r_tH4n_x0r_3484ebc}
Задание "droids1"

Задание похоже на droids0 вплоть до используемой библиотеки. Только на этот раз используется другая функция из библиотеки.

Используется функция fenugreek, при этом передаётся некоторый аргумент, который представляет собой ключ для всё той же функции unscramble. Аргументом является строчка, которая достаётся из ресурсов по коду 2131427375. Найдём имя строки в R.class.

Отлично, теперь осталось распаковать ресурсы и поискать строку "password". Распаковку ресурсов можно сделать с помощью apktool.

В итоге получаем директорию one, где лежит папка res в которой находятся все необходимые ресурсы. Выполним в ней рекурсивный поиск строки.

Попробуем в качестве ключа использовать строку "opossum".

Как и в прошлом задании значения списка "a" просто лежат в памяти библиотеки.
Запустим скрипт.

И пароль оказался верным, мы получаем флаг.
Ответ: picoCTF{pining.for.the.fjords}
Задание "Need For Speed"

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

В функции get_key получается ключ для дешифрования флага. При этом в функции явно понятно какой ключ должен быть возвращён.

Функция вызывает calculate_key, код которого очень прост. От некоторой константы отнимается по 1 в цикле, пока значение не достигнет другой константы, после чего финальное значение просто возвращается из функции.

Мы можем просто пропатчить функцию calculate_key, чтобы она сразу возвращала необходимое значение. То есть функция сразу вернёт 0xe923b0ab.

Таким образом она будет работать моментально и мы можем просто запустить файл и получить флаг.

Ответ: PICOCTF{Good job keeping bus #1cf20c02 speeding along!}
Задание "Time's Up"

Задача похожа на прошлую тем, что опять есть ограничения на время. Но теперь нужно ввести верный ответ на пример, после чего будет прочтён флаг. Также надо выполнять это задание на сервере организаторов, поэтому запатчить файл не получится.
Попробуем запустить файл и увидим, что у нас практически нет времени на ответ.

В этом случае мы можем написать небольшой скрипт на языке Python который будет обрабатывать вывод программы и стараться успеть отправить верный ответ.

Запустим его и получим флаг (возможно придётся сделать несколько запусков).

Ответ: picoCTF{Gotta go fast. Gotta go FAST. #3daa579a}
Задание "asm4"

Заключительное задание на анализ ассемблерного кода. В этот раз на вход функции подаётся целая строка.
Посмотрим исходный код.

Анализировать всё это вручную не очень хочется, поэтому просто скомпилируем его.

В файле asm4.s содержится код функции из задания. В файле main.c находится только вызов функции и печать на экран результата.

В файле asm4.s нужно сделать небольшой заголовок, чтобы компилятор смог верно его собрать.

Запускаем полученный исполняемый файл и получаем ответ 0x24d (выше есть изображение со всеми командами сборки и запуска файла).
Ответ: 0x24d
Задание "vault-door-7"

Очередное задание на анализ исходного кода на Java. Скачиваем исходник и открываем его и смотрим функцию проверки пароля.

На этот раз наш пароль считывается и переводится в числовой вид. На самом деле есть взять число из правой части, перевести его в 16-ричный вид и после декодировать в ASCII то вы получите символы пароля. Перевод в число осуществляется с помощью битового сдвига, таким образом 4 символа можно превратить в четырёх-байтное число.
Напишем небольшой код для декодирования значений.

Ответ: picoCTF{A_b1t_0f_b1t_sh1fTiNg_df5f8ed440}
Задание "droids2"

Очередное задание на Android. Делаем все те же действия, что и в прошлых заданиях. В целом задачи тут одинаковы по концепции: происходит XOR с ключом, ключ формируется внутри приложения. То есть нужно просто искать формирование ключа.
В этом задании ключ формируется путём конкатенации строк.

Индексы рассчитываются от известных значений, поэтому просто конкатенируем строки и получаем верный ключ: "dismass.ogg.weatherwax.aching.nitt.garlick". Вводим его в скрипт и копируем зашифрованный блоб из библиотеки.

Запускаем и получаем флаг.

Ответ: picoCTF{what.is.your.favourite.colour}
Задание "Time's Up, Again!"

Третье задание на время. На этот раз даётся крайне мало времени и обычный скрипт на Python не справится. Если немного вдаться в подробности временного ограничения в этом задании, то станет понятно, что оно достигается с помощью отправки сигнала ALARM процессу. Т.к. сигналы отправляются в пользовательский контекст, то пользователь может игнорировать сигнал.
Это мы и реализуем небольшим кодом на Си.

Теперь при запуске данного кода в директории с заданием у нас будет неограниченное количество времени на ввод ответа.

Ответ: picoCTF{Hasten. Hurry. Ferrociously Speedy. #1bba35a4}
Задание "droids3"

Очередное задание на Android. Все действия аналогичны решению прошлых заданий. Смотрим функцию проверки пароля.

Видим, что вызывается функцию из библиотеки. Взглянем на эту функцию. Она вызывает sumac() без аргументов, таким образом задача похожа на самую первую, когда внутри самой библиотеки находился ключ.

Подставим эти данные в скрипт и запустим его.

Запускаем и получаем флаг.

Ответ: picoCTF{tis.but.a.scratch}
Задание "vault-door-8"

Заключительный таск из серии анализа Java кода. Скачаем файл и посмотрим проверку пароля.

На этот раз алгоритм выглядит сложнее. Но он по сути полностью дан, поэтому мы можем просто перебрать все варианты реализовав его на Си.

Запустим и получим флаг.

Ответ: picoCTF{s0m3_m0r3_b1t_sh1fTiNg_ad0f0c833}
Задание "Forky"

Данное задание представляет собой исполняемый файл, который вызывает fork() внутри себя 4 раза. Необходимо ответить на вопрос "Какое значение будет в переменной в самом конце выполнения".

Нужно просто понять, сколько раз будет вызван fork(), то есть сколько раз к переменной прибавится число 1234567890.
Чтобы посчтитать все запускаемые процессы, можно использовать команду ltrace -f ./vuln

Немного сломав глаза, можно понять, что генерируется всего 16 процессов. Таким образом мы можем узнать финальное значение (не забываем про переполнение int32).

Ответ: picoCTF{-721750240}
Задание "Time's Up, For the Last Time!"

Это финальное задание на ограничение времени, на этот раз времени практически нет и наверное, это задание практически не реально решить другим способом, кроме как блокировки сигнала. Используем наш код из прошлого задания на эту тематику.
На этот раз выражение которое нужно посчитать выглядит иначе. Добавились некоторые знаки, которые не соответствуют обычным арифметическим знакам.

Придётся открыть исполняемый файл и посмотреть обработчики этих символов.
Обрабатываются они в функции sub_CA2

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

Передаём это в Python и получаем ответ.

В итоге получаем флаг.
Ответ: picoCTF{And now you can hack time! #0e9c1f05}
Задание "droids4"

Финальное задание на Android. Всё как и в прошлые разы, смотрим как формируется пароль.

Изначально инициализируется 4 строки, после чего они проходят через операции преобразования и склеиваются между собой. Попробуем выполнить этот код в онлайн-компиляторе Java.

Получаем ключ для xor-a "alphabetsoup".
Теперь нужно вытащить байты из библиотеки. Т.к. у нас осталась всего одна функция, которая не была использована в прошлых, заданиях, то не сложно понять какой именно блоб нужно вытаскивать.

Помещаем всё в скрипт и запускаем его.

Запускаем.

Ответ: picoCTF{not.particularly.silly}
Задание "B1ll_Gat35"

Последнее задание в данной категории и наконец-то на Windows. Скачиваем исполняемый файл и открываем в IDA. Сразу можно заметить, что из файла удаленна вся символьная информация, то есть мы не знаем имена используемых функций. Для того чтобы найти точку входа можно посмотреть на строчки, которые используются в файле.

Перейдём в место использования этих строчек и попадём в функцию main.

Логика программы довольно проста, она принимает некоторое число не больше 5 знаков, после чего инициализирует себя и просит ввести некоторый код. Если код был верный то выводится флаг.
Самый просто вариант - это попробовать подключиться отладчиком и после проверки кода поставить управление на блок, который вызывается при корректном ключе.

Для этой цели можно использовать x32dbg и опцию "Установить текущим адресом выполнения".
Вводим любые данные и получаем некоторый флаг.

Пробуем сдать его и действительно, это верный ответ.
Ответ: PICOCTF{These are the access codes to the vault: 1063340}
Всё, все задачи решены, всего их было 24.
Из интересного можно отметить задачи на время, они действительно заставили немного подумать и покопаться во внутренностях сигналов в Linux.
Если вы хотите порешать ещё заданий на обратную разработку, то вы можете найти их на сайте ReversEveryDay (https://reverseveryday.com/). Однако на этом сайте задачи несколько сложнее приведённых в данном посту.