Друзья, сегодня мы завершаем публикацию решений нашего CTF-марафона! В нем было пять уровней сложности, в каждом по пять заданий — всего 25 заданий. Перед вами разбор пятого уровня сложности. Предыдущие уровни вы можете изучить здесь: часть 1, часть 2, часть 3, часть 4.
Результаты марафона мы подвели в начале апреля, но задания все еще доступны — и вы можете попробовать решить их для себя.

CTF-2023 от «Доктор Веб»: задания уровня I Feel No Pain
1. Prehistoric dinosaur
Запускаем и видим, что это калькулятор:

Определяем, на чем он написан:

Далее либо соглашаемся, что все-таки на делфи никто не пишет, либо же возмущаемся, потому что легаси-проекты еще надо поддерживать, и делфи – прекрасный язык, который вообще-то не устарел (тут я в целом то согласен, но на нем действительно никто не пишет). Ах да, о чем это мы...
Это делфи, для этого есть тулза IDR (Interactive Delphi Reconstructor). Запускаем ее и после анализа создаем идс-скрипт.



P.S. Для тех, кто не знает: никогда не запускайте IDR на хостовой машине – он может напрямую запустить некоторые функции, которые могут являться малварными, иными словами – запустить на хостовой машине малварь.

Применяем .idc в IDA, ждем полчаса, соответственно размеру скрипта...

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

Начинаем черную магию – ищем, где у нас будут храниться все объектно ориентированные штуки (всяческие таблицы, адреса и т. д.).

Где в GUI-приложении могут быть какие-либо действия? Предположительно, в ивентах – следовательно, где-то эти ивенты нам надо найти. Скрипт не распарсил правильно, но у нас есть таблица методов, которую можно распарсить руками:

В функциях сложения, вычитания и пр. находится некая функция, которая вызывается из каждого такого ивента:

Что мы можем там найти? Например, switchcase с такими значениями:

Предположительно, это некие значения, которые надо ввести в калькулятор. Также в функции TForm1_equalsButtonClick есть очень интересная функция, которая вызывается, если условие с нужными числами соблюдено:

На этой функции, кстати, декомпиль ложится отдохнуть:

Лег он тут:

Есть «предположение», что столько аргументов оно в себя не принимает)):

Фиксим, декомпилируем – и внутри нас ждет… (барабанная дробь)… крипта!

А дальше – гигантская строка:

Некий вызов функций, который IDA решила не парсить:

Немного покопавшись в этом всем, определяем: у нас есть AES, есть шифрованное сообщение, и нам нужно составить некий ключ. Значений для этого ключа несколько, так что будем брутить.
Пара вариантов функции декрипта:

Флаг: DrWeb{1_l1k3_d3lph1_r3v3rs3_3ng1n33r1ng_d1d_y0u_kn0w_1_4m_utt3rly_1ns4n3?}
2. NAMRS
Запускаем игру, видим следующее:

Игровой функционал весьма скуден: мы можем стрелять, летать и ускоряться. Если чуток полетать, пострелять и попасть в вирус, то получится вот что:

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

Шейдеры (нам это не пригодится):

Текстуры (это великолепие нам тоже особо не будет нужно):

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

Прекрасно – строки есть, и они не обфусцированы. Таким образом мы весьма быстро находим функцию инициализации объектов:

Объектник ключа тут не создается – значит, его надо найти. Ищется он тоже просто:

При этом вся эта конструкция находится в цикле на 0x2121 итераций:

Листаем дальше, в цикле находим несколько функций. Одна из них задает некие константы, весьма большим объемом байт, а также имеет зашитую строку “flagis:”:

Отслеживаем все из этой функции:

Находим нечто прекрасное:

Переходящее в еще более прекрасную функцию:

В итоге у нас есть цикл на 0x2121 итераций, в каждую итерацию генерируется ключ путем XOR’а с константами 0x75 0x53AD 0x709D 0x779A 0x8D, а после дальнейшего XOR’a с пошифрованным флагом... ужас...
Но, зная это, можно запустить игру, дойти до ключей, запустить дебаггер, приаттачиться к процессу, и сдампить память (ну или искать прямо в ней).


Находим этот участок памяти и дампим его весь. Там будет находиться очень приличное количество объектов ключей с "flagis:XXXXXXXX...", тут уже есть вариант поискать нужный ключ вручную (флаг перекодирован в base64, чтобы было не так просто найти, и рядом с флагом лежит строка CORRECTcorrectCORRECTcorrect, по которой можно догадаться, что флаг нужный).

Один из простых способов – написать на питоне небольшой тул, который отсортирует нужный нам дамп памяти по корректным символам аскии.

Вывелись две строки:

Корректность флага можно определить по дополнительной вставочке “CORRECTcorrectCORRECTcorrect”.
Раскодируем “RHJXZWJ7NWM0bl9NM19NM20wcnl9” из Base64 и получаем флаг.
Флаг: DrWeb{5c4nM3M3m0ry}
3. Go Go Go DrWeb
По факту это просто закомпиленный бинарь на golang. Сложность же состоит в том, что все символы были стрипнуты. А так как go прекрасным образом вкомпиливает в бинарь вообще все, то нам предстоит каким-то магическим образом отыскать Main.
Главная загвоздка задания – в том, что IDA очень плохо парсит golang, а если отвязать символы, то все. А когда кто-либо стрипает символы при компиляции (без очередных запретных знаний, а просто флагом, который предлагает golang), они не стрипаются, а... отвязываются.
Так что в момент запуска и решения CTF вдруг обнаружилось, что IDA > 8.0 может эти отвязанные символы подгрузить...
Таким образом, обладая этими буквально запретными знаниями (
