В прошлые выходные у меня была возможность принять участие в CTF STACK the Flags, организованном GovTech Cyber ​​Security Group. Это был особенно интересный (и утомительный!) опыт — хотя моя команда застряла на многих задачах, это было напоминанием о том, что есть (и всегда будет) еще много всего, о чем нужно узнать.

Другой товарищ по команде (Джордж Чен) опубликовал статьи здесь и здесь для задач OSINT, и в этой записи подробно рассказывается об опыте решения первой задачи обратного проектирования, которой мы все занимались. относительно новый для.

На первый взгляд задача выглядит довольно просто:

Предоставленный файл представляет собой zip-архив, содержащий веб-страницу (приглашение), а также два файла JavaScript. Открытие веб-страницы в браузере показывает… пустую страницу. Однако консоль браузера дает первый намек на то, как решить эту проблему:

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

Попытка запустить canvas[_0x3f3a[3]](_0x3f3a[2]) приводит к ошибке, что canvas тоже равно null, поэтому давайте посмотрим, что должно быть canvas — из содержимого массива _0x3f3a мы можем сказать, что canvas должен быть элементом DOM с классом G, однако элемент canvas на странице (преднамеренно) имеет класс CSS только GG, поэтому давайте изменим исходный код страницы, чтобы элементы холста отражали правильный класс, и перезагрузим страницу.

После перезагрузки в консоли появляется другая ошибка:

Вернемся к источнику JS, чтобы посмотреть объявление для gl[_0x3f3a[11]]:

Проверка shade, ctype и cid подтверждает, что они соответствуют значениям атрибутов объекта <canvas> на странице. К сожалению, не имея представления о том, какими должны быть «правильные» значения атрибутов, а также из-за нехватки времени во время CTF, я отказался от стратегии, направленной на то, чтобы страница работала «правильно». Вместо этого я углубился в код, чтобы увидеть, что происходит:

Здесь анонимная функция — давайте попробуем присвоить выражение функции новой переменной и посмотрим, произойдет ли что-нибудь:

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

Оператор debugger здесь функционально эквивалентен установке здесь точки останова и будет делать это 1000 раз — очевидно, это не то, что можно обойти, нажав «Продолжить» в консоли отладки. Давайте удалим цикл и перезапустим код:

Это тот момент, когда это стало довольно захватывающим. Однако браузер не показывал предупреждения. Судя по описанию вызова, либо hhh, либо mmm почти наверняка должны были быть переменной, содержащей флаг, поэтому давайте быстро взглянем на то, что они содержат в настоящее время:

Сейчас это не очень полезно, поэтому давайте сделаем шаг назад и посмотрим, что код делал раньше. Первое, на что я посмотрел, был вызов compare():

…который, вероятно, был назван так намеренно, чтобы ввести в заблуждение. Функция на самом деле содержит функцию XOR, и функция вызывалась для XOR имени хоста, сообщенного браузером, с предварительно определенной строкой you're invited!!! (по символьному коду) перед сравнением со значением, декодированным в URL-адресе. К счастью, мы знаем, что можем найти неизвестное значение с помощью операции XOR над двумя известными значениями, так что давайте сделаем это:

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

Вот тут-то и становится неловко — среди волнения я совершенно упустил из виду значение массива x и сразу перешел к объявлениям для hhh и mmm. Было определено, что hhh связано с проверкой имени хоста, поэтому внимание переключилось на mmm, который, скорее всего, должен был содержать флаг. После значительного количества потерянного времени, пытаясь подобрать возможные возвращаемые значения rrr(), мне наконец пришло в голову, что функция возвращает разные значения при каждом вызове! Вернувшись к коду, мы увидим, где функция была заполнена:

Теперь все ясно — ooo возвращает функцию с ttt в качестве начального значения, из которого значение ttt вычисляется с помощью iii. Поскольку x было передано в iii, нам нужно увидеть, где устанавливались значения в массиве. В этот момент было тривиально пройтись по каждому if-условию, вручную установить элементы x в их правильные значения, а затем вызвать функции вручную через консоль:

x = [57, 88, 54]

В целом, это был тот, который, к сожалению, потребовал немало проб и ошибок и переделки из-за моего относительно плохого знания JavaScript. В будущем следует подумать о том, чтобы попытаться воспроизвести среду браузера, чтобы различные проверки скрипта правильно устанавливали значения в массиве x, и «исправление» приглашения, чтобы оно работало должным образом, путем представления флага в предупреждении браузера. вместо.