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 hook | npm install | scripts.postinstall в манифесте |
| полезная нагрузка во время импорта Python | первый импорт модуля | оператор уровня модуля в исходном коде |
| JavacDoor | javac в любом проекте, реализуемом на последующих этапах производства. | имя файла регистрации сервиса |
Хук установки — это объявление в манифесте, а манифест — это первое, что кто-либо читает. Полезная нагрузка во время импорта, по крайней мере, находится в читаемом исходном коде. Регистрация сервиса не является ни тем, ни другим: это имя файла плюс одна строка, указывающая на класс, а поведение находится в скомпилированном байт-коде в соседнем каталоге.
Сам класс полезной нагрузки выполняет разведку хоста путем отправки командной строки. Пулы констант скомпилированных классов содержат / Бен / ш, 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 до их удаления. Никакой код из этих артефактов не выполнялся ни на одном этапе.







