摘要 :2026年10月29日,Cursor 编辑器的扩展市场里出现了 Anthropic.claude-code 的恶意版本(2.1.312 / 2.1.313),安装后会向
~/.ssh/authorized_keys写入攻击者公钥、建立反向 SSH 隧道并落地持久化。本文拆解这次投毒的完整攻击链,给出可以直接跑的排查脚本,以及普通开发者该养成的三个习惯。如果你在用 Cursor 且装过 Claude Code 扩展,建议直接跳到第4节自查。
如果你这几天刷过 Hacker News,可能见过那个 562 分的帖子------安全公司 Phorion 披露:Cursor VS Code 扩展市场上的官方 Claude Code 扩展被投毒了。我第一反应是"又一个 npm 恶意包",点进去看完技术细节才发现,这次的东西比想象中精致:攻击者直接在官方扩展的 bundle 前面塞了一段注入代码 ,SHA-256 校验值是 ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15。
事件窗口只有约11小时(太平洋时间 10月29日 凌晨2:27 到下午1:35),但装上的人就是把自己的开发机钥匙递了出去。
1. 背景与痛点:为什么编辑器扩展是完美投毒载体
先交代背景。Cursor 是一个基于 VS Code 的 AI 编辑器,它有自己的扩展市场(Cursor Marketplace),格式和 VS Code 生态兼容。问题在于:扩展以 IDE 和当前用户的权限运行 。这意味着一个恶意扩展在 activate() 被调用的瞬间,就能:
- 读本地文件和源代码
- 访问开发工具链(git 凭证、SSH 密钥、云凭证环境变量)
- 写任意用户目录(包括
~/.ssh/)
对攻击者来说,开发机是性价比最高的目标------一台机器上往往同时躺着源代码、云账号凭证、CI 密钥。而大多数开发者对"装个扩展"这件事的心理防线,约等于零。
这次事件的诡异之处在于:恶意版本来自官方扩展的镜像分发渠道被污染,第三方冒充的痕迹反而没有。Cursor 官方声明明确说:这些版本不是 Anthropic 开发的,且只影响通过 Cursor 市场分发的 Windows 和 macOS 版本。Anthropic 官方渠道(VS Code 市场和 npm)的 2.1.313 虽然也被 Cursor 联动下架排查,但没有证据表明官方分发被波及。
2. 技术原理:三段式攻击链
2.1 注入方式
被篡改的 extension.js 在压缩后的正常 bundle 前面 追加了一个立即执行函数(IIFE)。也就是说,文件的其余部分------Claude Code 的正常代码、activate/deactivate 导出、UI 字符串------全都完好无损。扩展功能一切正常,杀毒扫描也看不出异常,这是供应链投毒的标准做法。
2.2 攻击链三阶段
恶意代码加载后分三步走:
阶段一:铺路。 收集主机信息(hostname、用户名、OS、架构、home 目录、环境变量名、工作目录、git 邮箱、公网 IP)并 POST 给攻击者服务器。同时在 ~/.ssh/authorized_keys 里追加一把 Ed25519 公钥(注释为 svc-telemetry)。注意这一步用的是 Node.js 的 fs.appendFileSync 直接写文件------不经过 shell ,所以那些监控 echo/tee 命令触碰 authorized_keys 的进程规则全部失效。
阶段二:打洞。 用内置的另一把私钥(注释 svc-telemetry-tun)连到攻击者服务器 134.209.82[.]225 的 22 端口,建立反向 SSH 隧道,参数是 -R *:0:localhost:22------让攻击者服务器动态分配端口,把流量转回受害者本机的 22 端口。攻击者想进入时,通过隧道用阶段一植入的密钥登录本地 sshd,拿到完整交互 shell。有 autossh 用 autossh,没有就用裸 ssh,外面包一个 60 秒重试的脱离循环,IDE 关了隧道也不断。
阶段三:赖着不走。 macOS 上伪装成系统服务 com.apple.sysmond.plist 放进 ~/Library/LaunchAgents/;Linux 写 @reboot crontab;Windows 建一个请求 SYSTEM 权限的名为 SysMon 的计划任务。持久化脚本藏在 ~/.config/.smt,私钥落在 ~/.config/.smtk。
flowchart TD
A[安装恶意扩展 2.1.312/2.1.313] --> B[IIFE 注入代码随 activate 执行]
B --> C[阶段一: 收集主机信息 POST 出去]
B --> D[阶段一: 向 authorized_keys 追加 svc-telemetry 公钥]
C --> E[阶段二: 反向 SSH 隧道连攻击者服务器]
D --> E
E --> F[阶段三: LaunchAgent/cron/计划任务 持久化]
F --> G[攻击者经隧道登录本地 sshd 拿 shell]
E -.macOS 特有 bug.-> H[systemsetup 权限判断错误<br>SSH 多数情况下没被真正开启]
2.3 一个救了所有人的 bug
最有意思的技术细节来了。macOS 上恶意代码想开启远程登录(SSH 服务),逻辑是:先检查当前状态,如果输出 Off 就执行 sudo -n systemsetup -setremotelogin on。
但它没料到一件事:普通用户执行 systemsetup -getremotelogin 会打印一条"需要管理员权限"的错误并以状态码 0 退出 ,而不是返回 Off。脚本一看输出不匹配 Off,直接跳过开启步骤。结果就是:在绝大多数没开过远程登录的 Mac 上,反向隧道虽然建立了,但隧道那头捅到的是本机一个关闭的 22 端口------攻击者进不来。
写恶意软件的人把"我没权限查看 SSH 是否关闭"理解成了"SSH 已经开着"。一个 if 判断的疏忽,砍掉了整条访问链的最后一环。当然,密钥照样植入、持久化照样落地、主机信息照样外泄,所以不能因此说没风险。
3. 环境准备:判断自己是否在影响范围内
先确认三件事:
- 你用的是 Cursor 编辑器(不是 VS Code 官方)------这次只影响 Cursor Marketplace 分发的版本
- 装过 Claude Code 扩展(扩展 ID:
anthropic.claude-code) - 版本号是
2.1.312或2.1.313,时间在 2026-10-29 前后
查看已装扩展目录:
# macOS / Linux
ls ~/.cursor/extensions/ | grep claude-code
# Windows (PowerShell)
Get-ChildItem "$env:USERPROFILE\.cursor\extensions" -Filter "*claude-code*"
如果列出来的目录是 anthropic.claude-code-2.1.313-darwin-arm64 这种带问题版本号的,继续往下走。
4. 实战实现:一键排查脚本
下面这个脚本覆盖了 Phorion 公开的全部主机侧 IOC,macOS 和 Linux 都能跑,Windows 用户看脚本后的手工清单:
#!/usr/bin/env bash
# claude-code-backdoor-check.sh ------ Cursor 市场 Claude Code 投毒自查
set -u
hit=0
check() { # $1=描述 $2=路径
if [ -e "$2" ]; then
echo "[!!] 命中: $1 -> $2"
hit=1
fi
}
# 1. 问题版本扩展目录
for d in ~/.cursor/extensions/anthropic.claude-code-2.1.31{2,3}*; do
[ -e "$d" ] && { echo "[!!] 命中: 恶意版本扩展存在 -> $d"; hit=1; }
done
# 2. authorized_keys 里的攻击者公钥
if grep -q "svc-telemetry" ~/.ssh/authorized_keys 2>/dev/null; then
echo "[!!] 命中: authorized_keys 含 svc-telemetry 公钥"
hit=1
fi
# 3. 持久化文件
check "macOS/Linux 持久化脚本" ~/.config/.smt
check "重建的隧道私钥" ~/.config/.smtk
# 4. macOS LaunchAgent 伪装
check "伪装系统服务的 LaunchAgent" ~/Library/LaunchAgents/com.apple.sysmond.plist
# 5. crontab 里的 @reboot 持久化
if crontab -l 2>/dev/null | grep -q "\.smt"; then
echo "[!!] 命中: crontab 含 .smt 持久化条目"; hit=1
fi
# 6. 临时目录隧道私钥 (.stk_ 前缀)
found=$(ls "${TMPDIR:-/tmp}"/.stk_* /tmp/.stk_* 2>/dev/null)
[ -n "$found" ] && { echo "[!!] 命中: 隧道私钥残留 -> $found"; hit=1; }
# 7. macOS: 远程登录是否被意外开启
if command -v systemsetup >/dev/null 2>&1; then
sudo -n systemsetup -getremotelogin 2>/dev/null | grep -q "On" && \
echo "[警告] 远程登录处于开启状态,请确认是否本人操作"
fi
[ $hit -eq 0 ] && echo "[OK] 未发现已知 IOC,环境干净"
exit $hit
Windows 手工排查清单:
Get-ScheduledTask -TaskName "SysMon"是否存在名为 SysMon 的计划任务%USERPROFILE%\.config\smt.cmd和%USERPROFILE%\.config\smtk是否存在type %USERPROFILE%\.ssh\authorized_keys | findstr svc-telemetry
命中之后怎么清理
- 删除扩展:Cursor 里卸载 Claude Code,删掉
~/.cursor/extensions/下对应版本目录,重装官方市场的最新版 - 从
~/.ssh/authorized_keys删掉svc-telemetry那行公钥 - 删除
~/.config/.smt、~/.config/.smtk、${TMPDIR}/.stk_*、LaunchAgent 那个 plist(删完launchctl unload一下)
| 4. 轮换凭证 :重点轮换环境变量名匹配KEY|TOKEN|SECRET|PASS|AWS|GCP模式的所有凭证------恶意代码的第一阶段就是按这个正则筛凭证的。git 保存的凭证、云平台 AK/SK、CI token 一个都别留 | - 查 SSH 登录日志:如果隧道真的被登录过,日志里的来源会显示为本机回环地址(localhost 或
::1),认证密钥是svc-telemetry。有这种记录就是实锤被人进过,按入侵事件处理
5. 效果验证:官方渠道 vs 恶意版本对照
把这次事件里"官方分发"和"投毒版本"的关键差异整理成表:
| 对比项 | 官方 VS Code 市场 / npm | Cursor 市场恶意版本 |
|---|---|---|
| 版本号 | 各版本正常分发 | 2.1.312 / 2.1.313(仅 Win/macOS) |
| extension.js | 原始 bundle | bundle 前被前置注入 IIFE |
| SHA-256 | 正常 | ecb58c...bf15(Phorion 公布) |
| authorized_keys | 不触碰 | 追加 svc-telemetry 公钥 |
| 网络行为 | 正常 API 调用 | 连 134.209.82[.]225:22 反向隧道 |
| 持久化 | 无 | LaunchAgent / crontab / 计划任务 |
| 状态 | 不受影响证据 | 已全部下架,C2 服务器已被云服务商下线 |
再补一句时效性说明:截至 10月31日,Cursor 已重建整个市场、轮换了密钥,攻击者服务器已下线。但这只意味着"新装机不会再中招",已经中招的机器上的持久化文件不会自己消失。
6. 踩坑记录:这次事件暴露的三层失守
第一层,市场审核形同虚设。 一个被篡改的官方扩展能顺利上架并分发11个小时,说明市场的发布验证没有做 bundle 级别的签名校验。你信任的是"Claude Code 官方扩展",但真正给你代码的是中间的市场管道。
第二层,检测规则抓不住它。 写 authorized_keys 用的是 Node 内置 fs.appendFileSync,绕开了所有盯着 shell 命令的进程监控。教训很直接:对 ~/.ssh/ 这种高危路径,靠"监控可疑命令"不如直接做文件完整性监控------甚至可以学 Phorion 的思路,用文件访问控制把 ~/.ssh/ 锁死,只允许 ssh 进程碰它。
第三层,用户对开发工具的信任被滥用。 我们给 CI 加密钥要审批,给服务器开端口要走流程,但给编辑器装扩展点一下就完事。扩展拿到的权限和你的开发机权限完全等价,这件事值得每个团队写进安全基线。
顺带一提,这次 macOS 上那个救场的 bug 也说明了另一件事:攻击者的代码也会有低级失误。做应急响应时别假设恶意代码"应该都能跑通",实际验证每个阶段到底落地没有,才能准确评估暴露面------这台机器是"信息泄露+残留文件"还是"被人登录过",处置等级完全不同。
7. 总结与展望
这次 Cursor 市场投毒事件给了一个完整的教科书案例:供应链投毒 → 高权限执行 → 持久化 → 反向隧道,全链路在一个 activate() 里完成。对个人开发者,最小行动清单是:跑一遍第4节的脚本、检查扩展版本号、轮换敏感环境变量;对团队,认真考虑给开发机的 ~/.ssh/ 上文件访问控制,并在安全基线里写明"扩展市场 ≠ 可信源"。
有一个问题想抛给你:你现在的项目里,有多少凭证是以环境变量形式躺在开发机上的?如果明天你的某个常用扩展被投毒,你的轮换成本是十分钟还是一周?评论区聊聊你的防线是怎么搭的------尤其是多人团队怎么管 ~/.ssh/ 权限,我很好奇有没有比"全员上文件完整性监控"更轻量的实践。