TRON, Aptos и BSC в одной цепочке заражения: расследование JavaScript-загрузчика, связанного с ChainVeil

в 9:28, , рубрики: investigation, threat intelligence

Введение

16 июня 2026 года исследователи Checkmarx изучили кампанию ChainVeil, использовавшую npm-пакеты для распространения многоступенчатых JavaScript-загрузчиков. И как раз недавно одному из моих знакомых (не)посчастливилось получить один из ранее неизвестных экземпляров.

Репозиторий с AI-агентом ему посоветовал... AI-агент. Да, отправной точкой заражения стала рекомендация от AI-помощника, а после клонирования репозитория и локального запуска антивирус прервал выполнение и классифицировал обнаруженный объект как:

Trojan.Script.Agent.mlgj

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

Начало расследования

Первым делом я решил заглянуть в сам репозиторий: узнать кто автор, когда и кем был внедрен код. Но оказалось, что автор не новичок на платформе: 320 репозиториев, 50 подписчиков и много данных в поле О себе. Но об истории Git'а и самом репозитории чуть позднее. Перейдем непосредственно к коду!

Анализ JavaScript-кода

В самом начале я упомянул кампанию Chainveil не просто так. Если присмотреться к коду, можно заметить первые совпадения артефактов кампании.

Во-первых, идентификатор: у кампании в начале обфусцированного вредоносного блока кода присутствовало объявление глобального значения:

global['!']='9-0554-3'

В ходе анализа, эта строка приняла следующий вид:

A9-0554-3

Если вы прочитаете статью Checkmarx указанную выше, то в самом конце сможете найти обнаруженные ими идентификаторы кампании, но все они имеют первый блок A6, в то время как я наткнулся на A9. Также стоит обратить внимание: источник не является npm-пакетом, это самостоятельный ИИ-инструмент на Python использующий JS для Web части приложения.

Продолжив анализ я еще больше убедился в сходстве с ChainVeil, образец использовал абсолютно те же самые техники, что указаны в статье от Checkmarx.

  • Многоступенчатая обфускация JS-кода.

  • Использование данных TRON, Aptos и BSC транзакций в качестве источника полезной нагрузки.

  • Адреса C2-инфраструктуры полностью совпали с теми, что опубликовали Checkmarx.

Код является нестандартным по архитектуре работы loader'ом. Он использует несколько blockchain сервисов для получения полезной нагрузки.

  • Через TRON получает последнюю исходящую транзакцию аккаунта злоумышленника.

  • Использует Aptos, если через TRON получить данные не удалось

  • Извлеченное значение после UTF-8 декодирования и разворота строки используется как идентификатор транзакции BSC, откуда, из поля input, извлекается закодированная строка.

Извлеченная из данных BSC транзакции строка проходит через несколько операций:

  • Извлечение необходимых частей по разделителю ?.?

  • XOR декодирование с ключом, находящимся в коде

Для деобфускации строк код использует функцию перестановки _$af163278 с фиксированным значением seed, так что мне удалось полностью восстановить используемые строки:

[0]: "r"
[1]: "_V"
[2]: "A"
[3]: "!"
[4]: "end"
[5]: "error"
[6]: "on"
[7]: ""
[8]: "data"
[9]: "parse"
[10]: "JSON"
[11]: "get"
[12]: "https"
[13]: "Promise"
[14]: "2.0"
[15]: "stringify"
[16]: "POST"
[17]: "request"
[18]: "write"
[19]: "join"
[20]: "reverse"
[21]: "split"
[22]: "utf8"
[23]: "toString"
[24]: "raw_data"
[25]: "/transactions?only_confirmed=true&only_from=true&limit=1"
[26]: "hex"
[27]: "from"
[28]: "Buffer"
[29]: "arguments"
[30]: "payload"
[31]: "/transactions?limit=1"
[32]: "?.?"
[33]: "substring"
[34]: "input"
[35]: "result"
[36]: "eth_getTransactionByHash"
[37]: "bsc-dataseed[.]binance[.]org"
[38]: "bsc-rpc[.]publicnode[.]com"
[39]: "length"
[40]: "charCodeAt"
[41]: "fromCharCode"
[42]: "String"
[43]: "getTime"
[44]: "Date"
[45]: "_p_t"
[46]: "2[...C"
[47]: "TMfK...7mAP"
[48]: "0xbe03...11e"
[49]: "m...]"
[50]: "TXf...zcG"
[51]: "0x3f0...ce3"
[52]: "node"
[53]: "-e"
[54]: "';"
[55]: "ignore"
[56]: "spawn"
[57]: "child_process"

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

Как вредоносный код оказался в репозитории

Чтобы понять, как вредоносный файл попал к конечному пользователю, пришлось исследовать историю Git-коммитов зараженного репозитория inbox-ai.

На первый взгляд, владелец аккаунта выглядит как обычный разработчик: более 300 репозиториев и активный профиль. Однако история изменений ключевого файла navigation.js раскрывает то, как именно вредоносный код попал в кодовую базу.

Первое появление файла зафиксировано в коммите a3940ca (ноябрь 2025 года):

commit a3940ca
Author: Lite Object <...@gmail.com>
Date:   Sat Nov 8 09:26:42 2025 -0600

    feat: Implement user preferences management with CRUD operations
    and integrate into settings page

В этот момент navigation.js был добавлен как чистый, легитимный скрипт. Вредоносный код появился значительно позже - 29 марта 2026 года в процессе слияния веток в коммите a40301a:

Commit: a40301a8a5b3a53d13c6c784b72ef5e1a192669e
Parents: 080ef0f, 5499bda
Message: Merge pull request #20 from .../ui-ux-improvements

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

Первоначальный осмотр файла, вредоносного кода не видно

Первоначальный осмотр файла, вредоносного кода не видно

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

Найденный блок вредоносного кода

Найденный блок вредоносного кода

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

Минимальное представление момента заражения репозитория

Минимальное представление момента заражения репозитория

Таким же образом вредоносный код был внедрен в файл calendar-rail.js 30 марта 2026 года 03:44:18 UTC.

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

Анализ инфраструктуры кампании

При самостоятельном анализе связанного BSC-адреса мне удалось обнаружить транзакцию от 7 февраля 2025 года, содержащую данные, которые по структуре соответствуют наблюдаемой цепочке доставки. Если данная транзакция действительно относится к той же инфраструктуре, это сдвигает предполагаемую дату начала ее активности еще дальше - за пределы периода, описанного в публичном исследовании Checkmarx.

Анализ временных интервалов между транзакциями показал, что полезная нагрузка обновлялась регулярно - примерно каждые 2-3 дня. При этом в отдельные периоды наблюдались серии быстрых обновлений, когда несколько новых версий публиковались в течение нескольких минут. Подобное поведение может свидетельствовать об использовании автоматизированного механизма публикации полезной нагрузки.

Весной 2026 года активность инфраструктуры резко возрастает. Появляются новые кошельки, увеличивается частота ротации полезной нагрузки, а вскоре после этого начинается распространение вредоносных npm-пакетов.

Наиболее интересным выглядит временное совпадение между расширением инфраструктуры кампании и заражением исследуемого репозитория.

Если проследить TRON и BSC транзакции, можно заметить следующее:

Событие

Время

Merge-коммит a40301a

29.03.2026

Коммит 42fa0c5

30.03.26

TRON-транзакция

31.03.2026 14:20:57 UTC

BSC-транзакция

31.03.2026 14:20:42 UTC

Появление первых npm-пакетов

18.05.26

Важной особенностью является то, что BSC-транзакция произошла на 15 секунд раньше TRON-транзакции. Это объясняется архитектурой загрузчика. Из данных TRON-транзакции извлекается идентификатор транзакции BSC, содержащей следующую стадию полезной нагрузки.

Фактически злоумышленники использовали TRON в качестве промежуточного уровня, а BSC - в качестве хранилища следующего этапа загрузчика.

Вредоносный код был внедрен в репозиторий 29 марта 2026 года, тогда как первые npm-пакеты, описанные Checkmarx, появились только 18 мая 2026 года. Это может свидетельствовать о том, что npm никогда не был единственным каналом распространения ChainVeil.

Заключение

Таким образом, исследуемый образец демонстрирует практически полное совпадение с ранее описанной архитектурой ChainVeil: от многоступенчатой обфускации и блокчейн-механизма доставки полезной нагрузки до используемой C2-инфраструктуры и конечного RAT-компонента. Checkmarx также описывает именно трехблокчейнную схему TRON/Aptos - BSC и связывает ее с кампанией SuccessKey.

При этом исследованный экземпляр отличается от известных npm-образцов как минимум каналом доставки и идентификатором кампании: вместо A6-* используется A9-0554-3. В публичном исследовании Checkmarx именно A6-* используется как общий маркер семейства известных npm-пакетов.

История Git позволяет установить точку появления вредоносного кода в исследуемом проекте и проследить его дальнейшее распространение в другие JavaScript-файлы. Однако она не позволяет установить, кто именно контролировал учетную запись разработчика в момент внесения изменений.

Автор: ChikoiSan

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js