JavacDoor, Maven зловреден софтуер, който се изпълнява по време на компилация

JavacDoor: Артефакт на Maven, който се е изпълнявал по време на компилация, а не по време на инсталация

TL; DR

Артефакт от Maven Central, публикуван като io.github.davidtimur:c2-lab бяха пуснати девет издания, осем от които изпълняваха полезен товар за отдалечен достъп по време на компилацията на който и да е проект надолу по веригата, който поставяше jar файла в пътя му за обработка на анотации. Не беше необходимо да се импортира от код на приложението. Не беше необходимо да се прави препратка към него от реда на изходния код. Нямаше инсталационен скрипт, еквивалент след инсталация и никакъв вид hook за жизнения цикъл, който инструментите да проверят.

Векторът за изпълнение е един 46-байтов файл вътре в jar файла: регистрация на доставчик на услуги на Java, която именува клас, имплементиращ javax.annotation.processing.ProcessorКомпилаторът на Java открива такива регистрации автоматично. След като бъдат намерени, javac създава инстанции и изпълнява класа като нормална част от компилирането. Това означава, че средата за изпълнение на полезния товар е машината за изграждане, в момента, в който машината за изграждане прави единственото нещо, за което съществува.

В деветте издания каналът за командно-контролен достъп е бил преизграждан три пъти: URL адрес за обратно извикване, предоставен от оператор, след това обратна обвивка през TCP тунел ngrok, и накрая HTTP канал за запитване, чиито пътища са се променили още два пъти. В последната версия е инсталиран no-op TLS мениджър на доверие като JVM по подразбиране. приемане на сертификати по време на компилацията.

Артефактът носеше думите „C2 Lab Payload“ в собствения си POM и лиценз на 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 не го съдържа.

Кога явански Когато се изпълнява, той сканира пътя на процесора за анотации за тези записи за услуги и зарежда това, което намери. Нищо в компилирания проект не е необходимо да споменава процесора, да анотира каквото и да било или да конфигурира каквото и да било. Присъствието в пътя е достатъчно. Това е свойството, което отличава JavacDoor от моделите на веригата за доставки, около които е изградена повечето инструменти:

МоделТригерВидимо като
npm инсталиране на кукаnpm installscripts.postinstall в манифеста
Полезен ресурс по време на импортиране в Pythonпърви импорт на модулаоператор на ниво модул в изходния код
JavacDoorjavac по всеки проект надолу по веригатаиме на файл за регистрация на услуга

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

Самият клас payload извършва разузнаване на хоста чрез shell-out. Константните пулове на компилираните класове съдържат / BIN / ш, Whoami, uname -a, хората с увреждания по пътя на Unix и Списъкът със задачи по пътя на Windows, заедно с пренасочванеErrorStream за да се обедини изходът за грешка на дъщерния процес в заснетия поток. Два 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

Изданията не са девет копия на един полезен товар. Те са лог на итерации и четенето им по ред показва, че каналът се преустройва, докато векторът на доставка остава фиксиран.

ОсвободетеАвтоматично се изпълнява при компилиранеКанал
1.0.0не (няма сервизен файл)Само URL адрес за обратно повикване, предоставен от оператора
1.0.1да (въведен е вектор)URL адрес за обратно повикване, предоставен от оператора
1.0.2даОбратна обвивка, ngrok TCP тунел
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-shellи терминатор на рамка __END__Версии 1.0.3 и нататък не съдържат нито едно от тези неща и вместо това достигат до HTTPS хост. Заявка за премахване, посочваща само изданията с билети, би посочила неработеща TCP крайна точка, като същевременно би оставила активния HTTP канал, присъстващ в шест по-късни издания, неспоменат.

Финалната версия премахва проверката на транспорта. Версия 1.0.8 добавя клас, имплементиращ javax.net.ssl.X509TrustManager чиито методи за проверка на сертификати не правят нищо, верификатор на име на хост, който винаги е верен, регистриран чрез setDefaultHostnameVerifierи довериеВсички рутина, която инсталира и двете като настройки по подразбиране на JVM. Ефектът е, че за останалата част от компилацията JVM приема всеки сертификат от всеки хост - не само за собствения трафик на полезния товар, но и за всичко останало, което компилацията извършва през TLS след това.

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

Самоизключването разказва

Всеки POM от версия 1.0.1 нататък задава аргумент на компилатора за собствената компилация на артефакта:

-proc:няма

Този флаг деактивира обработката на анотации. Ефектът му тук е предварителноcise: когато проектът, съдържащ процесора, се компилира, процесорът не работи.

Този флаг има съвсем обичайни приложения. Проект, който предоставя процесор за анотации, често трябва да избягва прилагането на този процесор към себе си по време на първоначалното зареждане, а документацията на build-tool препоръчва точно това. Само по себе си това не доказва нищо.

Взето заедно с това, което прави процесорът, обаче, това описва специфична асиметрия: кодът се изпълнява на машините на всички, които компилират спрямо артефакта, а не на машината, която изгражда артефакта. Флагът се появява в същата версия, която въвежда сервизния файл — 1.0.1 — и във всяка версия след нея. Връзката между „версията, в която започва изпълнението по време на компилация“ и „версията, в която изпълнението по време на компилация е изключено локално“ е най-полезният аналитичен сигнал в набора от артефакти и е видим в POM в обикновен текст, без да се декомпилира нищо.

Забелязваме ефекта и спираме дотук. Нищо в артефактите не говори за причината за поставянето на знамето.

Още една метаданна заслужава да се спомене, най-вече за да се отървем от нея. POM нарича проекта „C2 Lab Payload“, описва го като „артефакт на C2 lab payload“ и го лицензира от MIT. Самообозначаване от този вид понякога се предлага като доказателство, че даден пакет е изследователско упражнение.cise, а не „жива заплаха“, и понякога това тълкуване е правилно — деклариран канарче без достъпна инфраструктура е различен обект от този. Това не се отнася тук. Функционален имплант, публикуван в публично хранилище, достъпен за всеки потребител, с изходяща инфраструктура, която е била пресъздадена три пъти в девет издания, е „жива“ възможност, независимо как я наричат ​​метаданните ѝ. Името в 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 няма „hook“. Неговият тригер е файл за регистрация на услуга, чието име е Java интерфейс, а съдържанието е име на клас. За да хванете това статично, трябва да третирате META-INF/services/javax.annotation.processing.Processor като самостоятелна входна точка за изпълнение, наравно с след инсталиране скрипт — и след това да следва посочения клас в байткода. Екосистемите имат свои собствени механизми за автоматично откриване от този вид и всяка от тях е входна точка, независимо дали инструментариумът я изброява като такава или не.

Второто предположение е, че низовете се намират в изходните файлове. За един jar файл, крайните точки, командите на shell, JSON шаблоните и маркерите на лога са в константните пулове на .клас файлове. Инструментите, които обработват текст с greps, не намират нищо — не защото низовете са обфускирани, а защото са в структуриран двоичен контейнер, който текстовото сканиране не анализира. Всеки мрежов индикатор в тази публикация е резултат от анализ на константен пул. Един от тях е напълно формиран https:// URL адресът е видим във файл с клас; текстово сканиране на четливото съдържание на jar файла все още не би го разкрило. Тук няма кодиране, което да се преодолее, само формат на контейнера за четене.

И двете празнини имат еднаква форма: форматът на артефакта се третираше като торба с файлове, а не като структура с дефинирана семантика. Корекцията не е бляскава - анализирайте контейнера, избройте собствените точки за автоматично откриване на екосистемата и ги следвайте в компилирания код. По-специално за векторите по време на компилация, третата проверка е евтина и изненадващо диагностична: сравнете какво прави даден артефакт на потребителите с това, от което се освобождава. Артефакт, който регистрира процесор по време на компилация и едновременно с това деактивира обработката по време на компилация за собствената си компилация, ви е казал нещо за себе си в два реда POM в обикновен текст.

За екипите, използващи артефакти на Maven днес, следват три практически мерки:

  • Третирайте пътя на процесора за анотации като граница на изпълнение. Зависимостите, които попадат там, изпълняват код във вашата компилация. Когато компилацията не се нуждае от обработка на анотации, -proc:няма е толкова полезно в защита, колкото очевидно беше локално тук; където е така, закрепете набора от процесори изрично, вместо да го наследявате от пътя на класовете при компилиране.
  • Подпроцеси на компилатора на регистрационни файлове. Стъпка на компилация, която поражда шел, е аномална в повечето проекти и е тривиално предупредителна.
  • Не третирайте метаданните като свидетелство. „Лаборатория“, „тест“, „полезен товар“ и „Показател на съответствието“ в името или описанието на пакета не са ограничения на обхвата. Достижимостта и поведението са.

Артефактът и цялата му група бяха премахнати от Maven Централният път след нашия доклад; както пътят на артефакта, така и пътят на групата вече връщат 404, а индексът на Централния път не отчита съвпадащи координати. Това затваря този артефакт. Не затваря вектора, което е документирана функция на Java компилатора и е достъпна за всеки, който публикува jar файл.

Източници

Тази публикация не цитира външни източници. Всички открития са статичен анализ от първа ръка на деветте публикувани jar-а, извлечени от Maven Central преди премахването им. В нито един момент не е изпълнен код от артефактите.

инструменти за анализ на състава на софтуера SCA Tools
Приоритизирайте, отстранете и защитете софтуерните си рискове
Вземете своя безплатен акаунт.
Не е необходима кредитна карта.

Осигурете си разработването и доставката на софтуер

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