Shai-Hulud मैलवेयर हमला: Tinycolor और 40 से अधिक NPM पैकेज संक्रमित
(stepsecurity.io)- NPM ecosystem में लोकप्रिय @ctrl/tinycolor सहित 40 से अधिक पैकेजों में self-propagating malware इंजेक्ट किया गया, जिससे development environment के secrets और CI/CD credentials तक श्रृंखलाबद्ध संक्रमण फैल सकने वाला supply chain attack हुआ। संक्रमित versions को npm से हटा दिया गया है
- attack payload npm install प्रक्रिया के दौरान Webpack bundle (
bundle.js, ~3.6MB) को asynchronous तरीके से चलाता है, और environment variables, file system, तथा cloud SDKs के जरिए व्यापक credential collection करता है - malicious logic NpmModule.updatePackage के जरिए दूसरे पैकेजों को force patch और publish करके cascading propagation करता है, और GitHub Actions में shai-hulud workflow इंजेक्ट कर संगठन के secrets को
toJSON(secrets)के माध्यम से चुरा लेता है - एकत्रित data public GitHub repository
Shai-Huludबनाकर बाहर निकाला जाता है, और यह सामान्य development activity के रूप में छिप जाता है, जिससे detection से बचने की क्षमता अधिक होती है - AWS/GCP/Azure/NPM/GitHub tokens तथा metadata endpoints तक पहुंच, और TruffleHog आधारित secret discovery जैसी गतिविधियां चुपचाप की जाती हैं
- तुरंत package removal, repository cleanup, और सभी credentials rotation के साथ CloudTrail/GCP Audit logs की जांच, webhook blocking, branch protection/Secret Scanning/cooldown policies लागू करने की आवश्यकता है
Affected Packages
- कुल 195 packages/versions रिपोर्ट किए गए हैं। प्रमुख उदाहरणों में @ctrl/tinycolor(4.1.1, 4.1.2), @ctrl/ namespace के कई पैकेज, @crowdstrike/ modules, ngx-bootstrap/ngx-toastr/ng2-file-upload/ngx-color जैसे Angular/web UI ecosystem के पैकेज, @nativescript-community/ और @nstudio/ mobile stack, teselagen/ life sciences toolchain, ember-*, koa2-swagger-ui, pm2-gelf-json, wdio-web-reporter शामिल हैं
- हर पैकेज के सटीक versions के लिए मूल तालिका देखें और यह बारीकी से cross-check करें कि वे versions उपयोग में हैं या नहीं
- उदाहरण:
@ctrl/ngx-emoji-mart 9.2.1, 9.2.2,@ctrl/qbittorrent 9.7.1, 9.7.2,ngx-bootstrap 18.1.4, 19.0.3–20.0.5,ng2-file-upload 7.0.2–9.0.1आदि, यानी दायरा काफी व्यापक है
- उदाहरण:
Immediate Actions Required
Identify and Remove Compromised Packages
- project में संक्रमित packages की मौजूदगी जांचें:
npm ls @ctrl/tinycolorआदि से सत्यापित करें - संक्रमित packages को तुरंत हटाएं:
npm uninstall @ctrl/tinycolorजैसे command चलाएं - ज्ञात
bundle.jshashes खोजकर local traces जांचें:sha256sum | grep 46faab8a...का उपयोग करें
Clean Infected Repositories
- malicious GitHub Actions workflow हटाएं:
.github/workflows/shai-hulud-workflow.ymlको delete करें - remote पर बने shai-hulud branch को detect और delete करें:
git ls-remote ... | grep shai-huludके बादgit push origin --delete shai-huludचलाएं
Rotate All Credentials Immediately
- NPM tokens, GitHub PAT/Actions secrets, SSH keys, AWS/GCP/Azure credentials, DB connection strings, third-party tokens, CI/CD secrets आदि सबका पूर्ण rotation आवश्यक है
- AWS Secrets Manager/GCP Secret Manager में stored items सहित सब कुछ rotate करना आवश्यक है
Audit Cloud Infrastructure for Compromise
- AWS: CloudTrail में
BatchGetSecretValue,ListSecrets,GetSecretValuecalls के समय और patterns की जांच करें, और IAM Credential Report से असामान्य key creation/usage की पुष्टि करें - GCP: Audit Logs से Secret Manager access records की जांच करें, और
CreateServiceAccountKeyevents की मौजूदगी सत्यापित करें
1 टिप्पणियां
Hacker News राय
npm पर होस्ट किए गए पैकेजों का उपयोग करने वाले व्यक्ति के नज़रिए से, सभी dependencies और उनकी dependencies पर सीधे नज़र रखना व्यावहारिक तरीका नहीं है। मैं TypeScript/JavaScript विशेषज्ञ भी नहीं हूँ, इसलिए हमलावरों द्वारा छिपाया गया malware आसानी से पकड़ पाऊँगा, ऐसा नहीं लगता। हाल में मैं जिस बात पर सोच रहा हूँ, वह है updates को "delay mode" में करना — यानी latest version की जगह केवल उन versions पर update करना जो कुछ समय पुराने हों। तर्क यह है कि अगर कोई package लगभग 6 हफ्तों तक बाहर exposed रहा है, तो उसके malware होने की संभावना तब तक सामने आ चुकी होगी। यह कोई perfect तरीका नहीं है, लेकिन अच्छा होता अगर कोई tool ऐसा option देता कि security issue होने पर exception के तौर पर तुरंत latest update भी लागू किया जा सके।
लेख में सीधे उल्लेख किया गया तरीका यही है: NPM Package Cooldown Check नाम की एक सुविधा है। अगर संगठन द्वारा तय की गई अवधि (default 2 दिन) के भीतर release किया गया package version pull request में जोड़ा जाता है, तो build अपने-आप fail हो जाती है। ज़्यादातर supply chain attacks 24 घंटे के भीतर detect हो जाते हैं, इसलिए बहुत छोटा इंतज़ार भी security exposure कम कर सकता है।
सभी dependencies की पूरी जाँच करना मुश्किल है, इसलिए मैं यही कहना चाहूँगा कि जहाँ तक संभव हो dependencies की संख्या कम रखें, और केवल जाने-पहचाने व भरोसेमंद packages का उपयोग करें। बल्कि जब तक ऐसा controlled environment न हो जहाँ आप हर author पर भरोसा कर सकें, तब तक कुछ हद तक 'not-invented-here' बनाए रखना ही समझदारी है।
मेरी आदत है कि
package.jsonमें versions को स्पष्ट रूप से pin करके रखता हूँ, औरnpm ciका उपयोग करता हूँ ताकि केवलpackage-lock.jsonमें दर्ज versions ही install हों। CI मेंnpm auditचलाकर alert मिलता है अगर किसी package में vulnerability आ जाए। इससे packages लगभग "freeze" की स्थिति में रहते हैं, और सिर्फ package की उम्र ही infection की संभावना कम कर देती है।मेरे मामले में मैं इससे भी आगे जाता हूँ और dependency तभी update करता हूँ जब bug वास्तव में मेरे usage environment को प्रभावित करे। Security vulnerability हो लेकिन उसका असर मुझ पर न पड़े, तो मैं उसे छोड़ देता हूँ। ज़्यादातर developers अनावश्यक रूप से बहुत बार dependencies update करते हैं, जबकि सही यही है कि केवल ज़रूरत होने पर करें। अगर किसी package को बार-बार update करना पड़ता है या वह बहुत जटिल है, तो मैं या तो उसे इस्तेमाल ही नहीं करता, या अपने मानक के अनुसार उसे "freeze" कर देता हूँ।
Python के
uvसे भी इसी तरह updates सीमित किए जा सकते हैं। उदाहरण के लिएuv lock --exclude-newer $(date --iso -d "2 days ago")जैसे command से पिछले 2 दिनों में release हुए versions को exclude किया जा सकता है।ऐसी समस्याएँ इसलिए होती हैं क्योंकि नए packages या versions की पर्याप्त निगरानी नहीं होती। सबसे अच्छा तरीका Debian की तरह stable distribution को अलग चलाना है, जहाँ केवल security patches और bug fixes लागू हों, और testing/unstable distribution अलग हो जिसे package maintainers मॉनिटर करें। केंद्रीकृत open package repositories (NPM, Python, Rust आदि) से जुड़े सभी लोग इसी समस्या से जूझ रहे हैं।
developer culture में समस्या है। सैकड़ों (transitive) dependencies रखकर बिना सोचे-समझे auto-update करने की संस्कृति ही समस्या है। जब आप अपने build/runtime environment को इतने सारे third-party code के सामने खोलते हैं, तो उस चुनाव की ज़िम्मेदारी भी लेनी पड़ती है।
distributions पर भी maintain करने वाले packages की बढ़ती संख्या का बोझ महसूस हो रहा है। वास्तव में इसी कारण language-specific ecosystems (जैसे CPAN, Maven, RubyGems आदि) विकसित हुए। केवल Linux distributions के भरोसे users को उनके मनचाहे apps देना मुश्किल था, इसलिए freshmeat, linuxbrew, flatpak, PPA जैसी कई राहें बनीं। मुझे नहीं लगता कि हर community के पास इतनी क्षमता होती है कि वह असंख्य विविध libraries की कई branches को मॉनिटर और support कर सके।
Debian developer के रूप में मुझे लगता है कि upstream code शामिल करने से पहले बढ़ता हुआ "noise" (खासकर साधारण style changes, tooling updates) वास्तविक बदलावों को पहचानना बहुत कठिन बना देता है। काश ऐसे बदलावों से बचा जाए, जब तक वे सचमुच मानवीय review वाले refactoring, bug fixes, feature additions, या problematic code खोजने के लिए tooling output न हों।
Rust में
cargo vetनाम का एक system है। Google और Mozilla जैसी कंपनियाँ इसमें भाग लेकर packages को साझा रूप से verify करती हैं।मेरा मानना है कि decentralization बनाए रखते हुए भी कुछ safety guards लगाए जा सकते हैं। उदाहरण के लिए, एक निश्चित आकार से बड़े packages के लिए 2FA वाले दो accounts की approval अनिवार्य हो, या popular packages को केवल reproducible build system से ही npm upload करने दिया जाए। यानी पूरी तरह distributed model छोड़े बिना, सिर्फ बड़े projects के लिए थोड़ा अतिरिक्त effort जोड़ा जाए।
हाल के लगातार supply chain attacks की वजह से मैं server rendering (JavaScript के बिना) को अब ज़्यादा गंभीरता से सोचने लगा हूँ। HTMX की वजह से समझ आया कि JavaScript के बिना भी काफ़ी दूर तक जाया जा सकता है। ऐसा करने पर app ज़्यादा तेज़ और स्थिर भी हो सकता है।
मैं यह रेखांकित करना चाहता हूँ कि पारंपरिक JS environment वास्तव में सबसे सुरक्षित sandbox environments में से एक है। लगभग 30 साल से अरबों devices पर untrusted JS code चल रहा है, फिर भी browser engines में बड़े पैमाने पर सफल हमले बहुत कम हुए हैं। लेकिन NodeJS और npm environment security के लिहाज़ से पूरी तरह redesign माँगते हैं। leftpad जैसी घटनाएँ उस संस्कृति से निकलीं जहाँ साधारण code snippets तक npm पर publish कर दिए जाते हैं।
यह अजीब है कि ऐसे attacks की चर्चा अपने-आप केवल किसी एक environment (JavaScript) तक सीमित हो जाती है। असल में npm में जो security hardening measures मौजूद हैं, वे दूसरे environments (PyPI, Crates आदि) में बिल्कुल लागू नहीं हैं — यह अधिक बड़ी समस्या लगती है।
vendoring से exposure घटाया जा सकता है, लेकिन यह मूल समस्या का समाधान नहीं है। अगर NPM security को गंभीरता से लेता, तो publish के लिए 2FA और package pre-scan अनिवार्य करता, और hardware key से signing तक enforce करता। semver या CRC काफ़ी नहीं हैं। यह सब package management system के भीतर default रूप से built-in होना चाहिए।
सच तो यह है कि यह सिर्फ JavaScript की समस्या नहीं है; असली वजह यह है कि developers नई dependencies जोड़ते समय पर्याप्त निगरानी नहीं करते। यही बात Rust या Go जैसे अन्य language ecosystems पर भी लागू होती है।
जो भाषाएँ package managers पर बहुत अधिक निर्भर हैं और जिनकी standard library कमजोर है, वे सभी समान रूप से vulnerable हैं। लंबे समय में मुझे लगता है कि फिर से vanilla JavaScript की ओर लौटना चाहिए। Rust भी package dependencies पर बहुत निर्भर है। बल्कि इस मामले में Go एक आदर्श उदाहरण है।
मुझे लगता है कि एक ऐसी system की ज़रूरत है जो trusted keys से commits/releases पर sign करे, install और verification तक करे, और lightweight code को trace कर सके। npm में पहले से sigstore का उपयोग करने वाली provenance व्यवस्था है, लेकिन अभी यह व्यापक रूप से इस्तेमाल नहीं होती और शायद केवल publisher verification तक सीमित है।
2016 में ही NPM को यह vulnerability report की गई थी (CERT advisory), लेकिन NPM का जवाब WAI (working as intended) था।
जिन्हें WAI का मतलब नहीं पता: यह आम तौर पर “working as intended” का संक्षेप है।
शायद postinstall script न भी हो, तब भी build process, server startup, testing आदि में जैसे ही module import होगा, malware आखिरकार execute हो जाएगा। आखिर npm install के बाद किसी न किसी बिंदु पर कुछ वास्तविक रूप से चलना ही है...
left-pad घटना के समय यहाँ पढ़ी एक टिप्पणी याद आती है: एक प्रसिद्ध npm maintainer के पास 600 npm packages और 1,200 lines का JavaScript code था। उदाहरण के तौर पर मैं esbuild को सराहनीय मानता हूँ — लगभग कोई external dependency नहीं, और केवल Go standard library का उपयोग।
अन्य तथाकथित "next-gen" projects को भी dependency chain के लिहाज़ से देखें तो biomejs और swc भी काफ़ी कम dependencies रखते हैं। लेकिन Rust source code देखें तो अंततः biomejs और swc में भी बहुत dependencies हैं। अगर ऐसे projects फैलते गए, तो cargo ecosystem भी शायद उसी राह पर जाएगा। अगर किसी को esbuild जैसी सख़्त शैली में लिखा कोई बड़ा project पता हो, तो कृपया सुझाएँ।
Go पर जाने के कारणों में से एक purego शैली की library trend है। आम तौर पर वे standard library और
golang.org/xपर ही निर्भर रहती हैं, और CGO के बिना compile हो जाती हैं, इसलिए portability बहुत अच्छी होती है।go mod vendorसे short-term risk management हो सकता है, पर यह मूल समाधान नहीं है। Go में भी package verification (signing/key check आदि) end-to-end उपलब्ध नहीं है, इसलिए vulnerability बनी रहती है। खासकर बहुत ध्यान CI/CD infrastructure पर केंद्रित है; अगर signing keys को पास किए बिना build और deploy किया जा सके, तो security और मजबूत हो सकती है। मेरा मानना है कि package managers को GPG signatures को बढ़ावा देना चाहिए और git commits पर भी signatures अपनाकर distribute करना चाहिए।eslintका मामला खास तौर पर निराशाजनक लगता है। dependency graph देखें तो बहुत विशाल है, और अगर maintainer dependency reduction को प्राथमिकता नहीं देता, तो आखिर में दूसरे solution (oxlint) पर switch करना ही विकल्प बचता है।आसान features खुद बनाना और external dependencies कम करना ही समाधान है। आम तौर पर सिर्फ ऐसा करने से ही कुल dependencies का 2/3 कम किया जा सकता है। खासकर left-pad जैसी सरल चीज़ें खुद बनाकर, छोटे units और tests के साथ अपने नियंत्रण में रखना इतना बड़ा maintenance burden नहीं बनता। अनावश्यक dependencies को निःसंकोच हटाना चाहिए।
Rust project के root
Cargo.tomlमें जो दिखता है, वह पूरे workspace के लिए होता है; वास्तविक हर crate (package) की dependencies उससे कहीं उथली हो सकती हैं। असली dependency structure समझने के लिए और गहराई में देखना पड़ता है।कमी यह है कि अब JavaScript project की जाँच के लिए Golang भी पढ़ना पड़ेगा। ऊपर से post-install में फिर
node install.jsभी चलता है, इसलिए अंततः या तो पूरी तरह भरोसा करना होगा, या सारा code पढ़ना होगा।यकीन नहीं होता कि npm अभी भी default रूप से सभी dependencies के
postinstallscripts चलाता है। Pnpm या Bun इन्हें केवल allowlist में होने पर चलाते हैं, और Composer तो dependencies के लिए lifecycle scripts चलाता ही नहीं। मुझे लगता है कि build या development environment में dependency packages से आने वाले जोखिम को देखते हुए यह तरीका अधिक सुरक्षित है।जिज्ञासा है कि ऐसे बड़े attacks दूसरे package managers (जैसे Rust
build.rs, Python, Java आदि) में उतने सुनाई क्यों नहीं देते।postinstallही नहीं, सिद्धांत रूप में लगभग हर ecosystem में यह संभव है, फिर भी घटनाएँ npm-केंद्रित ही दिखाई देती हैं।मैंने देखा कि Pnpm का default scripts block करने में बदल गया है। community reaction जानने की उत्सुकता है — script allow करने को लेकर user experience कैसा है,
allowcommand का दुरुपयोग होता है या नहीं आदि। Python packaging community में भी wheel variants को लेकर कुछ मिलती-जुलती चर्चा चल रही है। दूसरे ecosystems के अनुभव जानना उपयोगी होगा।यह हमला अब 180 से अधिक packages तक फैल चुका है। विवरण के लिए Aikido Security ब्लॉग देखें।
मैं जानना चाहता हूँ कि इस attack को सबसे पहले किसने खोजा। अलग-अलग blogs अलग तरह से श्रेय देते हैं, जो दिलचस्प है। Aikido कहता है, “हमने एक large-scale attack खोजा,” और Socket, Ox, Safety, Phoenix, Semgrep आदि भी अपने-अपने तरीके से लिखते हैं।
मैं Aikido में काम करने वाला Mackenzie हूँ। इस मामले को सबसे पहले report करने वाले developer Daniel Pereira थे; उन्होंने यह Socket को बताया, और Socket ने शुरुआती 40 packages तथा malware का analysis किया। बाद में Aikido ने अतिरिक्त 147 packages और Crowdstrike package तक खोज निकाले। वास्तव में Step ने सबसे पहले पहचाना कि malware self-propagating worm है। यह दिलचस्प है कि कई संगठनों ने स्वतंत्र रूप से अलग-अलग भूमिकाएँ निभाईं।
लगता है कई developers ने लगभग एक ही समय पर इसे देखा। Step और Socket अलग-अलग लोगों का ज़िक्र करते हैं। अंततः industry security vendors ने AI code analysis (Socket, Aikido) या eBPF pipeline monitoring (Step) जैसे अपने-अपने तरीकों से इसे पकड़ा।
अगर इतने सारे vendors ने इसे स्वतंत्र रूप से detect कर लिया, तो क्या वे तकनीक npm के साथ सीधे साझा नहीं कर सकते थे ताकि malicious package registration ही block हो जाती? तब शायद vendors early warning system बेच नहीं पाते, इसलिए वे ऐसा नहीं करते — ऐसा संदेह होता है।
OP लेख सीधे यह पंक्ति quote करता है: “@franky47 ने इस phenomenon को खोजने के बाद तुरंत community को GitHub issue के माध्यम से सूचित किया।”
हमलावरों द्वारा दिया गया नाम 'Shai Hulud' काफ़ी चतुर लगता है — एक विशाल worm के नाम पर असली worm malware का नाम रखा गया। मुख्य
bundle.jsभी 3.6MB का है, यानी बहुत बड़ा। यहाँ तक कि malware variants भी npm-शैली में असामान्य रूप से भारी-भरकम हो गए हैं।मुझे लगता है कि जल्दी ही कोई एक supply chain attack संयोग से किसी दूसरे supply chain attack को भी trigger कर देगा।
malware भी Moore's law का पालन करता है। 1991 का tequila virus 2.6KB था, अब चीज़ें कई MB की हो गई हैं।