24 Agustus 20269 menit
Dua hari setelah merilis Syncraft Labs versi 0.4.0, saya lagi di luar dan nggak bawa laptop. Semua berjalan biasa sampai notifikasi di ponsel mengubah sisa hari itu. Ada issue baru di repo. Judulnya ditulis kapital semua. "[URGENT] npm packages compromised by PolinRider". Saya buka, dan sepuluh detik pertama membacanya terasa seperti baca cerita tentang proyek orang lain.
Ini cerita apa yang terjadi setelahnya. Bukan cerita heroik, soalnya di dalamnya ada panik, ada saling cek ulang dua kali, dan ada keputusan yang diambil cepat sambil berharap nggak salah.
Laporan yang bikin saya berhenti
Issuenya datang dari CharlieErsiksen. Saya nggak kenal dia sama sekali. Tapi laporannya lugas dan langsung menusuk. Ketiga paket npm saya, @syncraft-labs/core, @syncraft-labs/react, dan @syncraft-labs/vue, dirilis dalam versi 0.4.1 yang ternyata berisi kode terobfuscated milik malware PolinRider.
Yang bikin saya merinding bukan cuma kabar bajakannya. Tapi cara payload itu disembunyikan. Kode asli dipadatkan jadi satu blob nggak terbaca, lalu diselipkan di antara ratusan baris whitespace. Fungsinya kayak bantal, biar nggak mencolok kalau dilihat sekilas. Setelah dideobfuscated, baris-baris kuncinya mulai keliatan.
// ... padding whitespace
global.i = "A12-git";global.r = require;
// ...sisanya blob terobfuscated yang nggak saya muat ulang di sini
Baris pertama itu penanda internal malware, semacam cap identitas versi. Baris kedua lebih bahaya, soalnya nyimpen referensi require ke variabel global. Nanti dipakai buat ambil modul lain diam-diam. Versi 0.4.1 sendiri nggak pernah saya rilis secara sadar dari mesin saya. Itu bagian paling bikin merinding dari semua ini, dan penjelasannya ada di bawah.
Nyari tahu gimana ini bisa terjadi
Semuanya berawal dari laptop yang kena infeksi. Beberapa waktu sebelumnya, ada calon klien dari situs freelance yang minta saya redesain project-nya. Kayak biasa, saya clone repo-nya, setup, lalu run di lokal. Persis alur yang sudah saya lakukan puluhan kali tanpa pernah ada masalah.
Kali ini beda. Malware ikut mengendap diam-diam di mesin saya sejak saat itu. Nggak ada gejala, nggak bikin lemot, nggak muncul di mana-mana. Tugasnya cuma satu: ngintai. Dan incarannya bukan file-file saya, tapi sesuatu yang jauh lebih berharga, yaitu kredensial.
Dari laptop itulah PAT GitHub saya dicuri. Token itu punya izin cukup untuk nyentuh repo dan release. Dengan token curian, penyerang nggak perlu nyentuh laptop saya lagi. Mereka bisa dorong dan terbitkan versi 0.4.1 yang udah disuntik PolinRider langsung ke aliran distribusi npm.
Dan ini yang bikin insidennya naik level. Bukan cuma Syncraft Labs yang kena. Beberapa project lain di environment yang sama ikut terinfeksi, semuanya lewat jalur yang sama. Satu kali clone yang ceroboh, satu token yang terlalu percaya diri, dan efeknya nyebar ke segala arah.
Pelajaran utama yang saya tulis tebal-tebal di kepala: supply chain attack nggak selalu masuk lewat dependency di package.json. Kadang dia masuk lewat kode yang kita jalankan sendiri pakai tangan kita sendiri.
Dari notifikasi sampai install ulang
Yang membangunkan saya dari zona nyaman ternyata bukan laporan isunya, tapi notifikasi CI/CD. Pipeline yang selama ini sunyi tiba-tiba ngerakam aktivitas rilis yang nggak saya lakukan. Notifikasi itu yang bikin saya sadar ini bukan false positive, ini beneran terjadi.
Langkah pertama saya bukan perbaiki kode. Revoke dulu semua token, PAT GitHub maupun token npm, secepatnya. Selama kunci masih hidup, semua usaha bersihin lainnya sia-sia. Urutannya penting. Matiin aksesnya dulu, baru mikirin sisanya.
Setelah semua kunci mati, giliran mesinnya. Laptop yang udah terlanjur tercemar saya anggap hilang kepercayaan. Fresh install OS dari nol. Kedengeran ekstrem, tapi untuk kasus information stealer kayak gini, ini satu-satunya jalan yang bikin tenang. Malware semacam ini dirancang buat bertahan, dan saya nggak mau main petak umpet sama sisa-sisanya.
Baru setelah mesin bersih dan kredensial baru aman, saya mulai bongkar repo-repo yang tercemar satu per satu.
Lima jam sampai versi bersih rilis
Dari laporan masuk sampai rilis bersih, waktunya sekitar lima jam. Bukan karena saya hebat, tapi karena urutan langkahnya jelas begitu kepanikan berhasil dikendalikan.
Tapi sebelum semua itu, saya harus mastiin laptopnya bener-bener bersih dulu. Fresh install OS dari nol saya jalanin, biar nggak ada sisa-sisa malware yang masih ngumpet. Baru setelah lingkungan aman, saya bangun ulang semuanya dari tempat yang terisolasi. Hasil build diaudit dulu sebelum berani saya rilis. Token npm baru dibuat dengan rotasi penuh, dan publishing sekarang wajib pake two-factor authentication. Biar kejadian serupa butuh lebih dari sekadar token bocor.
Versi 0.4.1 saya deprecate habis-habisan dan jejaknya dicabut. Lalu v0.4.2 rilis berisi kode yang bener-bener bersih, disertai panduan buat siapa pun yang sempat pasang versi tercemar.
npm cache clean --force
rm -rf node_modules package-lock.json
# kunci ke versi bersih
"@syncraft-labs/core": "^0.4.2"
npm install --ignore-scripts
Panduannya juga nyaranin rotasi kredensial buat yang pernah jalanin versi tercemar, soalnya PolinRider target utamanya emang environment variable dan token.
Bagian paling berat dari seluruh proses sebenarnya bukan teknisnya. Tapi nulis pengumuman yang ngaku secara terbuka kalau paket saya pernah tercemar. Tapi itu keputusan yang nggak saya sesali. Pengguna berhak tahu, dan kejujuran lebih cepet nyembuhin reputasi daripada diem.
Pelajaran yang saya bawa
Kalau diringkas jadi satu kalimat: jangan asal run project sembarangan. Clone dari orang yang belum dikenal tetep aja menjalankan kode orang yang belum dikenal. Sisanya ada beberapa hal yang saya bawa ke depan.
-
Notifikasi CI/CD itu penyelamat. Tanpa rekam jejak pipeline, insiden ini mungkin berlangsung lebih lama sebelum ada yang sadar.
-
PAT harus minimal scope dan umurnya pendek. Token yang bisa segalanya dan berumur panjang adalah bom waktu yang tick-nya nggak kedengeran.
-
Pas insiden, revoke dulu baru mikir. Selama kunci pencuri masih aktif, semua langkah pemulihan lain cuma ngulang kerjaan.
-
Fresh install OS bukan overreaction. Buat stealer yang dirancang bertahan, format adalah cara paling murah buat yakin.
-
Environment build harus diisolasi. Build produksi nggak boleh lahir di tempat yang sama dengan eksperimen dan clone sembarangan.
-
Laporan security dari komunitas itu berharga. Orang yang laporin bug keamananmu bukan musuh, dia salah satu alasan library-mu masih hidup.
Penutup
Makasih banyak buat CharlieErsiksen yang udah laporin dengan rapi. Tanpa laporan itu, mungkin versi tercemar masih bergantung lebih lama, dan itu pikiran yang nggak enak.
Insiden ini udah ditutup dengan v0.4.2 yang bersih, tapi bekasnya sengaja nggak saya hapus dari ingatan. Jadi pengingat kalau kepercayaan di ekosistem open source itu tipis, dan menjaganya jauh lebih susah daripada dapetinnya.
Kalau kamu maintainer package yang belum ngaktifin 2FA publishing, atau lagi nimbang-nimbang apakah repo klien layak di-run langsung, anggap ini tanda. Kalau mau diskusi soal penanganan insiden kayak gini, kabari saya. Kotak masuk email saya selalu terbuka.