company_banner

Как создавался бекенд хакерской игры про уничтожение сервера


    Продолжаем рассказывать, как был устроен наш лазерный квест с уничтожением сервера. Начало в предыдущей статье про разгадку квеста.

    Всего у бекенда игры было 6 архитектурных единиц, которые мы и разберём в этой статье:

    1. Бекенд игровых сущностей, которые отвечали за игровые механизмы
    2. Шина обмена данных бекенда и площадки на VPS
    3. Транслятор из запросов бекенда (игровых элементов) на ардуино и железо на площадке
    4. Ардуино, которая занималась управлением релешками, получала команды с транслятора и делала фактическую работу
    5. Фактические устройства: вентилятор, гирлянды, торшеры и прочее
    6. Фронтенд — сам сайт Сокола, с которого игроки управляли устройствами

    Давайте пройдёмся по каждой из них.

    Бекенд игровых сущностей


    Бекенд был реализован, как spring boot-приложение: оно имело несколько rest-контроллеров, websocket endpoint и сервисы с игровой логикой.

    Контроллеров было всего три:

    • Мегатрон. Через GET-запросы отдавалась актуальная страница Мегатрона: до и после включения питания. Через POST-запрос лазер стрелял.
    • Мапинг тильдовских страниц, чтобы они отдавались по имени страницы. У тильды на экспорт выдаются страницы не с оригинальными названиями, а внутренним ID и информацией по соответствию.
    • Контроллер для капчи, чтобы отдавать псевдо-высокозагружающую сервер капчу.

    Websocket endpoint использовался для управления гаджетами: лампами, гирляндой и буквами. Его выбрали, чтобы синхронно отображать всем игрокам текущий статус устройства: включено оно или выключено, активно или нет, какой цвет буквы сейчас горит на стене. Для того чтобы чуть-чуть усложнить задачу включения лазера, мы накинули авторизацию на гирлянду и лазер с одинаковым логином и пароль admin/admin.

    Игроки могли протестировать его на включении гирлянды и повторить то же самое на лазере.

    Нами была выбрана настолько тривиальная пара логин-пароль для того, чтобы не мучить игроков лишним подбором.

    Чтобы сделать задачу чуть поинтересней, в качестве идентификаторов устройств в комнате использовались object ID из mongodb.

    ObjectId содержит в себе timestamp: два случайных значения, одно из которых берётся на основании идентификатора устройства, а второе на основании pid-процесса, который генерирует его и значение счётчика. Хотелось сделать идентификаторы сгенерированными через равные промежутки времени и с разными pid-процессов, но общим счётчиком, чтобы подбор идентификатора устройства лазера был интереснее. Однако в итоге все запустились с идентификаторами, различающимися только в значении счётчика. Возможно, это сделало этап слишком простым и не требующим анализа структуры идентификаторов objectId.

    Транслятор из запросов бекенда


    Питоновский скрипт, который занимался таймерами и переводил из абстракций игровых в физическую модель. Например «включить торшер» → «включить реле N2».

    Скрипт подключался к очереди RabbitMQ и передавал запросы из очереди на Ардуино. Также на нём была реализована логика параллельного включения света: вместе с некоторыми устройствами на них включался свет, например, при первичной подаче питания на Мегатрон, он подсвечивался сценическим светом. Дизайн света для кинематографичности всей сцены — отдельная история про большую работу нашего сопродюсера проекта и художника-постановщика Ильи Серова, и про неё мы расскажем в отдельном посте.

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

    Как была устроена логика генерации токена мегатрона


    Тестовый выстрел


    Каждые 25 секунд генерировался новый токен, его можно было использовать, чтобы включить лазер на 10 секунд на мощности 10/255. Ссылка на гитхаб с кодом Мегатрона.

    Затем лазер охлаждался 1 минуту — в это время он был недоступен и не принимал новые запросы на выстрел.

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

    Для генерации токена использовался алгоритм хеширования MD5. И схема получалась MD5 от MD5 + счётчик + секрет для боевого токена и без секрета для тестового.

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

    Боевой выстрел


    Боевой режим Мегатрона — это 100% мощность лазера в 3 Вт. Этого вполне достаточно на 2 минуты, чтобы пережечь верёвку, которая держала гирю, чтобы разбить аквариум и залить сервер водой.

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

    Зная эти данные, можно было перебрать 2 последних символа соли и фактически выяснить, что для неё использовались числа из Lost, переведённые в 16-ичную систему.

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

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

    Сервис взаимодействия с капчей


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



    Сервис подсчитывал, что отображать в мониторинге как текущую нагрузку: температуру и CPU Fan. Метрики передавались в timebase database и отрисовывались графаной.

    Если за последнее 5 секунд поступало более 50 запросов на отображение капчи, то нагрузка росла на фикс + рандомное количество шагов. Расчёт был на то, чтобы 100% нагрузку можно было получить за две минуты.

    В самом деле логики в сервисе было больше, чем отобразилось в конечной игре: мы поставили монитор таким образом, что было видно только вращение CPU Fan.

    Графану в начале квеста хотели оставить доступной с сайта Сокола. Но в ней были также springboot-метрики по отчёту бекенд-приложения, которые мы не успевали вычистить, поэтому решили закрыть доступ к ней. И правильно — ещё в начале квеста некоторые игроки догадались, что приложение написано на фреймворке springboot и даже откопали название некоторых сервисов.

    Хостинг и шина обмена данных


    Инструмент передачи информации с бекенда на площадку, VPS-сервер на котором было запущено RabbitMQ.

    Бекенд и шина данных держались на наших VPS. Его мощность была сопоставима с компьютером, который вы видели на экране: 2-х ядерная VPS-ка с двумя гигабайтами оперативной памяти. Тариф брали за ресурсы, поскольку пиковая нагрузка планировалась всего несколько дней — так и поступают наши клиенты, которые планируют нагружать VPS на короткий срок. Потом оказалось, что нагрузка выше, чем мы предполагали, и фиксированный тариф был бы выгоднее. Будете делать квест — выбирайте тарифы линейки турбо.

    Чтобы защитить сервер от DDoSa, мы использовали Cloudflare.

    Стоит сказать, что VPS с честью выдержала всё.

    Ардуино, которая занималась управлением релешками, получала команды с транслятора и делала фактическую работу


    Это больше тема следующей статью про хардварную часть проекта: бекенд просто присылал запросы включить конкретное реле. Так вышло, что бекенд знал почти все сущности и запросы с него, выглядели как «включи эту сущность». Мы делали это для раннего тестирования площадки (пока ещё не собрали все Ардуино и реле), в итоге так всё и оставили.

    Фронтенд


    Сайт мы быстро создали на тильде, это заняло один рабочий день и сэкономило нам тысяч 30 бюджета.

    Изначально мы думали просто экспортнуть сайт и накинуть нехватающую нам логику, но нарвались на terms of use, которые нам это запрещали.

    Мы не были готовы нарушать лицензию, поэтому было два варианта: сверстать всё самим или напрямую связаться с Тильдой, рассказать о проекте и попросить разрешения менять код.

    Мы выбрали второй вариант и они не просто пошли нам навстречу, а даже подарили нам год бесплатного бизнес-аккаунта, за что мы им очень благодарны. Было очень неловко показывать им дизайн сайта Сокола.

    В итоге мы прикрутили к фронтенду js-логику на отправку запросов на элементарные устройства, немного поменяли стили кнопок включения и выключения игровых элементов.

    Дизайн сайта


    История поисков, которая стоит отдельной главы.

    Мы хотели создать не просто старомодный сайт, а абсолютно тошнотворный, нарушающий все базовые правила дизайна. При этом было важно сохранить правдоподобность: он должен был не ломать ЛОР истории, демонстрировать претенциозность автора и игроки должны были бы поверить в то, что такой сайт может существовать и даже приводить клиентов. И привёл! Пока шла игра, нам дважды обращались за созданием сайтов.

    Сначала дизайн делала я сама, стараясь воткнуть побольше гифок и блестящих элементов. Но мой муж-дизайнер с 10-летним стажем, заглянув через плечо, забраковал его как «слишком хороший». Чтобы нарушать правила дизайна, надо их знать.



    Существует несколько комбинаций цветов, которые вызывают стойкое чувство омерзения: зелёный и красный одинаковой сочности, серый и розовый, синий плюс коричневый. В итоге мы остановились на сочетании красного и зелёного, как базовых цветов, добавили гифок с котиком и выбрали на фотостоке 3-4 фотографии самого Соколова. У меня было только несколько требований: мужчина средних лет, в плохо сидящем костюме на пару размеров больше и в позе «профессиональная фотосессия в студии». Для теста показывали её друзьям и спрашивали «ну как тебе?».

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

    Фактические устройства


    Вентиляторы и свет монтировали через твердотельные реле, чтобы они включались не сразу на полную мощность — чтобы нарастание мощности происходило параллельно с мониторингом.

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

    Stay tuned!

    Остальные статьи про квест с уничтожением сервера


    RUVDS.com
    VDS/VPS-хостинг. Скидка 10% по коду HABR

    Комментарии 11

      0
      Стоит сказать, что VPS с честью выдержала всё.

      Не считая инцидента в первый вечер, когда после продолжительно «DDoSa» все страницы стали отдавать ошибку spring'a, а затем и вовсе сервер стал недоступен и через пару минут возродился уже полностью под Cloudflare, включая все поддомены :)
        +1

        Москва не сразу строилась!

        +1
        Не подскажите, с юридической точки зрения, насколько законной была данная акция? Разве не получается, что победитель де-юре получает возможность познакомиться с российской фемидой?
          0
          Нет, все было законно.
          Не вполне поняла ваш вопрос, вы не могли подробнее раскрыть его?
            0
            Допустим, кто-то гениальный пользовался не вашими подсказками, а уязвимостями в вашем по, cloudflare, os и т.п. В рамках конкурса все отлично, вам реклама, победителю деньги. Но как на такое отреагирует тов майор, имея в кармане статью 272 УК РФ? Ведь уголовное дело заводится даже при отсутствии пострадавших. Сразу говорю, я не юрист и никогда не занимался пентестингом.
              0
              Попросить перевод в Monero.
                0

                Так получается, что пентест тоже незаконен, пострадавших нет, всё с согласия владельца...

                  0
                  1. Допустим, вас побили на улице, но вы на самом деле были не против. Какое наказание вам грозит за отказ описать нападавшего? Между тем в сфере айти без логов жертвы шансы найти преступника нулевые. Яровая не поможет, так как легитимные запросы к серверу тоже шли, а какие запросы привели к взлому может понять только владелец сервиса.

                  2. В случае с законом неприкосновенности жилища первичным является желание владельца жилища пускать кого-то или не пускать. Иначе бы можно было бы подставить любого, просто открыв замок, но заставив своими руками открыть дверь. Ведь прецеденты, когда судили людей, вломившихся в незапертую чужую дверь были. Не вижу причин, почему проникновение в информационную систему принципиально отличается. Закон не знает разницы между «отсутствием пароля», «пароль приходит по СМС», «пароль содержится в общедоступном словаре», «в пароле нужно угадать последнюю букву». Лёгкость/сложность доступа к системе является чисто субъективным понятием и само по себе ничего не говорит.
                  0
                  Если вы видите незапертую дверь в чужую квартиру и заходите в неё, то это потенциальное нарушение неприкосновенности жилища с уголовной ответственностью. Если вы приходите в гости к другу и он после звонка в домофон открывает замок на входной двери, но не дожидается вас и вы своими руками открываете дверь, то нарушения закона нет. Технические детали закону совершенно не важны — закрыта ли дверь, какого класса защиты на ней замок, его техническое состояние и т. д. Единственное, что важно — хотел ли видеть владелец жилища гостей или нет.

                  Я думаю, тут всё то же самое. Если владелец ресурса на самом деле хотел видеть гостей, то совершенно не важно, что технически доступ был затруднён. Ведь «затруднение» доступа очень субъективное понятие. В законе нет чёткой позиции, что «пароль высланный на почту/СМС» это ещё ок, а «пароль, последние две буквы которого нужно угадать» — уже не ок.

                  Плюс дела в сфере информационной безопасности невозможно расследовать без активного содействия потерпевшего (предоставления логов, IP-адресов атакующих и т. д.). Я сомневаюсь, что есть закон, который бы обязывал потерпевшего содействовать следствию. Могут быть вопросы, если потерпевший недееспособен, но в случае юрлица это неприменимо.
              0
              Хорошая подготовка backend'a. Спасибо за интересную игру)

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

            Самое читаемое