TL; DR
Sekelompok lima paket npm, yang dipublikasikan melalui dua akun, mengirimkan sebuah postinstall Sebuah program yang membaca kredensial cloud dari host dan mengirimkannya ke luar server. Paket-paket tersebut membawa nama-nama samaran atau nama bajakan — coral-wraith, ecto-corsair-whisper-6f3b9, ecto-corsair-flag-x9m4, ecto-rust-read-f3a9c1, ecto-nightly-spirit — dan menserialkan apa pun yang mereka kumpulkan menjadi barang palsu. ecto_module: Manifest YAML sebelum mengirimkannya. Kami melacak klaster sebagai ektoplasma.
Payload hanya akan berjalan ketika mendeteksi lingkungan tertentu: sebuah host yang namanya adalah string heksadesimal 12 karakter dan direktori kerja di bawah /app/node_modules — bentuk dari build atau worker CI yang dikontainerisasi. Ketika gerbang itu lewat, hook akan melakukan query ke Layanan metadata instance AWS (IMDSv2) untuk kredensial peran IAM, melakukan enumerasi Manajer Rahasia AWS mencakup tiga wilayah, membuang variabel lingkungan, membaca file di bawah /app, dan melakukan pengumpulan data untuk string "capture-the-flag". Kemudian, hasilnya dieksfiltrasi melalui dua cara: sebuah beacon ke sebuah webhook.site pengumpul dan manifes PUT ke titik akhir IP mentah, dengan daftar fallback localhost-pertama.
Deskripsi paket selanjutnya berbunyi "Payload CTF untuk pengujian rantai pasokan verdaccio." Kami melaporkan deskripsi diri tersebut sebagai fakta yang dapat diamati. Perilaku itu sendiri — akses keluar langsung ke IP publik, pembacaan kredensial IMDS yang sebenarnya, panggilan Secrets Manager yang sebenarnya — adalah apa adanya terlepas dari labelnya, dan merupakan alasan mengapa versi-versi ini diklasifikasikan sebagai berbahaya.
Salah satu nama dalam kelompok tersebut, coral-wraith, tidak berhenti hanya pada satu rilis. Ia menerbitkan ulang secara beruntun melalui puluhan versi hanya dalam hitungan jam — 1.0.0 memanjat ke 6.0.0 — dan edisi sebelumnya dengan nama yang sama telah menggunakan angka yang digembungkan. 9999.0.x nomor versi, bentuk klasik dari sebuah upaya kebingungan ketergantunganDi tengah perubahan tersebut, muatan data terlihat semakin matang: dari suar enumerasi host sekali jalan menjadi pivot kredensial AWS lengkap yang dibungkus dengan pemeriksaan lingkungan yang membuatnya tetap senyap di luar target yang dituju.
| Paket Exklusif | 5 nama; coral-wraith sendirian diterbitkan ulang dalam puluhan versi |
| Ekosistem | npm |
| Instal vektor | postinstall skrip siklus hidup |
| Target utama | Kredensial peran AWS IAM + nilai rahasia Secrets Manager, variabel lingkungan, /app arsip |
| Exfil | webhook.site beacon + raw-IP C2 PUT |
| Gerbang pemicu | nama host 12-hex + /app/node_modules cwd, ditambah pemeriksaan lingkungan yang menekan muatan di luar konteks tersebut. |
| Kerasnya | tinggi — pengungkapan kredensial cloud dan rahasia terkelola dari lingkungan pengembangan dan eksekusi berbasis kontainer |
Anatomi serangan
Setiap paket dalam klaster dibangun dengan cara yang sama: hampir kosong. index.js (modul.ekspor = {}), satu baris package.json naskah — “postinstall”: “node postinstall.js” — dan muatannya di postinstall.jsCukup dengan menginstal paket tersebut, hook akan berjalan; tidak diperlukan impor atau pemanggilan apa pun.
Gerbang penargetan. Sebelum melakukan apa pun, muatan keluarga ekto memeriksa sekitarnya:
function isAppWorker(): host = os.hostname() if host does NOT match /^[0-9a-f]{12}$/ -> exit if cwd does NOT contain "/app/node_modules" -> exit if cwd contains "/tmp/npm-safe" -> exit otherwise -> proceed Nama host 12 digit heksadesimal adalah bentuk default yang diberikan Docker pada sebuah kontainer, dan /app/node_modules adalah jalur instalasi konvensional di dalam kontainer. Klausa ketiga akan berhenti jika jalurnya terlihat seperti direktori ekstraksi yang diisolasi (sandbox). Efek bersihnya adalah muatan tetap tidak aktif di laptop pengembang atau lingkungan analisis terisolasi (sandbox) dan hanya aktif di dalam lingkungan build atau runtime yang dikontainerisasi — jenis lingkungan yang paling mungkin membawa kredensial cloud aktif. Paket paling awal dalam klaster, hantu karang, tidak memiliki gerbang semacam itu dan menjalankan koleksinya (yang lebih sederhana) tanpa syarat.
KoleksiSaat gerbang lewat, kaitnya akan keluar. execFileSync(“/bin/sh”, [“-c”, …]) dan menjalankan satu perintah gabungan yang, secara berurutan:
1. PUT /latest/api/token to 169.254.169.254 (IMDSv2 token request) 2. GET .../iam/security-credentials/ (IAM role name) 3. GET .../iam/security-credentials/<role> (temporary credentials) 4. dump env | sort (environment variables) 5. list /app (excl. node_modules) + cat first 15 (application files) 6. aws secretsmanager list-secrets (us-east-1, eu-west-1, eu-central-1) 7. scrape readable files for HTB{...} (capture-the-flag strings) Langkah 1–3 adalah contoh standar pengambilan IMDSv2: minta token sesi, lalu lampirkan sebagai X-aws-ec2-metadata-token Header untuk mengambil peran IAM instance dan kunci akses sementara peran tersebut. Pilihan untuk mengimplementasikan IMDSv2 daripada IMDSv1 yang lebih sederhana dan tidak terautentikasi. DAPATKAN Perlu dicatat — ini berarti payload berfungsi bahkan pada instance yang dikonfigurasi untuk memerlukan akses metadata berbasis token, yang merupakan pengerasan yang direkomendasikan AWS. Kredensial yang dikembalikan pada langkah 3 bersifat sementara. AccessKeyId/Kunci Akses Rahasia/Token Triple yang cakupannya sesuai dengan peran instance; apa pun yang dapat dilakukan oleh peran tersebut, pemegang kunci tersebut dapat melakukannya selama masa berlaku kredensial.
Langkah 4–6 memperluas pengambilan. The env File dump menangkap semua yang diwarisi oleh proses build atau runtime — dalam praktiknya, di sinilah token registri, string koneksi basis data, dan kunci API paling sering berada. / aplikasi File walk membaca hingga lima belas file aplikasi di luar node_modules, yang dapat membentuk konfigurasi permukaan, .env berkas, atau sumber. Langkah 6 memanggil aws secretsmanager list-secrets di tiga wilayah; kredensial yang diambil pada langkah 1–3 persis seperti yang mengautentikasi panggilan tersebut, sehingga pembacaan IMDS dan rantai enumerasi Secrets Manager digabungkan menjadi satu eskalasi: peran instance → inventaris rahasia terkelola. Langkah 7 merupakan isyarat pada kerangka "capture-the-flag" — ketika sebuah HTB{…} Jika flag ditemukan, flag tersebut dikirim sendiri-sendiri, jika tidak, blob mentah yang terkumpul akan dipecah menjadi empat bagian dan kemudian dikirim.
Versi-versi selanjutnya dalam kluster tersebut membawa eskalasi lebih jauh. Alih-alih berhenti pada inventaris, mereka menguraikan respons IMDS, mengekspor kunci sementara sebagai AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN variabel lingkungan, konfirmasikan identitasnya dengan aws sts get-caller-identity, dan kemudian mengulang setiap rahasia yang dikembalikan oleh rahasia daftar panggilan aws secretsmanager get-secret-value pada masing-masing — mengambil konten rahasia, bukan hanya namanya. Versi yang sama juga membaca biner flag yang disubstitusi proses (/baca bendera dan teman-teman) dan mencoba menjalankan biaya terhadap proyek Rust apa pun yang ditemukan di bawah / aplikasi, memperluas jangkauan pencarian di luar kredensial cloud hingga ke apa pun yang diekspos oleh lingkungan pengembangan.
Versi-versi selanjutnya ini juga menerapkan pembatasan akses yang lebih agresif. Selain nama host 12-hex dan /app/node_modules Dalam proses pemeriksaan, payload memeriksa konfigurasi registri paket aktif dan jalur direktori kerja, lalu keluar secara diam-diam ketika keduanya menunjukkan konteks analisis atau mirroring, bukan target aktif. Efek gabungannya adalah payload yang tidak melakukan apa pun yang dapat diamati di sebagian besar lingkungan inspeksi dan hanya menjalankan pengumpulan data lengkapnya jika dianggap berada di host yang menggunakan kontainerisasi yang asli.
exfiltrationData yang dikumpulkan meninggalkan host melalui dua saluran. Pertama, sebuah beacon. POST ke tetap webhook.situs Pertama, data tersebut dikumpulkan oleh kolektor, yang membawa nama host, UID numerik, direktori kerja, dan hingga 120 KB data yang dikumpulkan. Kedua, data tersebut dimasukkan ke dalam "manifest modul" YAML palsu dan PUT untuk /api/modul/ pada server target:
ecto_module: name: "<flag-or-chunk-0>" version: "1.0.0" power_level: "<chunk-1>" ship_deck: "<chunk-2>" cargo_hold: "<chunk-3>" Nama-nama kolom manifes (tingkat daya, dek kapal, ruang kargo) hanyalah hiasan — data yang dicuri berada di dalam nilai string, itulah sebabnya monitor jaringan melihat apa yang tampak seperti unggahan manifes registri paket yang tidak berbahaya, bukan sebagai data dump yang jelas. Saluran beacon membawa lebih banyak informasi: POST tubuh untuk webhook.situs Termasuk nama host, UID numerik, direktori kerja, dan hingga 120 KB dari blob yang dikumpulkan, sehingga bahkan satu beacon yang berhasil pun memberikan hasil lengkap. webhook.situs adalah layanan inspeksi permintaan gratis; menggunakannya sebagai pengumpul berarti operator tidak perlu lagi membangun infrastruktur penerima sendiri untuk saluran tersebut, dan permintaan yang tercatat tetap tersimpan di tempat sampah layanan tersebut.
Manifes PUT berjalan daftar cadangan yang dimulai dengan beberapa 127.0.0.1/localhost pelabuhan dan kemudian berlanjut ke tiga alamat publik di `154.57.164.0/24` rentang, berhenti di titik akhir pertama yang menjawab dengan status 2xx. Urutan localhost-pertama konsisten dengan deskripsi diri "pengujian verdaccio" (registri lokal pada loopback), tetapi fallback IP publik berarti data meninggalkan host setiap kali loopback tidak mendengarkan — yang berarti, pada mesin mana pun yang bukan perangkat uji penulis sendiri.
Perusahaan
Klaster ini menunjukkan pertumbuhan kemampuan secara bertahap, bukan penurunan secara tiba-tiba. Kami mengurutkannya berdasarkan perilaku yang diamati, bukan berdasarkan waktu publikasi:
| Tahap | Paket / Versi | Tingkah laku |
|---|---|---|
| Lari awal | coral-wraith 9999.0.x | Nomor versi yang digembungkan konsisten dengan upaya pengacauan dependensi; enumerasi dan eksfiltrasi pada saat instalasi. |
| Benih | coral-wraith 1.0.0 | postinstall mengumpulkan file id/env/flag; satu kali PUT ke 154[.]57[.]164[.]71:30782, penanda ECT-472839 |
| Iterasi cepat | coral-wraith 1.0.1 → 6.0.0 | Puluhan rilis dalam hitungan jam; muatan meningkat isAppWorker() gerbang, pengambilan kredensial IMDSv2, lengkapnya get-secret-value pivot, pemeriksaan lingkungan registri/jalur, dan penanda sink ganda |
| Nama paralel | ecto-corsair-whisper-6f3b9 1.0.14–1.0.18 | Muatan terkunci yang sama; webhook.site daftar cadangan beacon dan multi-endpoint |
| Varian | ecto-rust-read-f3a9c1 1.0.1–1.0.2 | Menambahkan penanda wastafel tambahan ECT-987654, ECT-654321, ECT-839201 |
| Varian | ecto-corsair-flag-x9m4 1.0.0, ecto-nightly-spirit 1.1.0 | Muatan terkunci yang sama, C2 dan suar yang sama. |
Ciri khas dari klaster ini adalah ritme penerbitannya: alih-alih satu paket dan satu versi, nama yang sama diterbitkan ulang berulang kali dalam waktu singkat, setiap rilis merupakan sedikit variasi dari rilis sebelumnya, bersamaan dengan beberapa paket lain dengan nama berbeda yang membawa muatan yang sama. Di dalam bisikan Kode keluarga tersebut terbagi menjadi dua sidik jari yang berdekatan — satu set memicu dua deteksi kritis, set lainnya tiga (sink baca file tambahan) — tetapi keduanya mengarah ke muatan yang sama; perbedaannya adalah pergeseran kode, bukan percabangan perilaku. Versi dari bisikan Di luar rentang yang dianalisis (hingga setidaknya 1.0.25 pada saat penulisan) diamati secara langsung di registri, dan hantu karang Nama tersebut terus menapaki tangga versi tersendiri di jendela yang sama.
Indikator kompromi
Semua indikator di bawah ini diekstrak dari kode sumber paket di disk. Indikator jaringan telah dinonaktifkan.
jaringan
| Indikator | Peran |
|---|---|
hxxp://154[.]57[.]164[.]71:30782 | Target C2 PUT (coral-wraith) |
hxxp://154[.]57[.]164[.]80:30543 | C2 PUT fallback (ecto-*) |
hxxp://154[.]57[.]164[.]82:31250 | C2 PUT fallback (ecto-*) |
hxxp://154[.]57[.]164[.]71:31289 | C2 PUT fallback (ecto-*) |
hxxps://webhook[.]site/602a4c72-7033-4e28-92ea-dc66e59206e5 | Pengumpul suar |
169[.]254[.]169[.]254/latest/... | Pembacaan kredensial IMDSv2 (sisi target, metadata AWS) |
Perilaku/berkas
| Indikator | Peran |
|---|---|
"postinstall": "node postinstall.js" | Instal vektor |
ecto_module: YAML dengan power_level / ship_deck / cargo_hold kunci-kunci | Skema manifes eksfil |
Penanda wastafel ECT-472839, ECT-987654, ECT-654321, ECT-839201 | Segmen jalur C2 /api/modules/<marker> |
isAppWorker() gerbang: tuan rumah /^[0-9a-f]{12}$/, cwd berisi /app/node_modules | Kondisi aktivasi |
aws secretsmanager list-secrets lebih us-east-1, eu-west-1, eu-central-1 | Enumerasi rahasia |
HTB{...} pengikisan regex | Panen dalam permainan merebut bendera. |
Hash file (sha256, diambil pada saat analisis)
| File | sha256 |
|---|---|
coral-wraith/postinstall.js | ce5ff035cfdfed1d0015446424b352c27b66bcb77e9fdb0a51e4245199146824 |
ecto-corsair-whisper-6f3b9 1.0.18/postinstall.js | b58432acba376aa6976f0490d9a1c04257ccdbc856d8390260c50322d63e31c3 |
Atribusi & perilaku yang diamati
Kelima nama paket tersebut diterbitkan di bawah dua akun npm yang berbeda, tetapi mereka berbagi infrastruktur yang cukup untuk diperlakukan sebagai satu klaster: sama. modul ecto skema manifes, sama ECT-472839 penanda wastafel utama, sama webhook.situs ID kolektor, dan titik akhir C2 di tempat yang sama Blok `154.57.164.0/24`. Paket benih (`coral-wraith`), lebih sederhana dan tanpa gerbang) dan ecto-family yang bergerbang dengan demikian dapat dibaca sebagai iterasi pada satu perangkat, bukan upaya independen.
Dalam versi-versi selanjutnya, paket-paket tersebut mendeskripsikan diri mereka sendiri sebagai berikut: “Payload CTF untuk pengujian rantai pasokan verdaccio.” Kami menampilkan label tersebut sebagai fakta yang dapat diamati dan tidak menyatakannya kembali sebagai temuan tentang tujuan. Apa yang dilakukan kode tersebut tidak ambigu dan independen dari bagaimana kode tersebut diberi label: kode tersebut membaca kredensial peran IAM dari layanan metadata instance, menghitung rahasia terkelola di tiga wilayah AWS, dan mengirimkan hasilnya ke IP publik dan pengumpul webhook pihak ketiga. Sebuah kerangka kerja pengujian yang benar-benar hanya menggunakan loopback tidak memerlukan daftar cadangan IP publik, pembacaan kredensial IMDS, atau panggilan Secrets Manager lintas wilayah. Karena akses keluar dan jangkauan kredensial itu nyata, versi yang dibatasi aksesnya diklasifikasikan sebagai berbahaya.
Gerbang khusus kontainer adalah ciri yang paling menonjol secara operasional. Ini merupakan tindakan penghindaran—tetap diam di laptop dan di lingkungan pengujian analisis—dan juga tindakan penargetan, yang hanya aktif di tempat di mana peran IAM yang sebenarnya dan rahasia aktif kemungkinan besar ada. Analis yang menjalankan paket ini di lingkungan pengujian umum tidak akan mengamati apa pun; perilaku tersebut hanya muncul di bawah nama host bergaya Docker dan jalur instalasi di dalam kontainer.
Dampak, tren, dan panduan bagi para pembela
Yang menjadi masalah di sini adalah pengungkapan kredensial cloud dan informasi rahasia di dalam kontainer build dan runtime. Kredensial peran IAM yang diambil dari IMDS membawa semua izin yang dimiliki peran tersebut; secretsmanager:ListSecrets (dan tindak lanjut apa pun) DapatkanNilaiRahasiaHal ini meluas hingga mencakup rahasia aplikasi yang tersimpan. Data variabel lingkungan sering kali memuat token registri, URL basis data, dan kunci API. Dalam konteks CI atau kontainer — persis seperti yang dipilih oleh gate — satu instalasi transisi dari salah satu paket ini sudah cukup untuk membocorkan materi tersebut.
Ectoplasm sesuai dengan pola yang terus kita lihat: muatan saat instalasi yang mengakses metadata cloud dan rahasia yang dikelola, bukan file lokal, dan yang membatasi diri untuk hanya aktif di lingkungan yang bernilai tinggi. Berikut dua pengamatan defensif.
- Bentuknya dapat dideteksi.Sebuah hook instalasi npm/PyPI yang grafik panggilannya mencapai API rahasia cloud (pengelola rahasia AWS, rahasia gcloud, brankas kunci az) atau alamat IMDS dan sink keluar jaringan adalah pola yang sempit dan bersinyal tinggi — hampir tidak pernah terjadi dalam skrip siklus hidup yang sah. Analisis aliran statis dapat menandainya tanpa bergantung pada domain atau IP tertentu.
- Pengerasan lingkungan mengurangi efektivitasnya. Menerapkan IMDSv2 dengan batas hop 1 mencegah beban kerja kontainer mencapai metadata instance; membatasi peran IAM ke hak akses paling minimal membatasi dampak kebocoran kredensial apa pun; dan menjalankan instalasi dengan –Abaikan-skrip Dalam CI, vektor install-hook dihapus sepenuhnya untuk paket yang tidak membutuhkannya.
Bagi pihak yang bertugas melindungi sistem, pemeriksaan praktisnya adalah: memberikan peringatan pada koneksi keluar dari kontainer build/CI ke IP publik yang tidak diizinkan selama instalasi npm; pantau akses IMDS yang berasal dari skrip siklus hidup paket; dan perlakukan setiap hook instalasi yang mengarah ke CLI cloud sebagai mencurigakan sampai terbukti sebaliknya.
Dua catatan tambahan khusus untuk kluster ini. Pertama, karena aktivasi dibatasi pada lingkungan berbasis kontainer, sebuah paket yang tampak tidak aktif saat diperiksa di workstation masih dapat aktif di lingkungan produksi — pemeriksaan perlu mereproduksi kondisi hostname dan path kontainer, atau membaca kode sumber secara langsung, daripada hanya mengandalkan "Saya menginstalnya dan tidak terjadi apa-apa." Kedua, penggunaan layanan inspeksi permintaan publik sebagai pengumpul beacon berarti beberapa data yang dieksfiltrasi mungkin dapat dipulihkan untuk respons insiden: sebuah organisasi yang menemukan salah satu paket ini dalam pohon dependensinya dapat mempertimbangkan apa yang akan terkandung dalam beacon yang berhasil berdasarkan logika pengumpulan payload, dan harus merotasi kredensial peran IAM, token registri, dan rahasia terkelola yang dapat dijangkau dari lingkungan build atau runtime yang terpengaruh. Rotasi kredensial, bukan penghapusan paket, adalah solusi yang tepat setelah instalasi dijalankan.





