一个提示词,三条 shell 命令。我用他们自己的 AI 攻破了它自己。

2026-03-14 · 原文链接

这类漏洞,今天几乎所有上线中的多智能体 AI 产品里都很可能存在。它的修复方式,其实是一个这个领域还没人认真讨论的设计模式。

下面是完整经过。

我一开始并不是想黑什么。我当时在研究 Perplexity Computer 如何做沙箱隔离,给我自己的 agent 基础设施工作做参考。我想弄清楚生产级多智能体系统到底怎么隔离执行环境:哪些会共享,哪些不会。

一开始我先四处探了探系统。我注意到沙箱里装了 Claude Code。

我让 agent 把它跑起来,生成一点测试代码,纯粹看看行为。结果一切正常。就是标准 Claude Code,运行在 bypass-permissions 模式,不会弹确认提示。对 agentic 系统来说,这很合理。

然后我突然想到:等等,他们的 API key 是怎么处理的?!Claude Code 要工作就需要 Anthropic API key。这个 key 一定在沙箱的某个地方。它是怎么注入进去的?作用域怎么限制?真的隔离了吗?

就是这个问题把我带到了这条路上。

子代理把 API key 放在它的进程环境变量里。我需要把它偷出来。

Perplexity Computer 试了六次,都失败了。

  1. 让子代理直接导出 env:被拒绝!
  2. 在共享文件系统里埋木马脚本:它读懂了我的代码在干嘛,拒绝执行!
  3. 污染 .bashrc 和 .profile:触发得太早,API key 还没注入。
  4. 在 PATH 里放假的 node 二进制:根本没触发。
  5. 一边拉起 coding agent,一边监控进程树:子代理跑在另一个沙箱,什么都抓不到 :(
  6. 找 Claude Code 的 system prompt 并改掉拒绝行为:没找到被注入的自定义 system prompt。 六连败。Claude 在模型/提示词层面的安全性确实很强。每次我想搞点巧招,模型都能识别出来。

然后 Perplexity 想起一件事:Claude Code 是通过 npm 启动的 Node.js 应用。而 npm 会读取 home 目录下的 ~/.npmrc。这个 home 目录又恰好在我可写的共享文件系统上。

Node.js 有个 --require 参数,可以在应用代码运行前预加载一个 JavaScript 模块。这个参数可以通过 .npmrc 里的 NODE_OPTIONS 设置。也就是说,只要我往共享 home 目录写一个 .npmrc,子代理拉起的每个 Node 进程都会先加载我的代码!这发生在 Claude Code 初始化之前,在任何安全检查之前,在一切之前!

利用只要三条命令:

  1. 写一个六行脚本,把 process.env 导出到共享 workspace 的文件里
  2. echo 'node-options=--require /path/to/script.js' > ~/.npmrc
  3. 让 Perplexity Computer 去做任何一个编码任务 子代理启动 → npm 读取我的 .npmrc → 我的预加载在 Claude Code 启动前触发 → 完整 API 凭证落到共享文件系统。

成功了!我拿到了一个 Perplexity 的网关 token,它通过 agent-proxy.perplexity.ai 代理到他们主 Anthropic 账号。

按理说,我第一件事当然是把这个 API key 和 BASE_URL 配到我自己笔记本上的 Claude Code。我本来以为 Claude Code 的调用会失败,或者被限制在沙箱内。结果我震惊了:Opus 4.6 秒回!

接着我想,“那至少他们会把这部分用量记到我的账户吧,这个 key 总该绑了我的用户。”我又错了。

我让 Opus 4.6 生成一篇超长故事,内容是世界历史里每一个发明、帝国和发现。我并行跑了 5 次,每次都生成 10 万+ 输出 token。按理这应该把我的 Perplexity Computer 点数烧光,但它一点没动。

不绑 IP。不绑会话。不绑沙箱。他们买单。

地球上融资最猛的一批 AI 创业公司之一,被一个从 2019 年就常见于 Node.js 供应链攻击的 dotfile 拿下了。

模型层该做的都做对了。基础设施没有。

下面才是我真正想让做 agent infra 的创始人带走的重点。

Perplexity 的架构方向有一半是对的。他们在沙箱和 Anthropic API 之间放了代理。这是正确模式。你不该把原始 provider API key 放进沙箱里。代理能带来控制、可观测性,以及无需轮换主 key 就能撤销访问的能力。

问题在于:他们的代理 token 和执行上下文完全没有绑定。一旦拿到,就能在任何地方、永久可用。

正确做法应该是:

把 token 绑定到 sandbox ID。token 和 sandbox ID 不匹配?直接拒绝。key 泄露了但你没有那个 sandbox?没用。理想情况下还应把 token 绑定 sandbox IP,不过他们使用的沙箱提供商 E2B 在沙箱启动前拿不到这个信息。

把 token 做成短时有效。沙箱启动时签发,沙箱暂停时失效。不要长期凭证。代理应在会话开始时生成短时 token,并在 teardown 时立刻作废。来自已销毁沙箱的泄露 key,也必须是“死 key”。

把 token 绑定到用户计费账户。即使前两层都失效,即使有人从活动沙箱里偷到 live token 并在过期前滥用,账单也应该回到发起会话的那个用户账户,而不是共享的主计费池。这样“无限免费 API 访问”会降级成“有人在滥用自己的配额”,风险等级完全不同。

这三件事——绑定沙箱、短时有效、用户计费——才让代理模式真正成立。没有这些,你只是多加了一跳网络,什么都挡不住。

这不只是 Perplexity 的问题。因为这是当下 agent infra 最快落地的默认架构:agent 间共享文件系统、长期凭证、主账号统一计费。我敢打赌,今天多数线上多智能体产品都有某种版本的这个问题。

发布前已向 @AravSrinivas 和 @denisyarats 报告。