JavacDoor — вредоносная программа Maven, запускающаяся во время компиляции.

JavacDoor: артефакт Maven, который выполняется во время компиляции, а не установки.

TL, д-р

Артефакт Maven Central, опубликованный как io.github.davidtimur:c2-lab Было выпущено девять релизов, восемь из которых выполняли полезную нагрузку удаленного доступа во время компиляции любого нижестоящего проекта, который поместил JAR-файл в свой путь обработки аннотаций. При этом не требовалось импортировать его в код приложения. Не требовалось ссылаться на него ни в одной строке исходного кода. Отсутствовал скрипт установки, не было эквивалента для postinstall, и не было никаких механизмов отслеживания жизненного цикла, которые инструменты могли бы проверить.

Вектор выполнения представляет собой единственный 46-байтовый файл внутри JAR-архива: регистрация поставщика услуг Java, которая указывает класс, реализующий интерфейс. javax.annotation.processing.ProcessorКомпилятор Java автоматически обнаруживает такие регистрации. После обнаружения, javac Он создает экземпляр класса и запускает его в рамках обычной компиляции. Это означает, что средой выполнения полезной нагрузки является сборочная машина, которая в данный момент выполняет свою основную задачу.

За девять релизов канал управления и контроля перестраивался трижды: сначала использовался URL-адрес обратного вызова, предоставляемый оператором, затем обратная оболочка через TCP-туннель ngrok, а затем HTTP-канал опроса, пути которого изменялись ещё дважды. В финальном релизе в качестве менеджера доверия TLS по умолчанию был установлен неактивный менеджер доверия TLS. принятие любого сертификата на время компиляции.

Артефакт содержал в своем файле POM слова "C2 Lab Payload" и лицензию MIT. Он был доступен в Maven Central в течение всего периода нашего наблюдения. с тех пор был демонтирован вместе со всем своим имуществом. io.github.davidtimur группа.

Анатомия: компилятор как исполнительный механизм

В Java существует фреймворк обработки аннотаций, позволяющий библиотекам генерировать код во время компиляции — механизм, лежащий в основе Lombok, Dagger и длинного списка ORM и инструментов сериализации. Обработчик объявляет о себе с помощью текстового файла внутри JAR-архива:

META-INF/services/javax.annotation.processing.Processor

Содержимое этого файла во всех затронутых версиях, дословно и полностью:

io.github.davidtimur.c2lab.C2Processor

Файл имеет идентичный размер в байтах во всех версиях с 1.0.1 по 1.0.8, md5. 7d2a08a5c8869a47eea9fa62487dfbe4. В версии 1.0.0 этого нет.

После появления Javac При запуске он сканирует путь к обработчику аннотаций на наличие этих сервисных записей и загружает найденное. В компилируемом проекте не требуется указывать на обработчик, аннотировать что-либо или настраивать что-либо. Достаточно наличия в пути. Именно это свойство отличает JavacDoor от шаблонов цепочки поставок, на которых построено большинство инструментов:

шаблонВызыватьВидно как
npm install hooknpm installscripts.postinstall в манифесте
полезная нагрузка во время импорта Pythonпервый импорт модуляоператор уровня модуля в исходном коде
JavacDoorjavac в любом проекте, реализуемом на последующих этапах производства.имя файла регистрации сервиса

Хук установки — это объявление в манифесте, а манифест — это первое, что кто-либо читает. Полезная нагрузка во время импорта, по крайней мере, находится в читаемом исходном коде. Регистрация сервиса не является ни тем, ни другим: это имя файла плюс одна строка, указывающая на класс, а поведение находится в скомпилированном байт-коде в соседнем каталоге.

Сам класс полезной нагрузки выполняет разведку хоста путем отправки командной строки. Пулы констант скомпилированных классов содержат / Бен / ш, Whoami, uname -a, PWD в пути Unix и Список задач по пути Windows, вместе с redirectErrorStream для объединения вывода ошибок дочернего процесса с захваченным потоком. Два шаблона JSON передают результаты с хоста — регистрационный маяк:

{"host":"%s","os":"%s","user":"%s","dir":"%s"}

заполнено из хоста командование и имя_ос., user.name и пользователь.dir Свойства системы и функция обратного вызова для получения результата:

{"version":"%s","host":"%s","time":"%s","output":"%s"}

Индикаторы выполнения, оставленные в собственном выводе компилятора, на удивление откровенны: [C2] Выполнение на этапе компиляции завершено, [C2] отправлен обратный вызов → HTTP , [C2] оболочка подключена к .

Девять релизов, три поколения C2

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

ReleaseВыполняется автоматически при компиляции.Канал
1.0.0нет (нет файла службы)URL-адрес обратного вызова, предоставленный оператором.
1.0.1да (введен вектор)URL-адрес обратного вызова, предоставленный оператором.
1.0.2ДаОбратная оболочка, TCP-туннель ngrok
1.0.3ДаHTTP-канал, /register /cmd /out
1.0.4ДаHTTP-канал, /register /cmd /out
1.0.5ДаHTTP-канал, /register /poll /out
1.0.6ДаHTTP-канал, /register /poll /out
1.0.7ДаHTTP-канал, /register /cmd
1.0.8ДаПроверка HTTP-канала и TLS отключена.

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

Вектор достигает значения 1.0.1, а не 1.0.2. В версии 1.0.0 сохранена та же логика разведки и обратных вызовов, но отсутствует файл сервиса и импорты для обработки аннотаций; он запускается только в том случае, если его кто-то вызывает. Начиная с версии 1.0.1, файл сервиса присутствует, и скомпилированные классы импортируются. javax.annotation.processing.SupportedSourceVersionЛюбая оценка, сравнивающая два выпущенных релиза — 1.0.0 и 1.0.2 — правильно заключает, что что-то изменилось, но неверно определяет, где именно.

Обратная оболочка существует ровно в одном релизе. В версии 1.0.2 содержится 0.tcp.ngrok[.]ioлоглайн [C2] оболочка подключена к 0.tcp.ngrok[.]io:19823интерактивный баннер c2-оболочкаи терминатор кадра __КОНЕЦ__В версиях 1.0.3 и далее таких ссылок нет, и вместо этого используется HTTPS-хост. Запрос на удаление, содержащий только отмеченные релизы, указывал бы на неработающую TCP-конечную точку, оставляя при этом без упоминания работающий HTTP-канал, присутствующий в шести более поздних релизах.

В финальной версии удалена проверка транспортировки. В версии 1.0.8 добавлен класс, реализующий интерфейс. javax.net.ssl.X509TrustManager методы проверки сертификатов которых ничего не делают, — это всегда верификатор имени хоста, зарегистрированный через setDefaultHostnameVerifierИ trustAll Эта процедура устанавливает оба сертификата в качестве сертификатов по умолчанию для JVM. В результате, на протяжении всей оставшейся части компиляции JVM принимает любой сертификат с любого хоста — не только для собственного трафика полезной нагрузки, но и для всего остального, что сборка будет делать по TLS впоследствии.

Во всех шести версиях, использующих HTTP-канал, перед каждым из хостов находится один и тот же хост: tableful-fervor-crazed.ngrok-free[.]devКаждый запрос содержит заголовок. ngrok-skip-browser-warning, которая подавляет промежуточные страницы, которые бесплатные туннели ngrok предоставляют браузерам. Наборы путей меняются в зависимости от версии — /вне исчезает в версии 1.0.7, и / cmd чередуется с /голосование — но хозяин дома никогда не меняется.

Свидетельство самоисключения

Начиная с версии 1.0.1, каждый POM-файл устанавливает аргумент компилятора для собственной сборки артефакта:

-proc:none

Этот флаг отключает обработку аннотаций. Его действие здесь не имеет значения.cise: когда проект, содержащий процессор, компилируется, процессор не запускается.

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

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

Мы отмечаем эффект и на этом останавливаемся. Ничто в артефактах не указывает на причину установки флага.

Ещё один элемент метаданных заслуживает упоминания, в основном для того, чтобы от него избавиться. В файле POM проект называется «C2 Lab Payload», описывается как «артефакт полезной нагрузки лаборатории C2» и лицензируется по лицензии MIT. Подобная самомаркировка иногда используется в качестве доказательства того, что пакет является результатом исследовательской работы.cisЭто не реальная угроза, и иногда такое толкование верно — объявленный «канарейка» без доступной инфраструктуры — это совсем другой объект. Здесь это не применимо. Функциональный имплант, опубликованный в общедоступном репозитории, доступном любому потребителю, с исходящей инфраструктурой, которая была перестроена трижды за девять релизов, является активной возможностью независимо от того, как он называется в метаданных. Название в POM ничего не меняет в том, что происходит на машине, которая компилирует его.

Индикаторы для сборочных машин

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

В кэше JAR-файла или локального репозитория

  • META-INF/services/javax.annotation.processing.Processor именования io.github.davidtimur.c2lab.C2Processor
  • Файл службы md5 7d2a08a5c8869a47eea9fa62487dfbe4
  • Классы в рамках io/github/davidtimur/c2lab/: C2Processor, C2Task, Taskа в разделе 1.0.8 внутренний класс Task$1

Встроенный вывод

  • [C2] compile-time execution complete
  • [C2] callback sent → HTTP
  • [C2] callback failed:
  • [C2] shell connected to
  • Интерактивный баннер c2-shell; терминатор кадра __END__

В процессе телеметрии

  • javac как родитель /bin/sh -c (Unix) или интерпретатор команд Windows
  • Дочерние команды whoami, uname -a, pwd (Unix) или tasklist (Windows) создано на этапе компиляции.

В сетевой телеметрии

  • Исходящий TCP-пакет 0.tcp.ngrok[.]io:19823 (версия 1.0.2)
  • HTTPS к tableful-fervor-crazed.ngrok-free[.]devпути /register, /cmd, /poll, /out (версии с 1.0.3 по 1.0.8)
  • Заголовок запроса ngrok-skip-browser-warning: true
  • Запросы на соответствие тел {"host":...,"os":...,"user":...,"dir":...} or {"version":...,"host":...,"time":...,"output":...}

В конфигурации

  • Переменные среды CALLBACK, CALLBACK_URL; свойство системы callback.url

Метаданные издателя

  • В группе io.github.davidtimurадрес издателя davudboi999@gmail[.]comключ подписи E520C345EF94423D

Сборка, работающая на версии 1.0.8, требует дополнительной проверки. Поскольку в этой версии в качестве стандарта для всей JVM устанавливается разрешительный менеджер доверия, любое TLS-соединение, установленное позже в той же JVM — разрешение зависимостей, загрузка артефактов, этап развертывания — проходит без проверки сертификата. Трафик из этого окна не следует считать аутентифицированным.

Почему для скомпилированных артефактов требуется разное сканирование?

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

Первое предположение заключается в том, что опасный код заявляет о себе в манифесте. Значительная часть инструментов управления цепочками поставок построена вокруг жизненного цикла продукта. hooksПотому что для npm и PyPI обычно именно там происходит основная работа. У JavacDoor нет хука. Его триггером является файл регистрации сервиса, имя которого — Java-интерфейс, а содержимое — имя класса. Чтобы перехватить это статически, нужно обрабатывать META-INF/services/javax.annotation.processing.Processor в качестве самостоятельной точки входа в процесс выполнения, наравне с послеустановка скрипт — а затем проследите за именованным классом в байт-код. Экосистемы имеют свои собственные механизмы автоматического обнаружения подобной структуры, и каждая из них является точкой входа, независимо от того, распознает ли ее инструментарий как таковую.

Второе предположение заключается в том, что строки находятся в исходных файлах. В случае JAR-файла конечные точки, команды оболочки, JSON-шаблоны и маркеры логов находятся в постоянных пулах. .учебный класс файлы. Инструменты, которые обрабатывают текст с помощью grep, ничего не находят — не потому, что строки обфусцированы, а потому, что они находятся в структурированном бинарном контейнере, который не может быть проанализирован обычным текстовым сканированием. Все сетевые индикаторы в этом посте получены путем анализа пула констант. Один из них — это полностью сформированный https:// URL-адрес находится на виду внутри файла класса; текстовое сканирование содержимого JAR-файла все равно не выявит его. Здесь нет кодировки, которую нужно обойти, есть только формат контейнера для чтения.

Обе проблемы имеют одинаковую форму: формат артефакта рассматривался как набор файлов, а не как структура с определенной семантикой. Решение не отличается привлекательностью — нужно проанализировать контейнер, перечислить точки входа автоматического обнаружения экосистемы и проследить их до скомпилированного кода. Что касается векторов времени сборки, то третья проверка оказывается недорогой и на удивление информативной: сравнить, что артефакт делает с потребителями, с тем, от чего он себя исключает. Артефакт, который регистрирует обработчик времени компиляции и одновременно отключает обработку времени компиляции для своей собственной сборки, уже в двух строках простого текста POM-файла сообщает вам кое-что о себе.

Для команд, использующих артефакты Maven сегодня, следует принять три практических меры:

  • Рассматривайте путь обработки аннотаций как границу выполнения. Зависимости, которые туда попадают, выполняют код в вашей сборке. В тех случаях, когда сборка не требует обработки аннотаций, -proc:none В целях защиты это так же полезно, как, по-видимому, было здесь локально; там, где это необходимо, набор процессоров фиксируется явно, а не наследуется из пути к классам компиляции.
  • Ведение журнала дочерних процессов компилятора. Этап компиляции, запускающий командную оболочку, является аномальным в большинстве проектов и легко выявляется.
  • Не следует рассматривать метаданные как свидетельство. Использование слов «Lab», «test», «payload» и «PoC» в названии или описании пакета не является ограничением области действия. Ограничением является доступность и поведение.

Артефакт и вся его группа были изъяты. Специалист Централизованная служба после нашего отчета сообщает, что и путь к артефакту, и путь к группе теперь возвращают ошибку 404, а центральный индекс не сообщает о наличии совпадающих координат. Это закрывает данный артефакт. Однако это не закрывает вектор, который является документированной функцией компилятора Java и доступен любому, кто публикует JAR-файл.

Референсы

В этом сообщении не приводятся ссылки на внешние источники. Все выводы основаны на непосредственном статическом анализе девяти опубликованных JAR-файлов, полученных из Maven Central до их удаления. Никакой код из этих артефактов не выполнялся ни на одном этапе.

sca-инструменты-программное обеспечение-композиция-анализ-инструменты
Расставьте приоритеты, устраните и защитите риски, связанные с программным обеспечением
Получите бесплатный аккаунт.
Нет необходимости кредитную карту.

Обеспечьте безопасность разработки и доставки программного обеспечения.

с пакетом продуктов Xygeni