JavacDoor, Malware Maven yang Berjalan pada Waktu Kompilasi

JavacDoor: Sebuah Artefak Maven yang Berjalan Selama Kompilasi, Bukan Instalasi

TL; DR

Sebuah artefak Maven Central yang diterbitkan sebagai io.github.davidtimur:c2-lab Telah dirilis sembilan kali, delapan di antaranya menjalankan payload akses jarak jauh selama kompilasi proyek hilir apa pun yang menempatkan jar tersebut pada jalur prosesor anotasi. Tidak ada kode aplikasi yang perlu mengimpornya. Tidak ada baris kode sumber yang perlu merujuknya. Tidak ada skrip instalasi, tidak ada padanan pasca-instalasi, dan tidak ada kait siklus hidup apa pun bagi alat untuk melakukan inspeksi.

Vektor eksekusi adalah sebuah file tunggal berukuran 46 byte di dalam jar: sebuah registrasi penyedia layanan Java yang menyebutkan sebuah kelas yang mengimplementasikan javax.annotation.processing.ProcessorKompiler Java secara otomatis menemukan registrasi tersebut. Setelah ditemukan, javac menginisialisasi dan menjalankan kelas sebagai bagian normal dari proses kompilasi. Itu berarti lingkungan runtime payload adalah mesin build, dan saat ini mesin build sedang melakukan satu hal yang memang menjadi tugasnya.

Selama sembilan rilis, saluran perintah dan kontrol dibangun ulang tiga kali: URL callback yang disediakan operator, kemudian shell terbalik melalui terowongan TCP ngrok, lalu saluran polling HTTP yang jalurnya bergeser dua kali lagi. Rilis terakhir memasang pengelola kepercayaan TLS no-op sebagai default JVM, menerima sertifikat apa pun selama proses kompilasi berlangsung.

Artefak tersebut memuat kata-kata "C2 Lab Payload" dalam file POM-nya sendiri dan lisensi MIT. Artefak tersebut aktif di Maven Central selama seluruh periode pengamatan kami dan sejak itu telah dihapus, bersama dengan seluruhnya. io.github.davidtimur kelompok.

Anatomi: kompiler sebagai mesin eksekusi

Kerangka kerja pemrosesan anotasi Java ada agar pustaka dapat menghasilkan kode pada waktu kompilasi — mekanisme di balik Lombok, Dagger, dan daftar panjang alat ORM dan serialisasi. Sebuah prosesor mengumumkan keberadaannya dengan file teks biasa di dalam jar:

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

Isi file tersebut di setiap rilis yang terpengaruh, apa adanya dan lengkap:

io.github.davidtimur.c2lab.C2Prosesor

File tersebut identik secara byte di seluruh rilis 1.0.1 hingga 1.0.8, md5. 7d2a08a5c8869a47eea9fa62487dfbe4. Versi 1.0.0 tidak memuatnya.

Ketika Jawa Saat dijalankan, ia memindai jalur annotation-processor untuk entri layanan ini dan memuat apa yang ditemukannya. Tidak ada dalam proyek yang sedang dikompilasi yang perlu menyebutkan prosesor, memberi anotasi apa pun, atau mengkonfigurasi apa pun. Keberadaan di jalur tersebut sudah cukup. Inilah properti yang membedakan JavacDoor dari pola rantai pasokan yang menjadi dasar sebagian besar alat bantu:

polaPelatukTerlihat sebagai
npm install hooknpm installscripts.postinstall dalam manifes
Muatan saat impor Pythonimpor pertama modulpernyataan tingkat modul dalam sumber
JavacDoorjavac pada proyek hilir apa punnama file pendaftaran layanan

Install hook adalah deklarasi dalam manifest, dan manifest adalah hal pertama yang dibaca siapa pun. Payload saat impor setidaknya berada dalam kode sumber yang mudah dibaca. Registrasi layanan bukanlah keduanya: itu hanyalah nama file ditambah satu baris yang menyebutkan sebuah kelas, dan perilakunya berada dalam bytecode yang telah dikompilasi di direktori yang berbeda.

Kelas payload itu sendiri melakukan pengintaian host dengan mengirimkan perintah shell. Kumpulan konstanta dari kelas yang dikompilasi berisi / Bin / sh, whoami, uname -a, pwd pada jalur Unix dan tasklist pada jalur Windows, bersama dengan redirectErrorStream untuk menggabungkan output kesalahan proses anak ke dalam aliran yang ditangkap. Dua templat JSON membawa hasil dari host — sebuah suar registrasi:

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

diisi dari hostname perintah dan nama os., user.name, dan direktori pengguna properti sistem, dan fungsi callback hasil:

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

Penanda kemajuan yang ditinggalkan dalam output kompiler itu sendiri sangat jujur: [C2] Eksekusi kompilasi selesai, [C2] callback terkirim → HTTP , [C2] cangkang terhubung ke .

Sembilan rilis, tiga generasi C2

Rilis tersebut bukanlah sembilan salinan dari satu muatan. Itu adalah catatan iterasi, dan membacanya secara berurutan menunjukkan bahwa saluran sedang dibangun kembali sementara vektor pengiriman tetap sama.

LepaskanBerjalan otomatis saat kompilasiSaluran
1.0.0tidak (tidak ada berkas layanan)Hanya URL callback yang disediakan oleh operator.
1.0.1ya (vektor diperkenalkan)URL callback yang disediakan operator
1.0.2iya nihReverse shell, terowongan TCP ngrok
1.0.3iya nihSaluran HTTP, /register /cmd /out
1.0.4iya nihSaluran HTTP, /register /cmd /out
1.0.5iya nihSaluran HTTP, /register /poll /out
1.0.6iya nihSaluran HTTP, /register /poll /out
1.0.7iya nihSaluran HTTP, /register /cmd
1.0.8iya nihValidasi saluran HTTP + TLS dinonaktifkan

Tiga detail dalam tabel itu layak diuraikan, karena masing-masing mengubah cara pembacaan kumpulan artefak tersebut.

Vektor tersebut tiba di 1.0.1, bukan 1.0.2. Rilis 1.0.0 membawa logika pengintaian dan panggilan balik yang sama tetapi tidak memiliki file layanan dan tidak ada impor pemrosesan anotasi; ia hanya berjalan jika ada sesuatu yang memanggilnya. Mulai dari versi 1.0.1, file layanan ada dan kelas yang dikompilasi mengimpornya. javax.annotation.processing.SupportedSourceVersionSetiap penilaian yang membandingkan kedua rilis berbayar — 1.0.0 dan 1.0.2 — secara tepat menyimpulkan bahwa ada sesuatu yang berubah, tetapi salah mengidentifikasi di mana perubahannya.

Reverse shell hanya ada di satu rilis saja. Rilis 1.0.2 berisi 0.tcp.ngrok[.]io, baris log [C2] shell terhubung ke 0.tcp.ngrok[.]io:19823, sebuah banner interaktif cangkang c2, dan terminator bingkai __AKHIR__Rilis 1.0.3 dan seterusnya tidak mengandung hal tersebut dan malah mengarah ke host HTTPS. Permintaan penghapusan yang hanya menyebutkan rilis yang diberi tiket akan menyebutkan titik akhir TCP yang mati sementara saluran HTTP yang aktif, yang ada di enam rilis selanjutnya, tidak disebutkan.

Versi final menghapus verifikasi transportasi. Rilis 1.0.8 menambahkan kelas yang mengimplementasikan javax.net.ssl.X509TrustManager yang metode pemeriksaan sertifikatnya tidak berfungsi, verifikator nama host yang selalu benar terdaftar melalui setDefaultHostnameVerifier, Dan percayai semua Rutinitas yang menginstal keduanya sebagai default JVM. Efeknya adalah, selama sisa proses kompilasi, JVM menerima sertifikat apa pun dari host mana pun — tidak hanya untuk lalu lintas payload itu sendiri, tetapi juga untuk apa pun yang dilakukan proses build melalui TLS setelahnya.

Saluran HTTP, di keenam rilis yang menggunakannya, dilayani oleh satu host tunggal: tableful-fervor-crazed.ngrok-free[.]devSetiap permintaan membawa header ngrok-skip-browser-warning, yang menekan halaman perantara yang disajikan oleh terowongan ngrok gratis ke browser. Kumpulan jalur bergeser di setiap rilis — /keluar menghilang pada versi 1.0.7, dan /cmd bergantian dengan /pemilihan — tetapi pembawa acaranya tidak pernah berubah.

Pengucilan diri memberi tahu

Setiap POM mulai dari versi 1.0.1 dan seterusnya menetapkan argumen kompiler untuk proses build artefak itu sendiri:

-proc:tidak ada

Bendera itu menonaktifkan pemrosesan anotasi. Efeknya di sini adalah precise: ketika proyek yang berisi prosesor itu sendiri dikompilasi, prosesor tersebut tidak berjalan.

Bendera ini memiliki kegunaan yang sepenuhnya biasa. Sebuah proyek yang menyertakan prosesor anotasi seringkali perlu menghindari penerapan prosesor tersebut pada dirinya sendiri selama proses bootstrap, dan dokumentasi build-tool merekomendasikan hal ini. Jika dilihat secara terpisah, bendera ini tidak membuktikan apa pun.

Jika dikaitkan dengan fungsi prosesor, hal ini menggambarkan asimetri spesifik: kode dieksekusi pada mesin semua orang yang mengkompilasi terhadap artefak tersebut, dan bukan pada mesin yang membangun artefak tersebut. Flag tersebut muncul dalam rilis yang sama yang memperkenalkan file layanan — 1.0.1 — dan di setiap rilis setelahnya. Korelasi antara "rilis tempat eksekusi kompilasi dimulai" dan "rilis tempat eksekusi kompilasi dimatikan secara lokal" adalah sinyal analitik paling berguna dalam kumpulan artefak, dan terlihat dalam POM teks biasa tanpa perlu mendekompilasi apa pun.

Kita mencatat dampaknya dan berhenti sampai di situ. Tidak ada satu pun artefak yang menjelaskan mengapa bendera itu dikibarkan.

Satu metadata lagi perlu disebutkan, terutama untuk menyingkirkannya. POM menamai proyek tersebut "C2 Lab Payload", mendeskripsikannya sebagai "artefak payload lab C2", dan melisensikannya dengan MIT. Pelabelan diri seperti ini terkadang ditawarkan sebagai bukti bahwa sebuah paket adalah eksperimen penelitian.cisAlih-alih ancaman nyata, dan terkadang interpretasi itu benar — sebuah "canary" yang dinyatakan tanpa infrastruktur yang dapat dijangkau adalah objek yang berbeda dari yang ini. Itu tidak berlaku di sini. Sebuah implant fungsional yang dipublikasikan ke repositori publik, dapat dijangkau oleh konsumen mana pun, dengan infrastruktur keluar yang telah dibangun ulang tiga kali di sembilan rilis, adalah kemampuan yang aktif terlepas dari apa pun sebutan metadata-nya. Nama dalam POM tidak mengubah apa pun tentang apa yang terjadi pada mesin yang mengkompilasinya.

Indikator untuk mesin pembuatan

Jika host build melakukan kompilasi terhadap artefak ini, buktinya tersimpan dalam log build dan telemetri jaringan, bukan dalam implant permanen di disk — payload berjalan di dalam proses kompilasi dan keluar bersamaan dengan proses tersebut.

Di dalam file JAR atau cache repositori lokal.

  • META-INF/services/javax.annotation.processing.Processor penamaan io.github.davidtimur.c2lab.C2Processor
  • File layanan md5 7d2a08a5c8869a47eea9fa62487dfbe4
  • Kelas di bawah io/github/davidtimur/c2lab/: C2Processor, C2Task, Task, dan di versi 1.0.8 kelas dalam Task$1

Dalam output build

  • [C2] compile-time execution complete
  • [C2] callback sent → HTTP
  • [C2] callback failed:
  • [C2] shell connected to
  • Spanduk interaktif c2-shell; terminator bingkai __END__

Telemetri dalam proses

  • javac sebagai orang tua dari /bin/sh -c (Unix) atau penerjemah perintah Windows
  • Perintah anak whoami, uname -a, pwd (Unix) atau tasklist (Windows) yang diinduk oleh langkah kompilasi

Dalam telemetri jaringan

  • TCP keluar ke 0.tcp.ngrok[.]io:19823 (rilis 1.0.2)
  • HTTPS ke tableful-fervor-crazed.ngrok-free[.]dev, jalur /register, /cmd, /poll, /out (rilis 1.0.3 hingga 1.0.8)
  • Header permintaan ngrok-skip-browser-warning: true
  • Permintaan pencocokan badan {"host":...,"os":...,"user":...,"dir":...} or {"version":...,"host":...,"time":...,"output":...}

Dalam konfigurasi

  • Variabel lingkungan CALLBACK, CALLBACK_URLproperti sistem callback.url

Metadata penerbit

  • Kelompok io.github.davidtimur; alamat penerbit davudboi999@gmail[.]comkunci penandatanganan E520C345EF94423D

Build yang menjalankan rilis 1.0.8 memerlukan satu pemeriksaan tambahan. Karena rilis tersebut menginstal trust manager permisif sebagai default di seluruh JVM, setiap koneksi TLS yang dibuat kemudian di JVM yang sama — resolusi dependensi, pengunggahan artefak, langkah deployment — berjalan tanpa validasi sertifikat. Lalu lintas dari jendela tersebut tidak boleh dianggap telah diautentikasi.

Mengapa artefak yang telah dikompilasi memerlukan pemindaian yang berbeda?

JavacDoor adalah kasus uji yang berguna karena membantah dua asumsi umum sekaligus, dan kedua kegagalan tersebut tidak spesifik pada perangkat lunak vendor tertentu.

Asumsi pertama adalah bahwa kode berbahaya akan mengumumkan keberadaannya dalam sebuah manifest. Sebagian besar perangkat bantu rantai pasokan diatur berdasarkan siklus hidup. hooks, karena untuk npm dan PyPI, di situlah biasanya aksi terjadi. JavacDoor tidak memiliki hook. Pemicunya adalah file pendaftaran layanan yang namanya adalah antarmuka Java dan isinya adalah nama kelas. Untuk menangkapnya secara statis, Anda harus memperlakukannya META-INF/services/javax.annotation.processing.Processor sebagai titik masuk eksekusi tersendiri, setara dengan pascainstal skrip — dan kemudian ikuti kelas yang disebutkan ke dalam bytecode. Ekosistem memiliki mekanisme penemuan otomatisnya sendiri dalam bentuk ini, dan masing-masing merupakan titik masuk terlepas dari apakah alat tersebut mencantumkannya sebagai titik masuk atau tidak.

Asumsi kedua adalah bahwa string berada di dalam file sumber. Untuk sebuah jar, endpoint, perintah shell, template JSON, dan penanda log semuanya berada dalam kumpulan konstanta dari .kelas berkas. Alat yang menggunakan grep untuk mencari teks tidak menemukan apa pun — bukan karena stringnya dienkripsi, tetapi karena string tersebut berada dalam wadah biner terstruktur yang tidak dapat diuraikan oleh pemindaian teks. Setiap indikator jaringan dalam posting ini berasal dari penguraian kumpulan konstanta. Salah satunya adalah bentuk lengkap. https:// URL tersebut terlihat jelas di dalam file kelas; pemindaian teks dari konten yang dapat dibaca di dalam file JAR pun tidak akan menampilkannya. Tidak ada pengkodean yang perlu diatasi di sini, hanya format kontainer yang perlu dibaca.

Kedua celah tersebut memiliki bentuk yang sama: format artefak diperlakukan sebagai sekumpulan file, bukan sebagai struktur dengan semantik yang terdefinisi. Perbaikannya tidak glamor — uraikan kontainer, enumerasi titik masuk penemuan otomatis ekosistem, dan ikuti hingga ke kode yang dikompilasi. Khusus untuk vektor waktu pembuatan, pemeriksaan ketiga murah dan mengejutkan diagnostiknya: bandingkan apa yang dilakukan artefak terhadap konsumen dengan apa yang dikecualikannya sendiri. Artefak yang mendaftarkan prosesor waktu kompilasi dan secara bersamaan menonaktifkan pemrosesan waktu kompilasi untuk pembuatannya sendiri telah memberi tahu Anda sesuatu tentang dirinya sendiri dalam dua baris POM teks biasa.

Bagi tim yang saat ini menggunakan artefak Maven, ada tiga langkah praktis yang perlu diperhatikan:

  • Perlakukan jalur prosesor anotasi sebagai batas eksekusi. Dependensi yang masuk ke sana menjalankan kode dalam proses build Anda. Jika proses build tidak memerlukan pemrosesan anotasi, -proc:tidak ada sama bermanfaatnya secara defensif seperti yang terlihat secara lokal di sini; jika memang demikian, tetapkan set prosesor secara eksplisit daripada mewarisinya dari classpath kompilasi.
  • Subproses kompilator log. Langkah kompilasi yang menjalankan shell merupakan hal yang tidak biasa di sebagian besar proyek dan mudah untuk diberi peringatan.
  • Jangan perlakukan metadata sebagai bukti. “Lab”, “test”, “payload”, dan “PoC” dalam nama atau deskripsi paket bukanlah batasan cakupan. Keterjangkauan dan perilaku adalah batasan cakupan.

Artefak tersebut dan seluruh kelompoknya telah dipindahkan dari Maven Setelah laporan kami, Central melakukan pengecekan; baik jalur artefak maupun jalur grup sekarang mengembalikan kode kesalahan 404, dan indeks Central melaporkan tidak ada koordinat yang cocok. Hal ini menutup artefak ini. Namun, hal ini tidak menutup vektor, yang merupakan fitur yang didokumentasikan oleh kompiler Java dan tersedia bagi siapa pun yang menerbitkan file JAR.

Referensi

Postingan ini tidak mengutip sumber eksternal apa pun. Semua temuan merupakan analisis statis langsung dari sembilan file JAR yang telah dipublikasikan, yang diambil dari Maven Central sebelum dihapus. Tidak ada kode dari artefak yang dieksekusi pada titik mana pun.

perangkat lunak analisis komposisi sca
Prioritaskan, perbaiki, dan amankan risiko perangkat lunak Anda.
Dapatkan Akun Gratis Anda.
Tidak perlu kartu kredit.

Amankan Pengembangan dan Pengiriman Perangkat Lunak Anda

dengan Rangkaian Produk Xygeni