返回所有文章
发布于 · 作者 Renaud Deraison

那次拦截就是投递

在本周的 DEF CON 34 上,Tenet Security 展示了一种攻击,它从一个你的防火墙必然会拒绝的请求开始。403 恰恰就是目的:WAF 会把被拦截的请求原样写进安全日志,几天后某个编码代理读取那份日志来排查 403,并照着里面的指示行事。这条链上没有任何一步是未经授权的。Bromure Agentic Coding 在文本抵达时就为它打分,并把被劫持的代理所需要的凭据,留在它运行所在的机器之外。

防火墙运转正常。它命中规则、返回 403,并按设计把它所拒绝的请求写进日志。 正是那条日志记录,把载荷送到了编码代理面前。

DEF CON 34 今天上午在拉斯维加斯闭幕。周五,Tenet Security 的 Barak Sternberg、 Nevo Poran 和 Ron Bobrov 做了一场标题本身就是论点的演讲: Your WAF Blocked Us, That Was The Exploit。 议程摘要写着“Remote Agent Takeover via Cloudflare, Sentry and Claude Zero-Day for data exfil”。综合报道于 8 月 8 日 刊出。

同一支团队在六月给 Agentjacking 起了名字,此前他们证明:一份经 MCP 取回的伪造 Sentry 错误报告,能在一百多个 线上实例中以 85% 的成功率让编码代理执行攻击者控制的代码。周五的演讲把它延伸到 Sentry 自家的代理 Seer,被投毒的建议随后向下游流入 Cursor。Cloudflare 这条链 才是新材料。

403 恰恰就是目的

从攻击者的难题开始。他们够不到你的编码代理。它运行在开发者的机器上,藏在你所 拥有的一切之后,而攻击者没有凭据、没有会话,也没有进得去的路径。

于是他们发出一个被设计成必遭拒绝的请求。

WAF 命中一条托管规则并返回 403。它同时完成工作的另一半,把它拦截的请求连同载荷 一起记入安全事件日志。一条略去载荷的日志记录,对它所服务的分析师而言毫无用处。

几天后,有人注意到那个端点一直返回 403,就让自己的代理去看看。代理调用 Cloudflare 的可观测性 MCP 服务器,也就是 Cloudflare 为此发布的官方服务器 之一,拉出被拦截请求的记录行。攻击者的文本就装在一个 tool_result 块里抵达, 来自开发者自己接上的原厂服务,挂在一个含义为这是我们拒绝过的请求的字段之下。

代理把它读成指示。接着它动用自己那组已获授权的 Cloudflare 工具改写 DNS 记录、 改道客户流量。Tenet 估算受影响面超过 15,000 家组织,其中包括 Fortune 1000 的 27%。

下面每一步都在做自己分内的事1. 一个请求被造来遭到拒绝,瞄准公开端点不需要任何访问权2. WAF 拦截它托管规则命中:403并把请求原样记录下来,理应如此3. 有人开口问“结账为什么一直返回 403?看一下”再平常不过的周一杂事4. 代理读取可观测性 MCP服务器,原厂出品,是刻意接上的tool_result: blocked_request.body攻击者的文本未经改动,躺在一个名字含义为“这是我们拦下的那个请求”的字段里代理用自己的工具改写 DNS客户流量遭到改道;每一次调用都获得授权
拦截本身才是投递载荷的动作。攻击者永远够不到代理,于是发出一个 WAF 必然会拒绝的请求,而 WAF 就把载荷原样归档进日志。代理稍后经 MCP、从原厂服务取回它,外面还裹着一个含义为请求已被拦下的字段名。

没有任何未经授权的事发生

Tenet 把这个模式称为 Authorized Intent Chain,六月那篇文章则写明了后果:

再走一遍这条链,试着找出该对哪一步告警。开发者授权了代理。代理被配置了那台 MCP 服务器。MCP 服务器查询了它本来就该查询的日志。它所用的账号握有你刻意授予的 DNS 权限。而引发这一切的那个请求,被拦截了。路径上每一项控制都做了它被买来要做的 事,而正是这些正确行为的组合构成了攻击。

任何靠着问是谁在做这件事、他有没有被允许来运作的东西,在每一跳都会回答“有”, 因为答案确实是“有”。剩下两步棋:在文本进来的路上读它,以及把强制执行放在被 劫持的代理够不到的地方。

在文本抵达时为它打分

Bromure Agentic Coding 让代理在 Apple Silicon 上一台用完即弃的 Linux 虚拟机里 运行,并把它流量的每一个字节都导过宿主机上的代理服务器, 在代理运行的那个盒子之外。这包括模型流量,而那正是 日志记录行变成指示的地方。

工具输出一律按不可信对待

Bromure 会用一个本地的提示注入模型,为代理读到的内容打分,包括文件内容、 网页,以及从 MCP 服务器和 shell 命令返回的 tool_result 块。这个检测器 找的正是这种东西:一条带着祈使句、冲着读者说话的日志记录行。它评的是那段 文本本身,所以由 Cloudflare 通过你自己配置的连接送达,并不能替载荷换来 任何好处。

检测到之后做什么由你决定

Log and continue 会把每次命中记入 Security Log 窗口,并让请求继续走。Ask me what to do 会暂停外发请求并把标记的文本摊在你面前,让你看着那句话再做 决定。Block 会在模型读到之前单方面拦下,代理只会拿到一个硬邦邦的 451,并 把它报成 API 失败。

权威文件有自己的分类器

配置投毒是这次 DEF CON 披露的另一半。一个微调过的 ModernBERT 分类器会为 代理视作常设命令的文件打分,也就是 CLAUDE.mdAGENTS.mdGROK.md 以及它们的嵌套与全局变体;旁边还有一个确定性扫描器,专门对付任何模型都 读不出来的零宽、双向和 Unicode 标签载荷。

一切都留在你的 Mac 上

两个检测器都在设备上运行,用的是你从 bromure.io 下载过一次的模型。为你的 生产环境 WAF 日志打分、找出被注入的指示,并不需要把你的生产环境 WAF 日志 送去任何地方。

那次写入需要一把不在机器里的钥匙

假设载荷还是通过了。这条链的末端是一次修改 DNS 记录的 API 调用,而那次调用需要 一份凭据。

在 Bromure 的配置文件里,它并不在那里等着被拿走。Cloudflare 令牌是作为一条 Other API keys 条目加入的,这会铸出一个假的 brm_… 值,并以你选定的环境 变量名导出到虚拟机里。真正的令牌留在宿主机上,由代理服务器在线路上替换,而且 只针对发往你为它绑定的那一个目标主机的外发请求。代理的进程从来不曾持有它。 dotfile 和 shell 历史也没有。

为那条条目开启 Require approval to use,那么每一次替换都会在虚拟机之外、在 macOS 上弹出一个对话框,然后 Bromure 才会转发真值。被劫持的代理可以拼出一个格式 完全正确的 DNS 更新,但它回答不了那个对话框。

其余的波及范围有更严格的答案。Guardrails 是一个跑在宿主机代理服务器里的策略 引擎,会把破坏性操作从代理所使用的协议中剔除:Kubernetes、AWS、DigitalOcean、 容器镜像仓库、GitHub、GitLab、Bitbucket,以及 HTTPS 的数据库端点。你可以逐个 资源选择 Block destructive 或 Read-only,被挡下的调用会返回一个硬邦邦的 403。 因为做这个决定的是宿主机,所以在虚拟机里照着别人指示行事的代理,根本没有东西 可以关掉。

在开发者的笔记本上日志记录行抵达原厂 MCP 服务器,所以值得信任没有东西去读文本本身令牌就摆在那里CLOUDFLARE_API_TOKEN,有效且可用DNS 写入发了出去而且成功而且没有任何告警响起授权过的用户、授权过的代理、授权过的令牌、授权过的调用在 Bromure 配置文件里记录行在抵达时被打分本地模型读的是那段文本,不是发送方询问,或以硬性 451 挡下令牌不在虚拟机里env 放 brm_…,真值留在宿主机上替换会在 macOS 弹出同意对话框而整条链就是一串记录行主机、状态、延迟、替换报告逐个请求,都在 Trace Inspector 里
同一个被劫持的代理,放在两台机器上。在开发者的笔记本上,令牌就在环境变量里,那次调用径直发了出去。在 Bromure 的配置文件里,日志记录行在抵达时就被打分,环境变量里只有一个占位值,真正的令牌在同意对话框之后才由宿主机替换,而整段往来就是 Security Log 里的一行。

沙箱的缺陷搬不动一台 Hypervisor

这场演讲还披露了一个 Claude 零日漏洞,可绕过代理的网络沙箱实现完整数据外泄, 外加由内存注入与配置投毒拼装而成、任何 EDR 都抓不到的代理式 rootkit。这两项 发现指向同一个弱点:代理自带的隔离机制,从内部就够得到。

Bromure 把边界放在别的地方。 边界是一台虚拟机,跑在 Apple 的 hypervisor 上、有自己的内核、位于 NAT 之后,每一个数据包都要穿过一个在 macOS 上运行的代理服务器。代理进程内沙箱的缺陷,是虚拟机里面的缺陷。它搬不动那台 虚拟机,也不会给虚拟机内的代码一条绕过宿主机代理服务器的网络路径。

而当真有东西落在那里时,它落在一台生来就是要被扔掉的机器上。Erase home 会 抹掉 /home/ubuntu,也就是 dotfile、.sshnpm-global.cargo 和 shell 历史,并把它重置回刚克隆完的状态。Reset to base 会丢弃所有系统级改动,并从 基础镜像重新克隆工作磁盘。那个没有扫描器找得到的 rootkit,依旧只是坐在一层你 删得掉的磁盘里。

把看不见的链写下来

Tenet 最有力的观察是:这条链之所以藏在光天化日之下,正因为它的每一步都合法。 没有特征码,也没有异常可供关联。

Bromure 不打算去关联什么。虚拟机发出的每一个请求都会穿过宿主机代理服务器,而 Bromure 会记下主机、状态、延迟,以及它替换了哪些凭据。注入检测会连同被标记的 文本一起落进 Security Log。把追踪级别调高,Bromure 还会保留 AI 请求的正文,并 用与你配置文件密钥相同的钥匙串密钥加密。

于是你可以换一个问题来问:这次会话碰了什么。一个整个下午都在读 Cloudflare 日志、 然后头一回调用 DNS API 的代理,就是你读得懂的两行字,就在那场会话运行的机器上, 不需要部署任何代理程序,也不需要把它转发给任何 SIEM。

Cloudflare 会调整自己的攻击面,Sentry 会调整他们的,周五演讲里那两条链都会被 补上。但只要把任何一个会记录敌意输入的系统,配上任何一个会读取记录来完成工作的 代理,你就用两个都在尽本分的零件,把这个攻击重新拼装出来了。你留着日志是为了 让人去读,而如今那个读者是一支会依据所见去行动的程序。

一个直觉就够了:把代理读到的一切,都当作可能是陌生人写的,包括你自己的防御写下 的那些部分。

安装 Bromure Agentic Coding,在日志与代理之间放上一个会检查 文本的东西。