Latar belakang
Baru-baru ini, sistem pemantauan keamanan MistEye menangkap informasi tentang munculnya versi abnormal pada beberapa paket npm di bawah organisasi Red Hat Cloud Services. Insiden ini melibatkan total 32 paket npm dan 96 versi di bawah organisasi tersebut. Artikel ini memilih 3 sampel lokal untuk dianalisis secara mendalam secara offline: sampel-sampel ini bukanlah paket yang meniru namespace atau typo-squatting, melainkan versi asli yang menggunakan scope @redhat-cloud-services; dari kode sumber sampel, dapat dikonfirmasi bahwa tarball-nya telah disisipi loader jahat multi-lapis yang akan secara otomatis memicu selama tahap instalasi.
Setelah pemulihan lengkap, dapat dikonfirmasi: ketiga sampel ini memiliki kemampuan kode inti untuk membaca memori GitHub Actions Runner, mengumpulkan kredensial multi-cloud dan lokal, mengekstrak data ke GitHub API dan dead-drop, menyuntikkan workflow GitHub, menyebar secara mandiri melalui npm, serta mempertahankan akses melalui Claude Code / VS Code / systemd / LaunchAgent, mengatasi Harden-Runner / StepSecurity, dan mendeteksi EDR / produk keamanan. Dari cakupan kemampuan ini, objek yang berpotensi terdampak mencakup host pengembang, CI/CD Runner, kontainer build, repositori GitHub, workflow GitHub Actions, rantai rilis npm, dan kredensial lingkungan cloud; cakupan dampak aktual perlu dikonfirmasi lebih lanjut melalui log instalasi, audit repositori, dan telemetry sisi platform.
Dari struktur kode, jalur penyebaran, dan kombinasi kemampuan, perangkat lunak berbahaya ini merupakan varian dari Shai-Hulud.
MistEye respons
MistEye adalah sistem intelijen ancaman Web3 dan pemantauan keamanan dinamis yang dikembangkan secara mandiri oleh SlowMist, yang mengintegrasikan kemampuan pemantauan keamanan dan agregasi intelijen untuk memberikan peringatan risiko real-time dan perlindungan aset kepada pengguna.
Setelah menangkap insiden penusukan rantai pasokan paket npm Red Hat Cloud Services dan sampel jahat terkait, sistem MistEye telah memicu peringatan tingkat tinggi, melakukan analisis sistematis terhadap struktur obfuscation, dekripsi payload, pemulihan kemampuan, dan IOC dari rantai serangan tersebut.
(https://enterprise.misteye.io/threat-intelligence/SM-2026-378450)
Overview of the Attack Chain
Bagian analisis teknis dalam artikel ini hanya berdasarkan analisis statis lokal offline, dengan membongkar, mendekode, dan memverifikasi kebingungan dari 3 sampel npm tgz.
Sampel yang dicakup dalam verifikasi ini adalah:
@redhat-cloud-services/frontend-components-config
Versi: 6.11.3
tgz SHA-256: 0c9c67ec40d5f23efa1ec3470d0ac88b4993ccc0e92be913fc29a337dfc4f060
@redhat-cloud-services/types
Versi: 3.6.1
tgz SHA-256: d543bb3cdf1569c2b3d38c8a4081ed746cfe78bf3236c2302704d79ab7fa9558
@redhat-cloud-services/rule-components
Versi: 4.7.2
tgz SHA-256: aaf00d06baa3c679b82452c50014e9824b8874e9ca2d150f19095f8de19ba90f
Tiga sampel memiliki titik masuk serangan yang identik: package.json semuanya berisi scripts.preinstall = "node index.js". Ini berarti bahwa setiap kali host pengembang atau lingkungan CI/CD menginstal versi-versi ini, file index.js di direktori utama akan dieksekusi otomatis selama tahap instalasi, tanpa perlu pengguna secara eksplisit mengimpor atau menjalankan kode bisnis.
Tiga sampel berbagi payload jahat inti yang sama, tetapi parameter pembungkus luar berbeda. Artinya, ketiga sampel menggunakan nilai pergeseran ROT/Caesar dan kunci AES-GCM/iv/tag yang berbeda; hal ini membuat fitur statis berbasis hash pembungkus luar, kunci tetap, atau pergeseran tetap sulit digunakan ulang secara langsung di antara paket. Namun, bootloader lingkungan runtime Bun dan hash payload jahat inti setelah dekripsi identik: satu adalah modul pendukung awal yang digunakan untuk menyiapkan lingkungan runtime Bun, dan yang lainnya adalah payload inti yang benar-benar mengandung fungsi jahat. Artikel ini menyebut keduanya masing-masing sebagai "Bun Runtime Bootloader (nama variabel kode sumber _b)" dan "Payload Jahat Inti (nama variabel kode sumber _p)".
Rantai serangan yang dipulihkan adalah sebagai berikut:

Analisis teknis
1. Masuk: Penyusupan skrip npm lifecycle
Ketiga sampel memicu rantai serangan melalui hook lifecycle preinstall di package.json:
"scripts": {
"preinstall": "node index.js"
}
preinstall akan dieksekusi secara otomatis selama tahap instalasi npm. Untuk komponen frontend biasa, definisi tipe, atau paket komponen aturan, biasanya tidak perlu menjalankan file JavaScript berukuran besar di direktori utama selama tahap instalasi; ini adalah sinyal anomali paling langsung.
Bukti tingkat sampel sebagai berikut:
@redhat-cloud-services/frontend-components-config@6.11.3
- main: lib/index.js
- bidang files: ["/lib", "/bin"]
- File index.js root SHA-256: 545a1838c66e1771f58d84a17b3e1841e5eeab91a73f4ccc59c9492450a6d9c0
@redhat-cloud-services/types@3.6.1
- main: index.d.ts
- Bidang files: Tidak diatur
- File index.js root SHA-256: b86c5ae9e95bd841a595440faa3eb6317441e746f241ae8fd641ab59ed1d1966
@redhat-cloud-services/rule-components@4.7.2
- main: index.js
- Bidang files: Tidak diatur
- File index.js root directory SHA-256: 1a30a9abe20bab121aaa75ed040565af14e6cdfb745609ee0e7b94a2d814fb9c
Dalam hal ini, bidang files dari frontend-components-config@6.11.3 hanya menyatakan "/lib" dan "/bin", tetapi ada file index.js tambahan di direktori root tarball yang dipanggil oleh preinstall. Penyimpangan ini hanya berlaku untuk sampel ini; types@3.6.1 dan rule-components@4.7.2 tidak menetapkan bidang files, sehingga penyimpangan ini tidak digeneralisasi ke semua sampel.
2. Lapisan pertama: array angka + substitusi huruf ROT/Caesar
Tiga sampel berkas index.js di direktori utama adalah file JavaScript satu baris yang sangat besar, dengan struktur yang konsisten, berupa wrapper try { eval(...) } catch (...) :
Wrapper ini terlebih dahulu mengembalikan array angka besar menjadi string melalui String.fromCharCode, kemudian melakukan pergeseran ROT/Caesar pada huruf alfabet dalam string tersebut, dan akhirnya menyerahkan hasil dekripsi ke eval untuk dieksekusi. Penanganan pengecualian hanya menampilkan prefix wrapper: untuk mengurangi eksposur pengecualian yang mencolok saat kegagalan pemasangan.
Nilai perpindahan luar dan hash Stage 2 dari tiga sampel adalah sebagai berikut:
frontend-components-config@6.11.3
index.js ukuran: 4.294.798 byte
ROT/Caesar shift: 10
Tahap 2 SHA-256: b19c2fd48535c8c40aeb3e627ce92775f33ef9292611767bb1236c238e6f90cc
types@3.6.1
index.js ukuran: 4.135.588 byte
ROT/Caesar shift: 4
Tahap 2 SHA-256: 9c0425aa6e6d7792ac38d24f3e7245f42fcaa553ddfeb6bd97677017f10c3b75
rule-components@4.7.2
index.js ukuran: 4.294.336 byte
ROT/geser Caesar: 11
Tahap 2 SHA-256: d590bd375d95e4ac072b7ebc1fc4489bcaf5f20a939e92486267aa398bcf1e5d
3. Lapisan Kedua: Dekripsi AES-128-GCM terhadap Bun bootloader dan payload inti
Kode Stage 2 setelah dekripsi ROT/Caesar dijalankan dengan Node.js crypto.createDecipheriv untuk mendekripsi AES-128-GCM (Advanced Encryption Standard - Galois/Counter Mode) dan mengatur tag otentikasi melalui setAuthTag. Tujuan dekripsi adalah dua komponen berikutnya—bootstrapper lingkungan runtime Bun dan payload berbahaya inti.
Di antaranya, bootloader lingkungan Bun bertanggung jawab untuk mendeteksi, melokalisasi, atau menyiapkan lingkungan Bun agar kode selanjutnya dapat dieksekusi melalui Bun; sedangkan beban berbahaya inti merupakan inti dari rantai serangan, yang mencakup kemampuan berbahaya utama seperti pengumpulan kredensial selanjutnya, penyebaran GitHub/npm, persistensi, penghindaran perlindungan, dan dekripsi sumber daya tertanam.
Tiga sampel masing-masing dienkripsi menggunakan kunci AES-128-GCM, vektor inisialisasi, dan tag otentikasi yang berbeda untuk kedua komponen tersebut, tetapi SHA-256 dari tiga pembuat boot lingkungan Bun setelah didekripsi adalah ac2a2208e1726e008be6c73dc0872d9bba163319259dff1b62055ac933ca46b6, dan SHA-256 dari tiga muatan berbahaya inti adalah 0dc06ecdaa63fe24859cfd955053c23245c536e4733480239d14bebf12688e35. Ini menunjukkan bahwa penyerang mengganti parameter pembungkus luar di berbagai paket npm, tetapi menggunakan kembali komponen berbahaya inti yang sama.
Setelah didekripsi, Stage 2 akan menulis beban berbahaya inti ke /tmp, lalu mengeksekusinya melalui Bun, dan akhirnya menghapus file sementara. Logika aktual yang direkonstruksi dari tiga sampel adalah sebagai berikut:
Oleh karena itu, pola file sementara yang dapat diverifikasi dalam ketiga sampel adalah:
/tmp/p.js
Bun runtime bootloader berukuran 898 byte, tiga sampel hash benar-benar identik:
ac2a2208e1726e008be6c73dc0872d9bba163319259dff1b62055ac933ca46b6
Bun runtime bootloader berisi logika seperti child_process.execSync, fs.existsSync, fs.mkdtempSync, fs.chmodSync, os.platform(), os.arch(), getBunPath(), yang digunakan untuk menemukan atau menyiapkan lingkungan runtime Bun. Artinya, Stage 2 tidak hanya mengasumsikan bahwa Bun telah terinstal secara global; di luar jalur runtime Bun, ia terlebih dahulu memuat bootloader lingkungan runtime Bun, lalu memanggil Bun melalui getBunPath() untuk menjalankan payload berbahaya inti.
4. Lapisan ketiga: tabel string obfuscator.io
Payload jahat yang diekstraksi bukanlah JavaScript teks biasa yang langsung dapat dibaca, melainkan tetap mengalami kebingungan gaya obfuscator.io melalui tabel string. Lapisan ini mengumpulkan sejumlah besar string ke dalam array, lalu merekonstruksinya secara runtime melalui indeks dan logika rotasi, sehingga analis tidak dapat langsung mengakses API, jalur, dan nama konfigurasi kunci berikutnya sebelum tabel string sepenuhnya direkonstruksi.
5. Lapisan keempat: B5 Enkripsi string khusus
Setelah merekonstruksi tabel string obfuscator.io, masih terdapat satu lapisan enkripsi string khusus B5 di dalam payload berbahaya utama. Parameter berikut telah dikonfirmasi:
- KDF: PBKDF2 (Password-Based Key Derivation Function 2)
- Fungsi hash: SHA-256
- Jumlah iterasi: 200000
- panjang kunci: 32 byte
- Jumlah ronde dekripsi: 3
- kata sandi:ba2c6ddb3672bdd6a611e6850b4f700b52aed3dab2f1b3d5f8c839d4a157a709
- salt: 5b26508dc0f1075a7c0b4d8aa464487e
Hasil dekripsi sebagai berikut:
Di antaranya, setelah dekripsi B5, terlihat berbagai string plaintext kunci. Perlu diperhatikan bahwa sebagian string awalnya berada di tabel string obfuscator.io, dan hanya akan langsung dapat digrep setelah kedua langkah "penggantian tabel string + dekripsi B5" selesai:
6. Lapisan penyemat tambahan: AES-256-GCM + gzip
Beban berbahaya inti juga berisi sumber daya tertanam yang dienkripsi AES-256-GCM dan dikompresi gzip. Logika pemulihan menggunakan:
Sumber daya tersembunyi yang telah dipecahkan dibagi menjadi tiga kategori berdasarkan fungsinya:
Pembacaan memori lintas platform:
Sumber daya semacam ini digunakan untuk membaca memori proses yang sedang berjalan. Kunci dan token dalam file biasa masih dapat ditemukan melalui pencarian file, tetapi CI/CD Runner atau alat pengembang menyimpan beberapa nilai sensitif sementara di memori saat berjalan. Penyerang menyisipkan skrip pembaca memori untuk tiga platform: Linux, Windows, dan macOS, dengan tujuan menangkap secret sementara ini dari memori proses di berbagai sistem.

Konfigurasi persisten:
Sumber daya semacam ini digunakan untuk "meninggalkan titik pemicu backdoor." Artinya, meskipun proses instalasi npm awal telah selesai, penyerang tetap ingin kode jahat dapat dijalankan kembali ketika pengembang membuka proyek, memulai editor, memasuki sesi Claude Code, atau setelah sistem login ulang. Oleh karena itu, sumber daya ini menargetkan konfigurasi tingkat proyek dan mekanisme auto-start tingkat pengguna: konfigurasi tingkat proyek lebih tersembunyi, sedangkan layanan tingkat sistem lebih cocok untuk berjalan dalam jangka panjang.

C2 dan penyebaran:
Sumber daya tertanam ini digunakan untuk mengontrol rantai dan pengangkutan secret repositori selanjutnya. YZ setelah didekripsi adalah GitHub commit search monitor, yang secara berkala mencari pesan commit dalam format firedalazer ., memverifikasi tanda tangan, lalu mengunduh dan menjalankan konten Python jarak jauh; zZ setelah didekripsi adalah template workflow GitHub Actions bernama Run Copilot, yang menulis ${{ toJSON(secrets) }} ke format-results.txt dan mengunggahnya sebagai artifact. Yang pertama menyediakan rantai pengambilan/pelaksanaan tugas selanjutnya, yang kedua menyediakan template pengangkutan secret repositori melalui artifact GitHub Actions; injeksi workflow aktual, penantian eksekusi, dan unduhan artifact dilakukan oleh logika GitHub API yang terkait dalam beban utama.

Analisis kemampuan muatan berbahaya inti
Setelah melalui beberapa tahap de-obfuscasi, sampel akhirnya melepaskan dan menjalankan payload jahat inti. Semua analisis berikut fokus pada modul payload inti, diuraikan secara bertahap berdasarkan lima tahap: pengambilan data, ekfiltrasi, penyebaran, persistensi, dan penghindaran.
1. Pembacaan memori GitHub Actions Runner:
Sampel menyertakan skrip pembacaan memori untuk tiga platform: Linux, Windows, dan macOS, dengan kemampuan dump lintas platform. Implementasi Linux dilakukan dengan membaca /proc//maps dan /proc//mem dari proses target:
Logika pemicuan aktif beban berbahaya utama menargetkan Linux runner GitHub Actions: ketika GITHUB_ACTIONS === "true" dan RUNNER_OS === "Linux", kode mencari proses Runner.Worker dan melakukan dump memori, kemudian mengekstrak rahasia yang dimaskikan dalam format "":{"value":"","isSecret":true}. Jalur pengumpulan aktif yang dapat dikonfirmasi pada ketiga sampel berfokus pada Linux runner, tetapi skrip untuk ketiga platform telah disematkan.
2. Multi-cloud dan pengumpulan kredensial lokal pengembang:
Beban berbahaya inti mencakup modul pengumpulan kredensial terstruktur, mencakup penyedia cloud, lingkungan CI, konfigurasi lokal pengembang, GitHub CLI, manajer kata sandi, dan file dompet.
Tujuan pengumpulan kredensial penyedia cloud:

Modul AWS juga mencakup logika terkait ECS/IMDS (Instance Metadata Service)/STS WebIdentity.
Pengumpulan kredensial lokal pengembang mencakup target berikut:

Logika manajer kata sandi diimplementasikan dalam kode sumber sebagai array runCommand(command, args), bukan penggabungan string shell.
Modul pengumpulan kredensial ini mencakup berbagai target mulai dari platform cloud hingga perangkat lokal pengembang; data sensitif yang dikumpulkan ditransmisikan melalui mekanisme berikut.
3. GitHub API eksternal dan dead-drop:
Beban berbahaya utama menggunakan GitHub API sebagai saluran utama ekstraksi data. Permintaan menyamar sebagai User-Agent python-requests/2.31.0, dan setelah memverifikasi token melalui x-oauth-scopes (harus mencakup repo, public_repo, atau workflow), operasi ekstraksi dilakukan: membuat repository (deskripsi tetap sebagai Miasma: The Spreading Blight), lalu menulis data yang dicuri yang telah di-encode base64 melalui PUT /repos///contents/results/.
Selain itu, payload menerapkan mekanisme dead-drop berbasis pencarian commit GitHub, yang mengambil perintah C2 dengan mencari marker "thebeautifulmarchoftime" (atau "thebeautifulsnadsoftime"):
let result = await X9("thebeautifulmarchoftime ", xZ);
Sumber tersemat YZ.bin adalah pemantau commit GitHub independen yang memantau secara berkala setiap 3600 detik untuk mencari pesan commit dalam format firedalazer ., dan setelah verifikasi tanda tangan, mengunduh serta menjalankan konten Python jarak jauh. Ini membentuk saluran kontrol lanjutan yang terpisah dari saluran eksternal utama.
Kode sumber juga berisi konfigurasi sender HTTP POST yang menargetkan endpoint api.anthropic.com dan v1/api. Bidang noop dalam konfigurasi ini diatur ke true, yang berarti sender tersebut tidak akan mengirim permintaan jaringan aktual jika tidak diubah dari luar. Oleh karena itu, analisis statis hanya dapat mengonfirmasi keberadaan struktur kode, tetapi tidak membuktikan adanya perilaku eksternal aktif ke Anthropic API.
4. Infeksi pada repositori GitHub dan alur kerja:
Beban berbahaya inti dapat mengoperasikan objek git repositori melalui API REST / GraphQL GitHub:

Payload yang dirancang secara jahat dengan nama release meminta izin id-token: write, lalu menukar token rilis npm melalui jalur OIDC (OpenID Connect). Variabel lingkungan dan antarmuka jalur OIDC adalah ACTIONS_ID_TOKEN_REQUEST_TOKEN dan ACTIONS_ID_TOKEN_REQUEST_URL, dengan audience diatur ke npm:registry.npmjs.org. Dari tingkat sumber kode, dapat dikonfirmasi bahwa payload memiliki kemampuan untuk menyisipkan workflow ke dalam repositori GitHub dan memanfaatkan OIDC / trusted publishing npm untuk mendapatkan izin rilis.
Selain metode injeksi workflow melalui git refs yang disebutkan di atas, payload juga menyertakan mekanisme pengangkatan rahasia yang lebih tersembunyi: workflow GitHub Actions yang menyamar sebagai Run Copilot menulis ${{ toJSON(secrets) }} ke format-results.txt dan mengunggah artifact. Dibandingkan dengan menulis rahasia langsung ke konten repositori, pendekatan ini lebih cenderung mengangkut data melalui hasil eksekusi workflow, sehingga tinjauan diff kode konvensional mungkin tidak langsung melihat konten rahasia yang diekspor.
5. Jalankan alur kerja Copilot melalui artifact untuk memindahkan secrets:
Sumber tersemat zZ.bin adalah workflow GitHub Actions yang menyamar sebagai Copilot, yang menulis ${{ toJSON(secrets) }} ke format-results.txt dan mengunggahnya sebagai artifact.
Perilaku GitHub API yang terkait mencakup: membuat/memperbarui branch sementara → menulis blob workflow → menunggu jalannya workflow → mengunduh zip artifact → membaca format-results.txt → menghapus workflow run dan branch sementara. Kata kunci terkait pencarian meliputi Run Copilot, VARIABLE_STORE, format-results, chore/add-codeql-static-analysis, .github/workflows/codeql.yml.
6. npm penyebaran mandiri:
Beban berbahaya utama memiliki dua jalur rilis / penyebaran npm yang terpisah.
Path npm token: Payload melalui GET /-/npm/v1/tokens untuk mencantumkan token npm pengguna saat ini, menyaring token dengan bypass_2fa === true dan memiliki izin penulisan paket, lalu gunakan token ini untuk melakukan publikasi otomatis. bypass_2fa adalah bidang metadata token, bukan parameter body publikasi.
Path GitHub Actions OIDC: Membaca variabel lingkungan ACTIONS_ID_TOKEN_REQUEST_TOKEN dan ACTIONS_ID_TOKEN_REQUEST_URL, meminta token OIDC dengan audience npm:registry.npmjs.org, dan menukar melalui antarmuka pertukaran token OIDC npm untuk mendapatkan izin rilis.
Prosedur operasi pencemaran tarball adalah membongkar → menulis index.js jahat → mengubah package.json:
Di sini dibedakan dua jenis entri: tiga sampel unggahan lokal menggunakan preinstall: "node index.js"—pada saat ini, lingkungan pemasangan tidak memerlukan Bun terlebih dahulu, sehingga menggunakan Node.js sebagai entri awal lebih tersembunyi; saat menyebar sendiri dan mencemari paket lain, maka ditulis preinstall: "bun run index.js" dan menambahkan dependensi bun—karena logika lanjutan dari beban berbahaya inti bergantung pada API yang disediakan oleh runtime Bun (seperti Bun.gunzipSync dan Bun.file), dalam skenario penyebaran mandiri diperlukan untuk memastikan lingkungan target telah memiliki Bun.
7. Mekanisme persistensi:
Beban berbahaya inti mencakup empat metode persistensi, mencakup direktori proyek dan auto-start tingkat sistem.
Claude Code SessionStart hook: Sumber daya S8.bin menyisipkan SessionStart hook ke .claude/settings.json, yang secara otomatis mengeksekusi node .claude/setup.mjs setiap kali sesi Claude Code dimulai. Payload berbahaya juga ditulis ke .claude/setup.mjs dan .claude/index.js (path disusun dari ".claude/" + M0, M0 = "index.js").
Tugas folderOpen VS Code: Sertakan sumber daya EZ.bin untuk menulis tugas folderOpen ke .vscode/tasks.json, yang akan secara otomatis menjalankan skrip yang sama saat folder proyek yang terinfeksi dibuka. Juga tulis ke .vscode/setup.mjs.
kitty-monitor (systemd / LaunchAgent): Menanamkan sumber daya RZ.bin untuk menginstal layanan persisten, dengan file tersebar di /.local/share/kitty/cat.py, /.config/systemd/user/kitty-monitor.service (Linux), dan ~/Library/LaunchAgents/com.user.kitty-monitor.plist (macOS). Payload jahat membuat direktori /tmp/kitty-, menulis YZ.bin (GitHub commit monitor) ke cat.py, lalu menginstalnya melalui RZ.bin.
gh-token-monitor (systemd / LaunchAgent): Menanamkan sumber daya jZ.bin untuk memasang layanan pemantau token GitHub, dengan file tersebar di direktori /.local/bin/gh-token-monitor.sh, /.config/gh-token-monitor/, dan jalur systemd / LaunchAgent yang sesuai. Monitor ini memeriksa apakah token GitHub mengembalikan kode status 40x serta apakah metadata paket npm @cap-js/openapi@1.4.1 tidak normal; setelah terpicu, akan menjalankan handler:
Ini berarti bahwa ketika monitor mengamati GitHub API mengembalikan kode status 40x, misalnya token dicabut, kedaluwarsa, atau status izin tidak normal, skrip jahat akan mengeksekusi handler yang dimasukkan. Handler yang direkonstruksi dari ketiga sampel dalam artikel ini adalah rm -rf ~/; rm -rf ~/Documents. Saat penanganan, jangan langsung mencabut token di host yang kemungkinan masih menjalankan payload—sebaiknya isolasi terlebih dahulu host tersebut, bersihkan item persisten dan proses yang berjalan, lalu ganti kredensial dari lingkungan yang bersih.
8. Harden-Runner / StepSecurity对抗:
Muatan berbahaya inti memiliki logika deteksi dan penghancuran khusus untuk StepSecurity Harden-Runner, yang melewati perlindungan CI/CD melalui langkah-langkah berikut:
- Periksa apakah socket Docker /var/run/docker.sock tersedia;
- Daftarkan kontainer Docker, cocokkan kata kunci harden-runner / stepsecurity di nama atau gambar;
- Menghentikan kontainer yang cocok melalui POST /containers//kill;
- Buat kontainer alpine yang memiliki hak istimewa, lalu ubah sudoers menjadi runner ALL=(ALL) NOPASSWD:ALL;
- Tulis entri resolusi tidak valid untuk domain terkait StepSecurity ke /etc/hosts;
- Timpa /etc/resolv.conf dengan 8.8.8.8 dan 1.1.1.1.
Logika pertahanan ini menunjukkan bahwa sampel tersebut secara khusus disesuaikan untuk lingkungan CI/CD yang dilindungi oleh StepSecurity.
9. EDR / Deteksi produk keamanan, penilaian lingkungan, dan penghindaran:
Payload berbahaya utama mendeteksi nama proses dan jalur instalasi berbagai produk EDR (Endpoint Detection and Response) / keamanan:

Payload juga mencakup logika penghindaran wilayah: mendeteksi variabel lingkungan LC_ALL, LC_MESSAGES, LANGUAGE, LANG; jika setelah diubah menjadi huruf kecil dimulai dengan ru, maka eksekusi dilewati. Dukungan identifikasi lingkungan CI mencakup GitHub Actions, GitLab CI, Travis CI, CircleCI, Jenkins, AWS CodeBuild, Buildkite, AppVeyor, Bitbucket, Drone, TeamCity, Cirrus CI, dll. Tanda status yang digunakan selama runtime meliputi tmp.0987654321.lock, __IS_DAEMON (menandai proses anak daemon terpisah), SKIP_DOMAIN (melewati jalur sender domain), /tmp/kitty-*, cat.py, dan /var/tmp/.gh_update_state.
Analisis dampak
Dari kemampuan sumber kode, dampak ketiga sampel ini tidak terbatas pada eksekusi sekali saja selama tahap pemasangan npm. Risiko aktualnya dapat dibagi menjadi empat lapisan:
Pada tingkat host pengembang. Sampel akan mengumpulkan variabel lingkungan, .npmrc, .pypirc, kunci SSH, konfigurasi Docker, .env, token GitHub CLI, data manajer kata sandi, dan file dompet, serta mempertahankan kemampuan pemicu lanjutan melalui Claude Code, VS Code, systemd user service, atau macOS LaunchAgent.
Pada tingkat CI/CD Runner. Sampel akan mengenali GitHub Actions Linux runner, membaca memori proses Runner.Worker, dan mengekstrak rahasia yang disembunyikan; sekaligus memiliki logika对抗 StepSecurity Harden-Runner, berusaha merusak atau melewati komponen perlindungan CI/CD.
Pada tingkat organisasi dan repositori GitHub. Sampel dapat menggunakan GitHub API untuk membuat repositori, menulis ke contents/results/, mengoperasikan git refs/blobs/trees/commits, menyisipkan workflow berbahaya, serta mengangkut secret melalui Run Copilot workflow + artifact.
Pada tingkat penyebaran ekosistem npm. Sampel dapat difilter berdasarkan token npm yang memiliki izin write package dan bypass_2fa === true, atau dapat memanfaatkan jalur GitHub Actions OIDC / npm trusted publishing untuk memperoleh kemampuan publikasi; selanjutnya unduh tarball target, tulis loader jahat, ubah preinstall, tambahkan dependensi Bun, tingkatkan versi patch, dan terbitkan, sehingga membentuk rantai penyebaran otomatis di npm.
Summary
Bukti kode sumber dari tiga sampel menunjukkan bahwa paket jahat ini bukan hanya skrip pencurian informasi pada tahap instalasi, melainkan kombinasi loader multi-tahap dan implant lengkap: lapisan luar dirandomisasi sesuai paket, sedangkan pengarah lingkungan runtime Bun dan beban jahat inti tetap konsisten; implant utama mencakup berbagai tahap seperti pengumpulan kredensial, ekstraksi secret CI, penyebaran melalui GitHub/npm, persistensi, dan penghindaran terhadap perlindungan.
Desain pintu masuk yang sangat tersembunyi. Penyerang tidak menyisipkan logika jahat dalam kode bisnis, tetapi memasang loader yang diacak dalam skrip lifecycle npm. Loader ini sendiri terutama bertanggung jawab untuk mendekripsi dan mengeksekusi beban tersemat, sehingga sulit mendeteksi kemampuan sebenarnya hanya dengan meninjau kode sumber bisnis.
Rantai deobfuscasi bersarang lima lapis. Beban berbahaya melalui lima lapisan pengemasan: array angka + substitusi huruf ROT, enkripsi AES-128-GCM, obfuscasi obfuscator.io, enkripsi string khusus B5, dan lapisan bersarang AES-256-GCM + gzip. Kunci atau parameter pada setiap lapisan dapat berubah secara independen di berbagai paket, sehingga membuat deteksi massal berbasis fitur statis menjadi lebih sulit.
Kemampuan serangan tingkat organisasi lengkap. Implant ini memiliki kemampuan membaca memori GitHub Actions Runner, mengumpulkan kredensial multi-cloud dan lokal, mengekstrak data melalui GitHub API dan dead-drop, menginfeksi repositori serta workflow GitHub, menyebar secara mandiri melalui npm, serta mempertahankan persistensi dan menghadapi upaya deteksi. Dari struktur sumber codenya, implant ini telah memiliki jalur kode yang memungkinkan penyebaran lebih lanjut dari instalasi titik tunggal ke repositori, CI/CD, dan rantai rilis npm; cakupan penyebaran sebenarnya masih perlu dikonfirmasi melalui log instalasi, audit repositori, dan telemetry sisi platform.
Berdasarkan bukti kode sumber, dapat dikonfirmasi bahwa ketiga sampel tersebut secara otomatis dipicu melalui preinstall dan mendekripsi serta menjalankan implant utama yang sama. Namun, tidak dapat dibuktikan hanya dengan ketiga sampel tgz ini bagaimana akses awal diperoleh dalam insiden sebenarnya.
Rekomendasi penanganan
- Telusuri dan hapus versi jahat di ketergantungan proyek, lockfile, cache registry pribadi, dan cache build.
- Periksa log instalasi apakah muncul preinstall, node index.js, bun run, /tmp/p*.js, tmp.0987654321.lock.
- Jangan langsung mencabut token pada host korban yang masih mungkin menjalankan payload. Disarankan untuk terlebih dahulu mengisolasi host korban, membersihkan proses yang berjalan dan item persistensi, lalu mengganti token terkait GitHub, npm, kredensial cloud, Kubernetes, Vault, SSH, Docker registry, dan manajer kata sandi dari lingkungan bersih.
- Periksa branch, commit, workflow, artifact, dan repository baru di repositori GitHub, dengan fokus pada kata kunci seperti Run Copilot, format-results, chore/add-codeql-static-analysis, .github/workflows/codeql.yml, OIDC_PACKAGES.
- Periksa apakah direktori proyek ditambahkan atau diubah: .claude/settings.json, .claude/setup.mjs, .vscode/tasks.json, .vscode/setup.mjs.
- Periksa persistensi tingkat pengguna: /.local/share/kitty/cat.py, /.config/systemd/user/kitty-monitor.service, ~/Library/LaunchAgents/com.user.kitty-monitor.plist, file terkait gh-token-monitor.
- Periksa riwayat npm publish, pastikan tidak ada rilis patch yang tidak sah; selain itu, audit metadata token npm, dengan fokus pada token yang dapat melewati 2FA (Two-Factor Authentication) dan memiliki izin write paket.
- Lakukan audit integritas terhadap produk turunan yang dibangun di lingkungan yang terkontaminasi.
IOC
File berbahaya
filename: redhat-cloud-services-frontend-components-config-6.11.3.tgz MD5: 633ad8849a59e2bfb7a0fe589e816a07 SHA1: 675294612f455fe6a9acb195f0cbe3687d8e2e34 SHA256: 0c9c67ec40d5f23efa1ec3470d0ac88b4993ccc0e92be913fc29a337dfc4f060
filename: redhat-cloud-services-types-3.6.1.tgz MD5: 9e6c5af01438b52c9a411686c1f1b8ff SHA1: 88d098c8d96e9ae17550e9798c3b62c420464b8c SHA256: d543bb3cdf1569c2b3d38c8a4081ed746cfe78bf3236c2302704d79ab7fa9558
filename: redhat-cloud-services-rule-components-4.7.2.tgz MD5: f1ffdbf5e639899f26a6ebab2eec408d SHA1: f3c5c21274045ae02fef11e931de6dcf8462a067 SHA256: aaf00d06baa3c679b82452c50014e9824b8874e9ca2d150f19095f8de19ba90f
SHA256
ac2a2208e1726e008be6c73dc0872d9bba163319259dff1b62055ac933ca46b6
0dc06ecdaa63fe24859cfd955053c23245c536e4733480239d14bebf12688e35
Ketergantungan jahat
npm:@redhat-cloud-services/topological-inventory-client@3.0.10
npm:@redhat-cloud-services/topological-inventory-client@3.0.11
npm:@redhat-cloud-services/topological-inventory-client@3.0.13
npm:@redhat-cloud-services/compliance-client@4.0.3
npm:@redhat-cloud-services/compliance-client@4.0.4
npm:@redhat-cloud-services/compliance-client@4.0.6
npm:@redhat-cloud-services/rbac-client@9.0.3
npm:@redhat-cloud-services/rbac-client@9.0.4
npm:@redhat-cloud-services/rbac-client@9.0.6
npm:@redhat-cloud-services/insights-client@4.0.4
npm:@redhat-cloud-services/insights-client@4.0.5
npm:@redhat-cloud-services/insights-client@4.0.7
npm:@redhat-cloud-services/frontend-components@7.7.2
npm:@redhat-cloud-services/frontend-components@7.7.3
npm:@redhat-cloud-services/frontend-components@7.7.5
npm:@redhat-cloud-services/frontend-components-utilities@7.4.1
npm:@redhat-cloud-services/frontend-components-utilities@7.4.2
npm:@redhat-cloud-services/frontend-components-utilities@7.4.4
npm:@redhat-cloud-services/remediations-client@4.0.4
npm:@redhat-cloud-services/remediations-client@4.0.5
npm:@redhat-cloud-services/remediations-client@4.0.7
npm:@redhat-cloud-services/frontend-components-notifications@6.9.2
npm:@redhat-cloud-services/frontend-components-notifications@6.9.3
npm:@redhat-cloud-services/frontend-components-notifications@6.9.5
npm:@redhat-cloud-services/patch-client@4.0.4
npm:@redhat-cloud-services/patch-client@4.0.5
npm:@redhat-cloud-services/patch-client@4.0.7
npm:@redhat-cloud-services/host-inventory-client@5.0.3
npm:@redhat-cloud-services/host-inventory-client@5.0.4
npm:@redhat-cloud-services/host-inventory-client@5.0.6
npm:@redhat-cloud-services/rule-components@4.7.2
npm:@redhat-cloud-services/rule-components@4.7.3
npm:@redhat-cloud-services/rule-components@4.7.5
npm:@redhat-cloud-services/frontend-components-advisor-components@3.8.2
npm:@redhat-cloud-services/frontend-components-advisor-components@3.8.4
npm:@redhat-cloud-services/frontend-components-advisor-components@3.8.6
npm:@redhat-cloud-services/notifications-client@6.1.4
npm:@redhat-cloud-services/notifications-client@6.1.5
npm:@redhat-cloud-services/notifications-client@6.1.7
npm:@redhat-cloud-services/sources-client@3.0.10
npm:@redhat-cloud-services/sources-client@3.0.11
npm:@redhat-cloud-services/sources-client@3.0.13
npm:@redhat-cloud-services/integrations-client@6.0.4
npm:@redhat-cloud-services/integrations-client@6.0.5
npm:@redhat-cloud-services/integrations-client@6.0.7
npm:@redhat-cloud-services/frontend-components-config@6.11.3
npm:@redhat-cloud-services/frontend-components-config@6.11.4
npm:@redhat-cloud-services/frontend-components-config@6.11.6
npm:@redhat-cloud-services/frontend-components-config-utilities@4.11.2
npm:@redhat-cloud-services/frontend-components-config-utilities@4.11.3
npm:@redhat-cloud-services/frontend-components-config-utilities@4.11.5
npm:@redhat-cloud-services/hcc-pf-mcp@0.6.1
npm:@redhat-cloud-services/hcc-pf-mcp@0.6.2
npm:@redhat-cloud-services/hcc-pf-mcp@0.6.4
npm:@redhat-cloud-services/frontend-components-remediations@4.9.2
npm:@redhat-cloud-services/frontend-components-remediations@4.9.3
npm:@redhat-cloud-services/frontend-components-remediations@4.9.5
npm:@redhat-cloud-services/eslint-config-redhat-cloud-services@3.2.1
npm:@redhat-cloud-services/eslint-config-redhat-cloud-services@3.2.2
npm:@redhat-cloud-services/eslint-config-redhat-cloud-services@3.2.4
npm:@redhat-cloud-services/javascript-clients-shared@2.0.8
npm:@redhat-cloud-services/javascript-clients-shared@2.0.9
npm:@redhat-cloud-services/javascript-clients-shared@2.0.11
npm:@redhat-cloud-services/quickstarts-client@4.0.11
npm:@redhat-cloud-services/quickstarts-client@4.0.12
npm:@redhat-cloud-services/quickstarts-client@4.0.14
npm:@redhat-cloud-services/config-manager-client@5.0.4
npm:@redhat-cloud-services/config-manager-client@5.0.5
npm:@redhat-cloud-services/config-manager-client@5.0.7
npm:@redhat-cloud-services/hcc-feo-mcp@0.3.1
npm:@redhat-cloud-services/hcc-feo-mcp@0.3.2
npm:@redhat-cloud-services/hcc-feo-mcp@0.3.4
npm:@redhat-cloud-services/entitlements-client@4.0.11
npm:@redhat-cloud-services/entitlements-client@4.0.12
npm:@redhat-cloud-services/entitlements-client@4.0.14
npm:@redhat-cloud-services/tsc-transform-imports@1.2.2
npm:@redhat-cloud-services/tsc-transform-imports@1.2.4
npm:@redhat-cloud-services/tsc-transform-imports@1.2.6
npm:@redhat-cloud-services/hcc-kessel-mcp@0.3.1
npm:@redhat-cloud-services/hcc-kessel-mcp@0.3.2
npm:@redhat-cloud-services/hcc-kessel-mcp@0.3.4
npm:@redhat-cloud-services/frontend-components-testing@1.2.1
npm:@redhat-cloud-services/frontend-components-testing@1.2.2
npm:@redhat-cloud-services/frontend-components-testing@1.2.4
npm:@redhat-cloud-services/types@3.6.1
npm:@redhat-cloud-services/types@3.6.2
npm:@redhat-cloud-services/types@3.6.4
npm:@redhat-cloud-services/chrome@2.3.1
npm:@redhat-cloud-services/chrome@2.3.2
npm:@redhat-cloud-services/chrome@2.3.4
npm:@redhat-cloud-services/frontend-components-translations@4.4.1
npm:@redhat-cloud-services/frontend-components-translations@4.4.2
npm:@redhat-cloud-services/frontend-components-translations@4.4.4
npm:@redhat-cloud-services/vulnerabilities-client@2.1.8
npm:@redhat-cloud-services/vulnerabilities-client@2.1.9
npm:@redhat-cloud-services/vulnerabilities-client@2.1.11
