С конца 2025 года три серверных проекта небольшой студии разработки заражены вредоносным ПО, которое распространяют связываемые с КНДР атакующие. Вместо исполняемых файлов они подкладывают в репозитории (хранилища исходного кода) поддельные файлы шрифтов. Снаружи это обычная часть проекта, внутри - скрытый код. Он запускается сам, как только разработчик открывает папку в редакторе VS Code. Открытия достаточно, чтобы заражённая машина начала переписывать проекты дальше. В руках злоумышленников оказываются исходный код, учётные данные и доступ к рабочим сервисам.
Описание
Схема опирается на доверие к среде разработки. В служебной папке проекта появляется конфигурация задач редактора: она срабатывает при открытии рабочей папки и запускает файл шрифта как программу. В других случаях полезную нагрузку дописывают в конец файла настроек линтера (утилиты автоматической проверки кода) - он всё равно исполняется при проверке. Третий путь ведёт через конфигурацию работы с базой данных, которая подтягивает следующий этап с внешнего адреса. Такие файлы разработчики читают редко.
По данным нового исследования, за последние недели механизм переделали, чтобы затруднить поиск. Новые версии принудительно отправляются в уже заражённые репозитории. В одном случае изменилась только цифра в имени поддельного шрифта: 400 сменилась на 500, и поиск по прежнему номеру перестал находить файл. В более позднем варианте подделка получила имя настоящего файла из набора Font Awesome (популярный набор шрифтов и иконок) и легла рядом с подлинным шрифтом. Постороннего в папке теперь не видно, а сама подделка почти в семь раз крупнее предыдущей версии. Кампанию отслеживают под именем PolinRider.
Перебор вариантов идёт параллельно. Разные сборки работают одновременно, и атакующие следят, какая из них доходит до большего числа машин. Полезная нагрузка отправляет на управляющий сервер метку сборки, по которой видно, какой вариант сработал. Адрес сервера злоумышленники достают из последней транзакции фиксированного криптокошелька в блокчейне, поэтому блокировка доменов почти не мешает кампании.
Хватает одного заражённого участника. Дальше вредоносное ПО на его компьютере само переписывает коммиты (зафиксированные версии изменений) во всех репозиториях, к которым у разработчика есть доступ. Коллега открывает проект, заражается и повторяет то же самое. За восемь месяцев в трёх проектах отметились 19 аккаунтов, и 18 из них действовали по одному шаблону. Один компьютер способен заражать всю команду месяцами.
По рабочей гипотезе, первый участник заразился в декабре или январе через приглашение на собеседование. Кандидату предлагали склонировать и запустить тестовый проект - обычный приём северокорейских группировок, которые работают под видом рекрутеров. Дальше заражение перешло на остальных участников организации без участия операторов.
История трёх репозиториев, заражённых почти весь 2026 год, показывает масштаб. Через них прошли все поколения поддельного шрифта. Журнал активности GitHub фиксирует каждую отправку на серверах компании, включая принудительные перезаписи истории, и с заражённой машины его не подделать. За восемь месяцев в трёх репозиториях накопилось 853 таких перезаписи.
Команда трижды пыталась убрать вредоносный код. Каждый раз он возвращался: его отправляла машина другого участника, часто под тем же сообщением коммита, что использовала сама уборка. Один из коммитов с заголовком об удалении вредоносного кода содержит этот код до сих пор. Удалённую строку потом переписывали больше сотни раз.
Инструмент на заражённых машинах действует однообразно. Он берёт последний коммит ветки и оставляет прежними имя автора, почту, дату и текст сообщения. Содержимое инструмент заменяет своим и за пару секунд рассылает результат по всем веткам. Из-за этого сортировка истории по дате показывает май для нагрузки, пришедшей в сентябре. Имена авторов переносятся из перехваченных коммитов и не совпадают с аккаунтом, который вёл перезапись. Часовые пояса в служебных полях тоже чужие.
Активность нарастает. Если в июле зафиксировали 18 принудительных перезаписей, то в августе - 202, а в сентябре - 285. В служебном списке, который исключает временные файлы из-под контроля версий, остаются строки с именами временных файлов автоматической отправки. В репозиторий они не попадают, зато выдают заражённый компьютер ещё до того, как в проект проникнет сама нагрузка.
Привычные проверки не срабатывают. Поиск по имени шрифта с номером 400 пропускает варианты 500 и 900. Поиск по типовой папке тоже не помогает: в новом варианте подделка лежит в разных местах в каждом проекте. Метки сборки меняются постоянно, а в одном из вариантов вовсе закодированы. Чтение истории коммитов обманывает сильнее всего: вредоносный коммит несёт имя и дату реального разработчика.
Работают проверки, которые смотрят на содержимое, а не на имя. Настоящий шрифт распознаётся по внутреннему формату, подделка оказывается обычным текстом. Файл задач лучше читать инструментом, который терпит комментарии и лишние запятые: ошибка разбора - повод присмотреться, а не повод пропустить файл. Автоматический запуск задач при открытии папки разумно отключить. Защита ветвей блокирует принудительные перезаписи, а требование подписанных коммитов обесценивает подмену авторства.
Убрать нагрузку из репозитория, не вылечив машины, не получится. Пока у кого-то из участников работает инструмент автоматической отправки, код вернётся при следующем открытии проекта. Сначала ищут заражённые компьютеры, и только потом откатывают изменения. Регулярные проверки содержимого шрифтов, отключённый автозапуск задач и защита всех значимых ветвей снижают риск повторов.
Индикаторы компрометации
URL
- github.com/Binary-Mindz/agimtula_server
- github.com/Binary-Mindz/diegovillariber-backend
- github.com/Binary-Mindz/rk-dodani-backend
- github.com/GulamRosul/Curved-navigation-bar
- https://260120.vercel.app/settings/linux?flag=9-test
- https://260120.vercel.app/settings/win?flag=9-test
Ethereum wallet
- 0xa322e5f3d311d3080e6f0121063e9adc2490ef1a
SHA256
- 1a21bad1df69b51efebfd2fae849ac27722b0dfac7b1cb459cf81d38b36f9705
- 91e334655d31f84963e6b9369d41698f51b00d7a203cf314ceba01780e6f337c
- fbf0e36dd7eb1e70fc4868a8d0d0cd8f6c80753241cd88a3518f40e9a67d232e