भविष्य को आकार देने वाली तकनीक पर गहन लेख।

Cursor Marketplace बैकडोर: VS Code फ़ॉर्क ट्रस्ट समस्या

Cursor Marketplace पर Claude Code एक्सटेंशन का बैकडोर दिखाता है कि VS Code फ़ॉर्क Microsoft का एक्सटेंशन ट्रस्ट मॉडल विरासत में क्यों नहीं ले सकते। अभी क्या जाँचें।

एक लैपटॉप जिस पर एक्सटेंशन टाइलों का ग्रिड दिख रहा है, एक टाइल टूटी हुई और लाल रोशनी के साथ, और उसकी छाया कीबोर्ड तक पहुँचती हुई
एक्सटेंशन ग्रिड बिल्कुल एक जैसा दिखता है, चाहे उसके पीछे की टाइल साफ़ हो या उसमें payload हो।

एक डेवलपर मंगलवार सुबह Cursor खोलता है, Claude Code एक्सटेंशन का अपडेट दिखता है, और वह उसे इंस्टॉल होने देता है। कुछ अलग नहीं लगता। एक्सटेंशन लोड होता है, AI असिस्टेंट काम करता है, और दोपहर तक मशीन में हमलावर की Ed25519 की ~/.ssh/authorized_keys में पहुँच चुकी होती है, एक रिवर्स SSH टनल इंटरनेट पर एक सर्वर से बीकन भेज रही होती है, और एक LaunchAgent जो खुद को Apple सिस्टम प्रोसेस बताता है, रीबूट के बाद भी सब कुछ चालू रखता है। यही वह Cursor Marketplace बैकडोर है जिसे Phorion ने उजागर किया, और यह अब तक का सबसे साफ़ उदाहरण है उस बात का जिसके बारे में सुरक्षा से जुड़े हम लोग सालों से बुदबुदाते रहे हैं: VS Code फ़ॉर्क Microsoft का कोडबेस तो विरासत में पाते हैं, पर उसका ट्रस्ट मॉडल नहीं।

मैंने अपने करियर का अच्छा-खासा हिस्सा डेवलपर मशीनों पर incident response करने में बिताया है, और यह घटना इसलिए चुभती है क्योंकि डिलीवरी का तरीका कितना आम था। कोई phishing नहीं। कोई zero-day नहीं। बस एक छेड़छाड़ किया गया एक्सटेंशन बंडल, वर्ज़न 2.1.312 और 2.1.313, एक ऐसे marketplace पर प्रकाशित जिस पर डेवलपर डिफ़ॉल्ट रूप से भरोसा करते हैं, क्योंकि वह उसी चीज़ जैसा दिखता है जिस पर वे पहले से भरोसा करते हैं। Anthropic के अपने चैनल साफ़ थे — किसी ने Cursor की publishing pipeline तक पहुँच का इस्तेमाल करके उस एक्सटेंशन की संशोधित कॉपी शिप कर दी, जो Anthropic ने उस registry के लिए कभी बनाई ही नहीं थी। प्रभाव क्षेत्र हर वह Cursor उपयोगकर्ता था जिसने ग्यारह घंटे की खिड़की में ऑटो-अपडेट किया।

बैकडोर ने असल में क्या किया

इम्प्लांट minified extension.js बंडल के ऊपर जोड़ा गया एक immediately invoked function expression था। इंजेक्ट किए गए prefix के बाद का सब कुछ सामान्य एक्सटेंशन जैसा दिखता था — वही activate और deactivate exports, वही UI कोड, वही स्ट्रिंग्स। यही वह हिस्सा है जो आपको परेशान करना चाहिए अगर आपकी detection रणनीति "बंडल को upstream से diff करो" है: दुर्भावनापूर्ण हिस्सा कुछ सौ बाइट्स का अतिरिक्त JavaScript है, जो मेगाबाइट्स लंबी एक वैध फ़ाइल के ऊपर बैठा है। यह तब चलता है जब Node मॉड्यूल लोड करता है, एक्सटेंशन के अपने activate callback के फ़ायर होने से पहले। उपयोगकर्ता को कुछ क्लिक करने, Claude को invoke करने, या एक्सटेंशन से किसी भी तरह बातचीत करने की ज़रूरत नहीं होती।

पेलोड खुद एक चार-चरणीय काम है, और ईमानदारी से कहूँ तो काम सक्षम है:

  • Key planting. एक attacker-controlled Ed25519 public key जोड़ता है, जिस पर कमेंट svc-telemetry है, ~/.ssh/authorized_keys में सही 0700/0600 permissions के साथ।
  • SSH enablement. सुनिश्चित करने की कोशिश करता है कि sshd चल रहा हो — Linux पर systemctl, macOS पर Remote Login, Windows पर PowerShell के ज़रिए OpenSSH Server।
  • Reverse tunnel. एक operator-controlled सर्वर से outbound SSH कनेक्शन खोलता है -R *:0:localhost:22 के साथ, ताकि हमलावर टनल के ज़रिए पीड़ित के port 22 तक पहुँच सके और planted key से लॉग इन कर सके। किसी custom C2 protocol की ज़रूरत नहीं — SSH ही C2 है।
  • Persistence और recon. macOS पर com.apple.sysmond.plist नाम का user LaunchAgent, Linux पर @reboot cron entry, Windows पर Startup folder fallback। फिर hostname, username, git email, public IP, और अधिकतम चालीस environment variable नामों वाला एक बार का beacon, जो /KEY|TOKEN|SECRET|PASS|AWS|GCP/i से मेल खाते हैं।

यह आखिरी item एक ऐसे तरीके से चालाक है जिसे समझना ज़रूरी है। बीकन आपकी AWS secret key बाहर नहीं भेजता — वह इस तथ्य को भेजता है कि आपके पास एक है। यह एक targeting inventory है। ऑपरेटर को पता चल जाता है कि कौन-सी compromised मशीनें cloud engineers की हैं जिनके environment में live credentials हैं, और टनल के ज़रिए सबसे पहले किसके पास वापस जाना है, यही तय होता है।

वह macOS बग जिसने अनजाने में लोगों को बचा लिया

Incident responder के रूप में मुझे जो बात गहराई से मज़ेदार लगती है: malware का macOS enablement path एक क्लासिक scripting गलती से टूटा हुआ है। कोड unprivileged user के रूप में systemsetup -getremotelogin चलाता है और Remote Login चालू करने की कोशिश तभी करता है जब आउटपुट /Off/i से मेल खाता हो। लेकिन macOS पर यह command administrator access के बिना चलने से इनकार करता है। यह stdout पर "You need administrator access to run this tool... exiting!" प्रिंट करता है — और status 0 के साथ exit करता है।

// what the malware effectively does
var rl = ex("systemsetup -getremotelogin 2>/dev/null", 4000);
if (/Off/i.test(rl)) {
det("sudo -n systemsetup -setremotelogin on >/dev/null 2>&1");
}

error string में "Off" नहीं है, इसलिए regex फ़ेल होता है, इसलिए enablement step कभी नहीं चलता। स्क्रिप्ट "मुझे यह जाँचने की अनुमति नहीं है कि SSH बंद है या नहीं" को "SSH पहले से चालू है" मान लेती है। फ़ेल होते command से exit code 0 शेल scripting के सबसे पुराने footguns में से एक है, और इस बार इसने किसी sysadmin की जगह malware लेखक को काट लिया। ऐसे Mac पर जिस पर Remote Login पहले से चालू नहीं है, रिवर्स टनल बाहर तक ठीक जुड़ता है, पर टनल से आगे भेजी गई किसी भी चीज़ को बंद port 22 मिलता है। Keys और persistence फिर भी छोड़ दिए जाते हैं — मशीन फिर भी compromised है — लेकिन interactive access का रास्ता शुरू होते ही मर जाता है।

इससे आराम मत लीजिए। अगर उपयोगकर्ता के पास पहले से Remote Login चालू था, या वह बाद में चालू करता है, तो planted key authorized_keys में इंतज़ार कर रही है। Windows path में भी ऐसा ही बग है (यह एक PowerShell command को एक helper से pipe करता है जो /bin/sh को hardcode करता है), और Linux cron logic बार-बार चलने पर उपयोगकर्ता का मौजूदा crontab मिटा सकता है। इंसानों के लिखे malware में इंसानी बग होते हैं। अगले महीने वाले में नहीं होंगे।

फ़ॉर्क्ड Marketplaces Microsoft का ट्रस्ट मॉडल विरासत में क्यों नहीं पा सकते

यही संरचनात्मक बात है, और इसे मैं engineering leads के दिमाग में बिठाना चाहता हूँ। जब आप आधिकारिक Visual Studio Marketplace से एक्सटेंशन इंस्टॉल करते हैं, तो उसके पीछे एक असली pipeline होती है: domain verification वाले publisher accounts, publish पर scanning, takedown प्रक्रियाएँ, और दूसरी तरफ़ एक कंपनी जिसका legal department है। यह बिल्कुल सही नहीं है — दुर्भावनापूर्ण एक्सटेंशन उससे निकल चुके हैं — पर यह एक सिस्टम है।

Cursor जैसे VS Code फ़ॉर्क लाइसेंसिंग कारणों से उस marketplace का उपयोग नहीं कर सकते, इसलिए वे वैकल्पिक registries की ओर इशारा करते हैं — Open VSX या अपना खुद का infrastructure। editor UI बाइट-दर-बाइट परिचित है। एक्सटेंशन पेज वही नाम, वही आइकन, एक विश्वसनीय लगने वाला publisher string दिखाता है। पर मूल marketplace को जिन नियंत्रणों ने भरोसेमंद बनाया था, उनमें से हर एक या तो दोबारा लिखा गया है, या है ही नहीं। Publisher को किसने verify किया? Bundle को किसने scan किया? Artifact उसी source repo से बना था जिसका वह दावा करता है, और क्या आप इसे जाँच सकते हैं? इस घटना में छेड़छाड़ वाले वर्ज़न शायद Microsoft के registry पर कभी थे ही नहीं — वे सिर्फ़ फ़ॉर्क पर थे। भरोसा पूरी तरह दिखावटी था।

हम package-registry की दुनिया में यही फ़िल्म पहले देख चुके हैं। LiteLLM PyPI compromise इसलिए काम कर गया क्योंकि कमज़ोर कड़ी publishing access है, कोड नहीं। एक्सटेंशन packages से एक ज़रूरी मायने में बदतर हैं: यहाँ lockfile की संस्कृति नहीं है, npm ci जैसी reproducibility नहीं है, hash pinning नहीं है। Editor चुपचाप auto-update करता है, और जैसा एक Hacker News commenter ने खोजा, VS Code का auto-update delay "trusted publishers" पर लागू ही नहीं होता — एक ऐसी सूची जिसे आप configure नहीं कर सकते। आपका एक्सटेंशन सेट एक लाइव, unpinned dependency tree है जो आपके पूरे user privileges के साथ चलता है और आप सोते समय खुद को अपडेट करता रहता है।

दो एक जैसी दुकानें, एक ठोस और दूसरी पीछे से टिकाए गए खोखले मुखौटे के रूप में उजागर
फ़ॉर्क्ड marketplace सामने से बिल्कुल एक जैसा दिखता है; उसके पीछे का trust infrastructure एक दिखावा हो सकता है।

AI Coding Tools एक privilege escalation हैं जिसे आपने चुना है

Marketplace के ड्रामे को हटाकर देखिए कि असल निशाना क्या था। एक AI coding एक्सटेंशन color theme नहीं है। वह IDE की permissions पर चलता है — जो आपकी permissions हैं — और उसका पूरा काम आपका source tree पढ़ना, आपके API credentials रखना, और तेज़ी से आपकी ओर से shell commands चलाना है। जब हमने agentic coding CLIs permissions कैसे संभालते हैं इस पर लिखा था, तो बार-बार आने वाली थीम यह थी कि उपयोगी agent और खतरनाक agent को policy अलग करती है, capability नहीं। जो एक्सटेंशन child_process चला सकता है, वह एक बुरे publish से कुछ भी चलाने से बस एक कदम दूर है।

एक commenter ने इसे सीधे कहा: इन टूल्स का उपयोग करने वाले सभी लोग असल में रोज़ curl-pipe-bash चला रहे हैं, और हमने एक दशक तक जूनियर डेवलपर्स को यही न करने के लिए कहा है। यह अतिशयोक्ति है, पर थोड़ी सी ही। curl | bash और ऑटो-अपडेट होने वाले agentic एक्सटेंशन में फ़र्क बस इतना है कि एक्सटेंशन का UI बेहतर है। मैंने पहले भी तर्क दिया है कि CLI tools को डिफ़ॉल्ट रूप से background daemons इंस्टॉल नहीं करने चाहिए; यह घटना वही है जब daemon किसी और ने इंस्टॉल किया और आपकी कोई सहमति ही नहीं ली गई। IDE बंद होने के बाद बचा रहने वाला detached ssh process ठीक वही व्यवहार पैटर्न है जिसे मैं reviews में चिह्नित करता हूँ, और इसे एक हमलावर ने भरोसेमंद update channel से भेजा।

अभी अपने fleet की जाँच कैसे करें

अगर आपके संगठन में कोई Cursor चलाता है, तो इसे समाचार नहीं, एक लाइव incident मानिए। फ़ॉरेंसिक write-up से सीधे लिए गए उच्च-संकेत indicators ये हैं:

# macOS / Linux: the planted key is the cleanest signal
grep -s svc-telemetry ~/.ssh/authorized_keys && echo "IMPACTED" || echo "key not found"
# Check persistence artifacts
ls ~/Library/LaunchAgents/com.apple.sysmond.plist 2>/dev/null   # macOS
ls ~/.config/.smt ~/.config/.smtk 2>/dev/null                   # macOS + Linux
crontab -l 2>/dev/null | grep -F .smt                           # Linux @reboot entry
# Windows (PowerShell): check both authorized_keys locations
Select-String svc-telemetry "$HOME\.ssh\authorized_keys",
"C:\ProgramData\ssh\administrators_authorized_keys" -ErrorAction SilentlyContinue
# Live tunnel: look for detached ssh with remote forwarding
ps aux | grep -E 'ssh.*-R' | grep -v grep

अनुभव से कुछ hunting notes। पहला, सिर्फ़ कमेंट नहीं, key fingerprint जाँचिए — इम्प्लांट कमेंट स्ट्रिंग svc-telemetry पर dedupe करता है, और cleanup की कोशिश भी ऐसा कर सकती है। दूसरा, tunnel key फ़ाइल temp directory में .stk_ जैसे नाम के साथ रहती है, उसके बाद hostname और home path के MD5 से निकले दस hex characters; यह हर मशीन के लिए निश्चित है, इसलिए आप सटीक filename निकालकर उसे खोज सकते हैं। तीसरा, EDR process telemetry standard users के लिए macOS enablement प्रयास नहीं दिखाएगा, क्योंकि बग के कारण वह कभी execute ही नहीं होता। आपके logs में systemsetup -setremotelogin की अनुपस्थिति का मतलब compromise की अनुपस्थिति नहीं है। Key और LaunchAgent ही असली सबूत हैं।

अगर आपको key मिलती है, तो मशीन को पूरा IR इलाज मिलना चाहिए: हर उस credential को rotate कीजिए जिसका environment variable नाम बीकन हुआ हो सकता है (जो अधिकतर होंगे — regex AWS, GCP, और नाम में KEY या TOKEN वाली हर चीज़ पकड़ता है), मशीन की access revoke कीजिए, और reimage कीजिए। सिर्फ़ key मिटाकर बात खत्म मत मानिए। ऑपरेटर के पास interactive shell access था, या उसने पाने की कोशिश की थी; इम्प्लांट के code से अकेले आप यह नहीं गिन सकते कि उसने क्या छुआ। यहीं cloud authentication logging की blind spots आपको काटती हैं — एक्सटेंशन अपडेट और disclosure के बीच की खिड़की में प्रभावित उपयोगकर्ता की credentials से console या API उपयोग के लिए अपने cloud audit logs जाँचिए।

एक टॉर्च की रोशनी चाबियों के एक गुच्छे में से एक लाल चमकती चाबी उठाती हुई, जिसकी केबल अँधेरे में खिंची जा रही है
Planted key को उसके label से नहीं, fingerprint से खोजिए — label ही वह पहली चीज़ है जिसे cleanup मिटाता है।

Hardening: सिर्फ़ एक्सटेंशन नहीं, Editor को Contain कीजिए

Incident के बाद सब पूछते हैं कि क्या बदलें। मेरा रुख यह है, और मैं इसका बचाव करूँगा: आप कभी भी audit करके सुरक्षित extension ecosystem तक नहीं पहुँचेंगे, इसलिए आप contain करते हैं। AI agents के लिए containment बनाम monitoring पर हमने सामान्य trade-off पर बात की थी, और यह घटना उसी containment का केस स्टडी है। यहाँ Monitoring ने तब तक कुछ नहीं बताया जब तक कोई third-party security firm संयोग से बंडल का विश्लेषण नहीं कर बैठी।

  • एक्सटेंशन versions pin कीजिए और auto-update बंद कीजिए उन सभी के लिए जिनके पास व्यापक permissions हैं, और यही नियम सभी forks पर लागू कीजिए। हाँ, update fatigue झेलनी पड़ेगी। यह incident response से सस्ता है।
  • Developer workstations पर egress filtering। यहाँ पूरी access model एक VPS IP से outbound SSH कनेक्शन पर निर्भर थी। अगर आपकी dev मशीनें मनमाने outbound port 22 नहीं खोल सकतीं, तो tunnel वाला चरण मर जाता है। लगभग कोई यह नहीं करता, और यह इस implant को पूरी तरह बेअसर कर देता।
  • SSH state को canary की तरह देखिए। Remote Login चालू होने, authorized_keys में नई keys आने, और उन मशीनों पर sshd processes पर अलर्ट कीजिए जिन्हें sshd चलाने का कोई कारण नहीं है।
  • AI tooling की inventory वैसे ही बनाइए जैसे आप servers की बनाते हैं। जानिए कि कौन-से extensions और agent CLIs स्वीकृत हैं, उनके पास कौन-सी permissions हैं, और वे किस marketplace से आते हैं। अगर "हमारे Cursor installs किस registry से आते हैं" का जवाब कंधे उचकाना है, तो यही आपकी अगली तिमाही का प्रोजेक्ट है।
  • जहाँ संभव हो, sandboxes या containers में चलने वाले agents चुनिए, जिनमें credentials हर session में अलग से inject हों, न कि process environment में खुले पड़े रहें। बीकन का env-name scraping इसीलिए काम करता है क्योंकि हम process environments में secrets यूँ ही छोड़ देते हैं।

बिना अपनी verification pipeline वाला extension marketplace marketplace नहीं है — वह एक install बटन वाली file host है।

असहज निष्कर्ष

Anthropic की कोई breach नहीं हुई। Cursor का marketplace हुआ, और फिर भी Anthropic के ब्रांड को नुकसान उठाना पड़ा, क्योंकि डेवलपर के लिए एक्सटेंशन ही product है, चाहे किसी ने कौन-सा build publish किया हो। फ़ॉर्क्ड ecosystems का अर्थशास्त्र यही है: किसी उच्च-मूल्य लक्ष्य पर हमला करने का सबसे सस्ता तरीका है उस सबसे कमज़ोर registry पर हमला करना जिस पर उस लक्ष्य के उपयोगकर्ता भरोसा करते हैं। AI coding assistants ठीक उसी चौराहे पर बैठते हैं — विशाल install base, अधिकतम local privilege, हर जगह credentials, और एक supply chain जो उनके artifacts को बिल्कुल अलग security स्तरों वाले registries में फ़ॉर्क और दोबारा होस्ट करती है।

यह मामला छेड़छाड़ वाले वर्ज़न लगभग ग्यारह घंटे में हटाए जाने और एक macOS बग से नुकसान सीमित होने पर खत्म हुआ। प्रतिक्रिया ठीक थी। समस्या आर्किटेक्चर की है, और वह नहीं बदली: आपका editor एक package manager है जो जो भी इंस्टॉल करता है उसे आपके रूप में चलाता है, उसे चुपचाप अपडेट करता है, और बढ़ती संख्या में setups में ऐसे registry से packages लेता है जिसका आपकी security team ने कभी आकलन नहीं किया। Egress rules ठीक कीजिए, extensions pin कीजिए, और agents की inventory बनाइए। अगला publisher-compromise उपयोगकर्ताओं को किसी regex बग के भरोसे नहीं बचाएगा।