Данное описание подготовлено и публикуется со слов участника CTF, занявшего почетное второе место – Сементинова Сергея ( @cema_rus ). Мы разбили прохождение реверста на несколько статей потому что они достаточно обширны и подробны, первая часть разбором с заданиями Task1.exe, Task2.exe, Task3.exe, taskSyzran.exe уже доступна, как и вторая часть c Deep.exe, а это уже третья часть. Дальше текст передается as-is.

Final.exe

Финальное задание и самое интересное! Далее будут описаны только основные шаги решения, а тонны негодования, возникших проблем на разных этапах и различные поиски, я оставлю “за кадром”.

Собираем всё необходимое

Сразу же смотрим информацию в программе Detect-It-Easy и видим, что компилятор Embarcadero Delphi(XE7) - будем облегчать себе жизнь при помощи Interactive Delphi Reconstructor (https://github.com/crypto2011/IDR); Качаем, подкидываем ей все “базы знаний”, запускаем, открываем наш Final.exe и ждём, пока пройдёт анализ файла. После окончания анализа, в меню Tools выбираем пункт IDC Generator и сохраняем куда-нибудь скрипт Final.idc;

Запускаем IDA и открываем наш Final.exe, смотрим функцию start (ДО) и радуемся, что большую часть за нас всё сделает полученный выше скрипт. В меню File выбираем пункт Script File и открываем наш Final.idc и дожидаемся, пока скрипт все сделает - готово (ПОСЛЕ).

Анализ кода после декомпиляции

Функция EntryPoint

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

В конце главной функции видим вызов Halt0. С высокой долей вероятности - это будет завершение программы; из чего следует, что переменная byte_4E13C4 - означает что введенная строка оказалась верной. Назначаем ей имя bFlagIsOK и продолжаем распутывать этот клубок, попутно рассматривая условия, зависимости и прочие штуки, которые помогут нам понять алгоритм проверки.

Находим в каком месте в bFlagIsOK присваивается единица и какие условия для этого должны быть выполнены (помечаем) и, если верить логике, то в случае невыполнения условия говорим, что что-то пошло не так и уходим на очередной круг цикла (помечаем и это место).

При этом получаем метку, на которую ещё из 2х мест уходит наша программа, а значит и в этих местах проверка флага “не проходит”.

Изучая эти условия, мы понимаем, в какой переменной находится введенная нами строка и продолжаем анализ всех действий, которые с ней происходят. Далее я пропущу описание поиска всех значимых для алгоритма переменных, а просто покажу часть “причесанного” кода, по которому уже можно строить алгоритм проверки введенной строки.

Всё описанное и немного фантазии, дают нам следующий алгоритм для проверки строки:

  1. Читаем строку из консоли;
  2. Если строка начинается не с последовательности “flag”, уходим на метку lblFlagIsNotCorrect;
  3. Если длина строки не равняется 26 символам, 4й символ (начиная с нулевого) не равен ‘{‘ или 25й символ не равен ‘}’ - уходим на метку lblFlagIsNotCorrect;
  4. Для каждого символа в фигурных скобках формируем строку, в которой этот символ повторяется столько раз, какой он идёт по порядку от скобок; т.е. для введенной строки flag{12345.... Это будут строки: “1”, “22”, “333”, “4444”, “55555” и т.д.;
  5. Идёт обработка двумя функциями sub_4D37DC, sub_4D372C и если результат их выполнения равен проверяемому символу, то увеличиваем счётчик;
  6. Если, по окончании строки, счётчик равен 20 (количеству символов в скобках), то bFlagIsOK = 1, выводим радостное сообщение и выходим из программы.

В целом, картина уже довольно ясная, кроме 2х моментов - это функции sub_4D37DC и sub_4D372C.

Функция sub_4D37DC

Разбор этой функции почти превратился для меня в ад, но “голос свыше” обратил моё внимание, что цифры при заполнении массива в одной из функций внутри очень нарядно выглядят:

Поиск в гугле по этим цифрам, сказал что такие полиномы (если их можно так назвать) используются при расчёте хэша MD5 и SHA-1. Изучив подробнее, выясняем, что в SHA-1 используется еще 5й полином, а значит наш случай - это MD5. Переименовываем функцию и идём дальше)

Функция sub_4D372C

На вход ей отправляется результат расчёта MD5 хэша из функции выше и байт из некоторого массива, индекс которого соответствует индексу проверяемого символа (после скобок, естественно). Далее функция переводит бинарный код в HEX строку и результатом функции является символ, стоящий на определенной позиции из этой HEX-строки. Позиция соответствует значению переданного байта.

Итог

Заменяем в нашем алгоритме пункт 5 на пункты: 5.1. Расчет хэша MD5 для сформированной строки; 5.2. Формируем из бинарного кода HEX-строку; 5.3. Возвращаем n-й символ из строки (n зависит от значения байта в неком массиве);

Ну что ж… Общий алгоритм готов. Теперь берём в руки какой-нибудь язык программирования и пишем кейген) В нашем случае, это будет не совсем кейген, а программа, которая покажет все возможные символы для каждой из позиций в строке.

Делаем калькулятора флага

Первую реализацию калькулятора флага, сделал на C#. Всё, вроде, выглядело хорошо и должно было работать так же.

Но, как обычно, что-то пошло не так и для некоторых позиций не было ни одного подходящего по алгоритму значения.

Ломал голову и так, и эдак, и всё перепроверил по 100 раз, но результата никакого… Спустя какое-то время, когда я уже был готов жевать бумагу с заметками, выяснилось, что MD5 не такая уж MD5 как кажется на первый взгляд. Наиболее внимательные уже заметили, что полиномы для расчёта MD5 те же, но порядок у них другой.

Пришло время второй реализации, но… На чём её писать?! Ведь C# в чистом виде уже не подходит, реализации на нём в интернетах у меня вылетали с ошибкой. Из всего остального у меня установлено и имеется достаточный опыт только на чистом C под микроконтроллеры. Значит быть “железному” кейгену) Нашел реализацию MD5 для С, адаптировал её под микроконтроллер и написал кейен уже на выбранное железо.

Компилирую, загружаю в железяку, запускаю терминал и… Вот результат:

Долго мудрить не будем, составим флаг по первым символам в каждой позиции и проверяем сначала в программе, потом на сайте - Correct!

Ну и фото используемой железяки:

Задание выполнено!

Для себя могу отметить одно. Подобные “соревнования” очень сильно стимулируют к изучению чего-либо. И даже не наличием подарков, а просто турнирной таблицей. И хочется сказать СПАСИБО всем причастным к организации данного мероприятия. Было очень интересно!


Файлы заданий