TL, д-р
Артэфакт Maven Central, апублікаваны як io.github.davidtimur:c2-lab Было выпушчана дзевяць рэлізаў, восем з якіх выконвалі карысную нагрузку аддаленага доступу падчас кампіляцыі любога праекта, які размяшчаў jar-файл у шляху працэсара анатацый. Ніякі код прыкладання не патрабаваў яго імпарту. Ніякі радок зыходнага кода не патрабаваў спасылкі на яго. Не было ні ўсталявальнага скрыпта, ні эквівалента пасля ўсталёўкі, ні перахопніка жыццёвага цыклу, які можна было б праверыць з дапамогай інструментаў.
Вектар выканання — гэта адзін 46-байтавы файл унутры JAR-файла: рэгістрацыя пастаўшчыка паслуг Java, якая называе клас, які рэалізуе javax.annotation.processing.ProcessorКампілятар Java аўтаматычна выяўляе такія рэгістрацыі. Пасля іх выяўлення... javac стварае і запускае клас як звычайную частку кампіляцыі. Гэта азначае, што асяроддзем выканання карыснай нагрузкі з'яўляецца машына зборкі ў дадзены момант, калі машына зборкі робіць адзіную рэч, для якой яна існуе.
На працягу дзевяці выпускаў канал камандавання і кіравання перабудоўваўся тры разы: спачатку URL-адрас зваротнага выкліку, прадастаўлены аператарам, потым зваротная абалонка праз тунэль ngrok TCP, а потым канал апытання HTTP, шляхі якога змяняліся яшчэ двойчы. У фінальным выпуску ў якасці JVM па змаўчанні быў усталяваны no-op менеджар даверу TLS. прыём любых сертыфікатаў на працягу кампіляцыі.
Артэфакт меў надпіс «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 яго няма.
Калі явак Падчас працы ён скануе шлях да працэсара анатацый на наяўнасць гэтых запісаў службы і загружае знойдзенае. У кампіляваным праекце не трэба згадваць працэсар, што-небудзь анатаваць або што-небудзь наладжваць. Прысутнасці ў шляху дастаткова. Гэта ўласцівасць адрознівае JavacDoor ад шаблонаў ланцужкоў паставак, вакол якіх пабудавана большасць інструментаў:
| шаблон | Трыгер | Бачна як |
|---|---|---|
| npm усталёўвае перахопнік | npm install | scripts.postinstall у маніфесце |
| Карысная нагрузка падчас імпарту Python | першы імпарт модуля | аператар узроўню модуля ў зыходным коде |
| JavacDoor | javac па любым праекце ніжэй па плыні | імя файла рэгістрацыі службы |
Перахопнік усталёўкі — гэта аб'ява ў маніфесце, а маніфест — гэта першае, што чытаецца. Карысная нагрузка падчас імпарту прынамсі знаходзіцца ў чытальным зыходным коды. Рэгістрацыя службы не з'яўляецца ні тым, ні другім: гэта імя файла плюс адзін радок з назвай класа, а паводзіны знаходзіцца ў скампіляваным байт-кодзе ў іншым каталогу.
Клас карыснай нагрузкі сам выконвае разведку хоста шляхам вываду дадзеных. Пулы канстант скампіляваных класаў утрымліваюць /bin/sh, хто я, uname -a, Pwd па шляху Unix і Tasklist на шляху Windows разам з перанакіравацьErrorStream каб аб'яднаць вывад памылак даччынага працэсу з захопленым патокам. Два шаблоны JSON пераносяць вынікі з хоста — рэгістрацыйны маяк:
{"host":"%s","os":"%s","user":"%s","dir":"%s"}
населены з імя хаста каманда і назва аперацыйнай сістэмы, імя карыстальніка, і user.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-канал, /рэгістрацыя /апытанне /выхад |
1.0.6 | ды | HTTP-канал, /рэгістрацыя /апытанне /выхад |
1.0.7 | ды | HTTP-канал, /рэгістрацыя /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і тэрмінатар кадра __КАНЕЦ__У выпусках 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, які падаўляе прамежкавую старонку, якую бясплатныя тунэлі ngrok паказваюць браўзерам. Наборы шляхоў змяняюцца паміж рэлізамі — /выхад знікае ў версіі 1.0.7, і /каманда чаргуецца з /апытанне — але гаспадар ніколі не мяняецца.
Скажыце пра самавыключэнне
Пачынаючы з версіі 1.0.1, кожны POM усталёўвае аргумент кампілятара для ўласнай зборкі артэфакта:
-proc:няма
Гэты сцяжок адключае апрацоўку анатацый. Яго дзеянне тут папярэдняеcisе: калі праект, які змяшчае працэсар, сам кампілюецца, працэсар не запускаецца.
Гэты сцяг мае цалкам звычайнае прымяненне. Праект, які пастаўляецца з працэсарам анатацый, часта павінен пазбягаць прымянення гэтага працэсара да самога сябе падчас загрузкі, і дакументацыя build-tool рэкамендуе менавіта гэта. Сам па сабе ён нічога не даказвае.
Аднак разам з тым, што робіць працэсар, гэта апісвае пэўную асіметрыю: код выконваецца на машынах усіх, хто кампілюе артэфакт, а не на машыне, якая збірае артэфакт. Гэты сцяг з'яўляецца ў тым самым рэлізе, які прадстаўляе файл службы — 1.0.1 — і ў кожным наступным рэлізе. Карэляцыя паміж «рэлізам, дзе пачынаецца выкананне падчас кампіляцыі» і «рэлізам, дзе выкананне падчас кампіляцыі адключана лакальна» — гэта адзіны найбольш карысны аналітычны сігнал у наборы артэфактаў, і ён бачны ў POM у выглядзе звычайнага тэксту без дэкампіляцыі.
Мы адзначаем эфект і на гэтым спыняемся. Нішто ў артэфактах не тлумачыць, чаму быў усталяваны сцяг.
Варта згадаць яшчэ адзін фрагмент метададзеных, галоўным чынам для таго, каб ад яго пазбавіцца. POM называе праект «C2 Lab Payload», апісвае яго як «артэфакт карыснай нагрузкі C2 lab» і ліцэнзуе яго 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":...}
У канфігурацыі
- Environment variables
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:няма гэтак жа карысны ў абароне, як і, відавочна, лакальна тут; там, дзе гэта так, замацуйце набор працэсараў відавочна, а не наследуйце яго з класнага шляху кампіляцыі.
- Падпрацэсы кампілятара журналаў. Этап кампіляцыі, які спараджае абалонку, з'яўляецца анамальным у большасці праектаў і можа выклікаць трывіяльнае папярэджанне.
- Не разглядайце метададзеныя як сведчанне. «Лабараторыя», «тэст», «карысная нагрузка» і «PoC» у назве або апісанні пакета не з'яўляюцца абмежаваннямі вобласці дзеяння. Дасяжнасць і паводзіны абмяжоўваюць.
Артэфакт і ўся яго група былі выдалены з Спецыяліст Цэнтр пасля нашай справаздачы; і шлях артэфакта, і шлях групы цяпер вяртаюць 404, і індэкс Цэнтра не паведамляе пра адпаведныя каардынаты. Гэта закрывае гэты артэфакт. Ён не закрывае вектар, што з'яўляецца дакументаванай функцыяй кампілятара Java і даступнай кожнаму, хто публікуе jar-файл.
Спасылкі
У гэтай публікацыі няма спасылак на знешнія крыніцы. Усе высновы з'яўляюцца вынікам статычнага аналізу дзевяці апублікаваных файлаў JAR, атрыманых з Maven Central перад выдаленнем. Код з артэфактаў ні ў адзін момант не выконваўся.







