Привет! Это Маша из AppSec Альфа-Банка. В прошлом году мы провели наш первый Alfa CTF Surfing Edition — соревнование в сфере кибербезопасности. Их делают для того, чтобы лучше искать уязвимости, атаковать чужую инфраструктуру или защищать свою.
Сегодня мы разберем пару тасок из нашего CTF: «Запреты Роскамбалы» и «Звуки ностальгии». Задачи пропитаны work-life blend, послевкусием летнего отпуска и волн.
Приступим.

№1.«Звуки ностальгии»: задача на внимательность и гуглеж
Описание задачи:
«Вы находите в уголке хижины пыльную коробку с надписью «Не трогать». Внутри — чей-то личный дневник, тетрадь с Tokio Hotel на обложке и тр3-плеер. В приступе ностальгии вы надеваете наушники и нажимаете Plaу... Внезапно мир вокруг начинает меняться, и вы оказываетесь в совершенно незнакомом месте, недоумевая, как сюда попали.
Вокруг ходят люди с кнопочными телефонами и косыми чёлками, которые совершенно не реагируют на просьбы помочь. Пытаясь разобраться вы заходите в компьютерный клуб и находите открытый веб-чат, в котором кто-то до вас уже столкнулся с точно такой же проблемой.
Чат называется: NETCLUB.20070912.HTM
Задача: Выберитесь из ловушки ностальгии и найдите способ вернуться в настоящее: nostalgia-aticjZp9.alfactf.ru/»
Осматриваемся
В решении любой CTF-задаче важна не только эрудированность, но и умение разбираться в любой ситуации, находить информацию и думать. Поэтому мы изуча��м то, что у нас есть — ссылку и чат. По ссылке мы обнаруживаем MP3 Player и некую карту.

Помним, что это CTF, а значит, это лишь начало пути (потому что задачи созданы, чтобы копать), поэтому идём читать чат. Изучаем чат так тщательно, насколько можем, ведь неочевидные подсказки могут быть даже в сообщениях, которые не имеют прямого отношения к делу.

Например, сообщения пользователя, который жалуется, что не помнит, как он сюда попал. А из последних воспоминаний остались только те, в которых он нашёл наушники и, надев их, оказался где-то в прошлом, без понимания, как вернуться домой. Может быть это то что нам нужно?
Над ним смеются, но находится пользователь с интересным ником TimeTravel_0, который тоже через такое проходил и настаивает, что нужно найти улицу, где была записана песня, которая его сюда затянула. Наш бедолага спрашивает: «Какая песня? Я даже не помню, что слушал…У меня есть только это фото». Фото оказывается кадром из клипа.

Зацепка
Это зацепка. Вероятнее всего, нам нужно:
Найти точное место, где снимался клип.
Оказавшись на точке, мы включим ту самую песню и так разорвем временную петлю.
Гуглинг, ожидаемо, ничего не даёт. Но так как это задание на внимательность, то мы замечаем сообщения пользователя с ником Alpha Music (а у нас CTF от Альфы), который пишет, что сохраняет треки в свой ЖЖ.
Ищем ЖЖ пользователя и просматриваем его записи, благо их всего 10.

В одном из постов видим ту самую фотографию из чата.

Находим клип
Шазамим, находим клип, смотрим. На второй минуте находится кадр с фрагментом нашей фотографии. Значит это именно тот клип и та песня.

Что делать дальше? Изучать информацию, касающуюся произведения, а так как нам нужна точка на карте, то ищем место, где снимался клип.

Но дальнейшее изучение вопроса ни к чему не приводит. Возможно, стоит изучить клип ещё раз клип и поискать зацепки?

Находим локацию
Изучив несколько кадров понимаем, что съемки проходили на территории некого бело-зелёного здания со скошенной крышей. Внимательное изучение дало плоды — засветилось название кафе.

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

Это где-то недалеко от Лос-Анджелеса.

Флаг получен
В итоге у нас есть песня и локация, в которую нам нужно переместиться. Целимся, метимся, перемещаемся по карте именно на те координаты, на которых находится человек на фото.

Но секрет в том, что нам нужно навестись на здание.

И вот, получилось — мы забираем флаг, задача решена!
Решая эту задачу, нужно было просто внимательно читать чат, сосредоточенно смотреть клип и получше гуглить. В целом всё.
#2. «Запреты Роскамбалы»: уровень medium well
Описание задания:
«Вы прогуливаетесь по острову и внезапно встречаете аборигенов. Странное дело: все они выглядят очень грустными. Оказывается, что местные жители давно не видели новых мемов, а смеяться над «Me gusta» и «Oh stop it, you» уже надоело.
Дело в том, что вредная Роскамбала ограничила аборигенам доступ в интернет: завалилась на трансатлантический кабель и пережала его своим толстым брюхом. Победите Роскамбалу и восстановите свободный доступ к мемам.
У вас есть сайт, исходники и устройство инфраструктуры с несколькими нодами: rkb-eu-vw4vauyx.alfactf.ru/.
Тестовые исходники с одной нодой: rkb_f0613d5.tar.gz.
Устройство прод-инфраструктуры с несколькими нодами: production_architecture_chart.png»
Изучаем исходники
Открыв сайт мы видим саму Роскамбалу и её характеристики: 100 ХП и 5 stamina. При этом у нас есть три разных вида оружия: с пятью единицами урона, с десятью и с одной единицей. Каждый раз, когда мы выбираем оружие и наносим удар, тратится одна stamina и вычитается энное количество урона из здоровья. Но даже с самым сильным оружием нам просто не хватает урона, чтобы её добить.

Изучив устройство инфраструктуры, заметим, что пользователи могут обращаться к трём различным серверам: Backend EU, Backend US, Backend SG. Эти сервера взаимодействуют со своими Redis Clusters, которые потом синхронизируются друг с другом.

Посмотрим исходники Backend:
Используется Redis.
У нас есть только три вида оружия и мы не можем обойти это ограничение.
У нас есть несколько функций для взаимодействия с Redis и основная функция с WebSocket.
Как всё работает:
Есть всего два типа запросов:
“new_game”и“attack”, которые могут прилететь на Backend.Когда мы подключаемся к сайту по WebSocket, то создаётся игра. В это время Backend отправляет JSON с ключом
“type”:“game_created”, с ключом“game_id”: game_id,с ’’hp’’: 100и“stamina”: 5.Когда отправляем запрос атаки, то сначала проверяется, что указан верный
game_id. Если нет, то отправляется ошибка.Далее проверяется оружие. Если оружие некорректное, сервер отправляет ошибку, и атака не выполняется.
Затем сервер получает значения stamina (выносливости) и hp (очков здоровья) игрока из базы данных. Сервер проверяет: если hp меньше или равно нулю, он отправляет JSON-ответ с ключом “gameover”, в котором содержится флаг.
Далее сервер проверяет, осталась ли у игрока stamina. Если stamina исчерпана, сервер отправляет JSON с сообщением об окончании игры и предлагает создать новую. Важно: сначала проверяется stamina и HP, а уже потом происходит сама атака.
В бэкенде есть Dockerfile, где указано, что сервер запускается по IP-адресу хоста на порте 8000. Docker Compose перенаправл��ет этот порт 8000 на внешний порт 8101. В файле окружения (environment variables) также виден флаг — в исходниках он зацензурен, поэтому будем извлекать его через exploit.
Как вы понимаете, просто так заспамить Роскамбалу с одного конкретного сервера не получится. Поэтому будем пробовать спамить с трёх: по 5 атак с каждого сервера — это 15 атак в сумме, а после синхронизации (теоретически) мы сможем добить до -50 ХП и получить флаг.
Собственно, план, и реализация
Собственно, exploit состоит в том, что мы используем несколько серверов разных регионов (EU, US, SG), указанных в условии задачи:
Берём базовый URL сервера EU из условия и заменяем «EU» на «US» и «SG», получая три адреса: server-EU:8101, server-US:8101, server-SG:8101.
Подключаемся ко всем трём по WebSocket.
На первом сервере (EU) отправляем запрос на создание новой игры (отправляем только на один, иначе будут созданы три разные игры) и получаем
“game_id”из ответа.Ждём инициализацию игры на бэкенде.
Спамим атаками на каждый из серверов: создаём 50 атак на одно соединение, суммарно — 150.
Создаём атаку в функции attack, передавая туда соединение и
“game_id”по WebSocket.Функция attack отправляет JSON по указанному WebSocket. В JSON указан тип запроса,game_id и оружие, которое наносит больше всего урона.
Затем на все три соединения отправляем по 50 атак с этим же Game ID, чтобы истощить HP и stamina, вызвав условие Gameover с флагом на любом сервере.
Отправляем все 150 атак и ждём, когда придут ответы на все атаки, и получаем результат:
results = await asyncio.gather(*[
asyncio.wait_for(ws.recv(), timeout=0.5) for ws in conns
])
После этого у нас должен полететь JSON в котором есть ключ “is_game_over” с флагом:
for r in results:
data = json.loads(r)
flag = data.get(“is_game_over”)
if isinstance(flag, str) and “alfa{” in flag:
print(flag)
return
Собственно, это как раз и есть проверка.

Запустив exploit, мы получим результат — нужный нам флаг.
Задача решена!
Если хочешь проверить себя снова — у нас для тебя сюрприз. 🚀 Мы уже анонсировали новый CTF-турнир. Лендинг уже в сети, детали — совсем скоро. Жми на ссылку, занимай стартовую позицию и выигрывай денежные призы!
Решайте задачи, играйте в CTF и делайте безопасно.

