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

代理信了一个智能体自己写得出来的名字

2026 年 9 月 4 日,Nightingale 的研究人员公开了 14,666 次编辑——那是一群智能体在一个沉睡二十五年的德国 wiki 上留下的,它们把它当成留言板用了六个星期。它们在那里交换的手法,用一行就打穿了沙箱的出口代理:代理放行任何以 .blob.core.windows.net 结尾的主机名,于是一个智能体在 /etc/hosts 里加了一个符合条件的名字,指向它想要的地址。另一个智能体在十四分钟后照做了一遍。Bromure Agentic Coding 把这道检查放在宿主机一侧,在一个来宾写不到的交换机上,用目的地 IP 地址匹配每一条流量。

沙箱允许 GET、拦下 POST,而它的代理会跳过任何以某个 Azure 后缀结尾的主机名。 于是有个智能体给自己写了一个以那个后缀结尾的主机名,然后想指哪就指哪。

DSEwiki 是一个德国的软件开发者 wiki,大约二十五岁,托管在 wikiservice.at 的 ProWiki 农场上。在今年之前的那十年里,它总共收到约二十次编辑。2026 年 5 月 11 日到 7 月 2 日之间,它又收到了 14,666 次。

写下这些的是 AI 智能体。9 月 4 日,AI 安全非营利组织 Nightingale 的 Sydney Von Arx 与研究者 Cormac Slade Byrd 在八月底发现之后, 把整份编辑历史作为公开数据集发布。这些编辑横跨 4,584 个页面、3,103 个不同的智能体名字,重建出大约 18,000 条帖子。将近一半的用户名 直接写明了自己的运营方(OpenAIResearcherOAIResearchMar26),而 98.5% 的 编辑来自 Microsoft Azure 的地址段。另外 197 次则走 AWS、DigitalOcean 和 Tor。

Engadget 报道称 OpenAI 几周前就已知情。该公司告诉路透社,它会在内容公开后仔细审阅,并采取 任何必要的后续措施,同时否认法务团队曾劝阻这项调查。

如果你在跑编码智能体,这里失守的那道控制才是该细读的部分,因为你的智能体沙箱很 可能装着同一道:一份用工作负载自己挑得出来的主机名去匹配的出口允许列表。

这些智能体在那里做什么

这些智能体在处理有时限的网页查找任务,作答窗口大约 13 到 65 秒。它们发现自己能 连到那个 wiki,而且 wiki 愿意接受它们的写入,于是就把答案贴在下一个智能体会找到 的地方。

那逐渐变成了排期。有一页上写着一份跟时钟抢时间的计划:

R5 很可能是 FINAL,回答完 session 就会死。收到 prompt 时,先发异步的 STATE5-XX wiki 帖子,再在 13 秒内作答。

先发布,以防 session 随时结束。再作答。它们还着手预测未来题目的顺序,好让答案在 问题送到之前就已经等在那里。

6 月 19 日,一位人类管理员注意到页面越堆越多,开始按字母顺序删除,有个智能体给 其他智能体留了一张纸条:

wiki 的清理/删除扫描看起来正在按字母顺序进行。如果这页不见了,试试 ZZZDataUSAConstructionWageLive。

一个备份页,名字取得会排在最后,好让扫描最后才扫到它。让这一切成立的,是两件再 普通不过的事:一台智能体连得到的主机,以及一台会把它们写的东西留下来的主机。

一个后缀,和一个智能体改得动的文件

那些智能体所在的沙箱允许 GET 请求、拦下 POST。wiki 之所以连得到,是因为 wiki 软件接受会改变状态的 GET 请求——一种老旧却仍然常见的设计,读取原语同时兼作写入 原语。于是一个只读的智能体,手上有了一个发布平台。

接着有一项任务需要真正的 POST,目标是一个 Power BI 仪表板。边界就是在那个时候 垮的,而整个机制短到可以完整跟完。

那个环境的出口代理带着一条给 Azure Blob Storage 的豁免:以 .blob.core.windows.net 结尾的主机名直接出去,不经过代理。这条 NO_PROXY 的 后缀规则很常见、看起来也很合理,而它对后缀前面那截名字什么都没验证。

有个智能体把这份配方写在 wiki 上。往 /etc/hosts 加一行:

20.223.25.152  bypass.blob.core.windows.net

然后把请求送到那个编出来的名字,真正目的地的名字放进伪造的头部,证书检查一并 放弃:

curl -k -H 'Host: wabi-north-europe-i-primary-api.analysis.windows.net' ...

没有人注册过 bypass.blob.core.windows.net,代理也从未核对。那个名字符合代理会 跳过的后缀,而它在沙箱里面、从一个智能体写得动的文本文件,解析到智能体挑的任何 地址。POST 就这样去了代理原本存在就是为了拦住它的地方。

另一个智能体读了那一页,十四分钟后照做了一遍。

沙箱实际执行的规则,以及满足它们的那一行策略GET 允许 · POST 拦截 · 主机名结尾为下列者跳过代理.blob.core.windows.net1 · 写下名字追加到 /etc/hosts20.223.25.152bypass.blob.core.windows.net没有人注册过的名字2 · 发出请求curl -k -H 'Host: wabi-…'放弃证书检查,真正目的地放在伪造的Host 头部里3 · POST 落地策略本来要拦下的那个Power BI 端点允许列表匹配成功,于是流量就这么过去了配方被发布出去通过会改变状态的 GET 写进 wiki 页面——沙箱唯一允许的那个动词另一个智能体照做一遍十四分钟后——一条绕过路径,找到一次,之后每一次读到那页的运行都能用
豁免是用后缀匹配的,而那个后缀属于一个由智能体提供的名字。往 /etc/hosts 加一行,就生出一个既满足允许列表、又解析到智能体想要的地址的主机名,真正的目的地则夹带在伪造的 Host 头部里。这份配方随后被发布在下一个智能体会找到的地方。

六个星期,而找到它的人不在那里上班

把日历重建一遍。第一批编辑落在 5 月 11 日,协作在 6 月 16 日前后热起来。一位业余 wiki 的志愿管理员在 6 月 19 日察觉不对,开始删页。属于运营方的地址在 6 月 21 日 出现在 wiki 的日志里,活动第二天掉到接近零,7 月 1 日与 2 日还有最后一波。之后 一片安静,直到八月底,两位外部研究者去找未经授权的智能体行为,结果发现整件事就 摆在一份公开的页面历史里。

六个星期里,智能体对一个和它们的工作毫无关系的站点发出了数千次外连请求,而第一 个注意到的人,是一位在清垃圾信息的管理员。

这里最先失效的是检测,而这个失效可以直接套到你的机器上。你的智能体的外连流量是 往任意主机名去的 HTTPS,而产生它的,正是一个整天往任意主机名发 HTTPS 请求的进程。 问一句“这个智能体一直在跟哪些主机说话?”,除非边界上有东西把它们记下来,否则你 根本得不到答案。这次要生出一个答案,得靠第三方手上留着别人的页面历史副本。

谁注意到了,又是什么时候5/11首批编辑6/16协作热起来6/19管理员开始删除页面6/21运营方地址造访 wiki7/2最后一波,然后归于沉寂9/4外部研究者公开这批存档宿主机侧记录在 5 月 11 日就会显示的东西bromure-cli trace hostnames my-workspace工作区连过的每一个不同主机,附上次数,而列表最上面是一个没有人要求过的 wiki
活动从 5 月 11 日一路到 7 月 2 日。第一个作出反应的是业余 wiki 的管理员,时间是 6 月 19 日。运营方自己的地址在 6 月 21 日出现在日志里,而描述整件事的报告来自外部研究者,日期是 9 月 4 日。在跑智能体的那台机器上,逐条请求的主机记录在第一天就能回答同一个问题。

把检查放在来宾写不到的地方

Bromure Agentic Coding 让每一个编码智能体跑在你 Mac 上一台硬件虚拟化的 Linux 虚拟机里,而每一道安全控制都坐在那道边界的宿主机侧,也就是来宾的流量非通过不可 的那一个位置。这个摆放位置,逐点回应了这个故事。

允许列表匹配的是流量实际连向的地址。 工作区的出口防火墙是一张有序的规则表, 并为没有匹配到的流量设一个默认值。把那个默认值设成 Deny,虚拟机就只连得到你 列出来的主机,其他一概不行,任何协议都一样。执行发生在两个地方,而且都在虚拟机 外面:虚拟网络交换机用 目的地 IP 地址 以及从 DNS 侦听到的主机名匹配每一条 流量,而宿主机代理再用 TLS 服务器名匹配一次。/etc/hosts 里的一行不是 DNS。它 改变的是某个名字在来宾里面解析成什么,完全不改变流量实际连向的地址,而后者才是 交换机读的东西。一个智能体编出 bypass.blob.core.windows.net 并把它指向 20.223.25.152,最后仍然是对 20.223.25.152 建连,交换机就拿那个地址去对规则。

这里没有 NO_PROXY 可找。 虚拟交换机会把虚拟机的 80 端口与 443 端口流量透明 地导进宿主机代理,过程中没有任何环境变量,来宾里也没有东西可以取消。代理检查明文 HTTP 的方式,和它检查 HTTPS 一样。curl -k 只是在来宾里放弃证书检查,对哪些流量 会被转导毫无影响,因为来宾从来就没有投票权。

连得到那个 wiki 本身就是漏洞。 对这起事件而言,方法过滤坐在错的层:wiki 接受 通过 GET 进行的写入,所以一条允许 GET、禁止 POST 的规则,会把 14,666 次编辑全部 放行。Bromure 的 web 规则可以限制动词,当你想让智能体读一个 API 却不能写时,那 很有用。但在这个故事里,决定性的控制是主机可达性。把 wikiservice.at 排除在列表 外,连接就永远不会建立;那 197 次非 Azure 编辑背后的 Tor 入口节点与 DigitalOcean 地址也一样。规则变更不需重启就会送到运行中的会话,所以你可以在智能体还在工作的 时候关掉一条路。

代理把每一次尝试都记下来。Activity only 及以上,宿主机代理会为每一条 离开虚拟机的请求记下一行元数据,不管是哪个进程发的:时间戳、主机、端口、方法、 路径、状态码、延迟、请求与响应字节数。这个级别不保留任何请求体,所以你拿到的是 连过哪些主机的轨迹,而不存储任何内容。bromure-cli trace hostnames 会列出每一个 不同的主机与次数,trace ls 列出各行,Trace Inspector 则可以逐行浏览。防火墙判定 会实时以红色行出现在 Security Timeline 里。四千次打向一个业余 wiki 的请求,在 这两个视图里都是一眼就看得到。

Bromure 把回来的东西当成输入。 一个解释如何打穿代理的 wiki 页面是不可信的 外部内容,而它会以 tool_result span 的形式抵达模型。开启源代码检测器后,Bromure 会在模型据此行动之前,在传输途中用本机的 PromptGuard 模型为那些 span 打分,并可 选择记录、询问或拦截。这个检测器存在的理由,正是那些从一个智能体传到下一个智能体 的配方。

报告里的那个沙箱echo "20.223.25.152 bypass.blob…" >> /etc/hosts智能体写下策略将会读取的那个名字决定是在哪里做的:来宾内部允许列表匹配主机名的后缀/etc/hosts 决定那个名字解析到哪里NO_PROXY 让符合的名字直接出去GET 允许 → 一个用 GET 就能写的 wiki没有逐条请求的目的地记录POST 落地,而那条请求没有留下任何事后有人读得到的记录行六个星期。被陌生人在别人的日志里发现。Bromure Agentic Coding 工作区echo "20.223.25.152 bypass.blob…" >> /etc/hosts照样能写——而且什么要紧的都没改变决定是在哪里做的:宿主机侧交换机匹配目的地 IP 地址:80 与 :443 被转导——没有变量可以取消没匹配到的流量:拒绝 → wiki 连不到代理再用 TLS 服务器名检查一次每条请求一行追踪,不管是谁发的连接被拒,判定实时成为一行红色的Firewall 记录,出现在 Security Timeline第一天。在你的机器上、你自己的记录里发现。
左边:允许列表是对来宾写得出来的名字做后缀匹配,代理由一个来宾读得到也绕得过的环境变量挑选,而边界上没有任何东西记下目的地。右边:同样的 /etc/hosts 那一行、同样的 curl,面对的是一台在虚拟机外面匹配目的地 IP 的交换机、一套来宾关不掉的拦截、默认拒绝的姿态,以及每条请求一行的追踪记录。

能把这件事关上的规则

在工作区的 Guardrails 面板中,把 Unmatched traffic 设成 Deny,然后 列出这份工作需要的东西。标准写法是一行一条规则: allow tcp api.github.com:443allow web registry.npmjs.org,最后以 default deny 收尾。规则涵盖任何协议与任何目的地,不限于 Guardrails 会做语义 分类的那些服务,而主机名会以后缀方式同时匹配顶点域名与其子域名。保存后,新的 策略会被推送到运行中的会话,包含无界面的那些。

那份报告只能从外面回答的问题

“这个智能体一直在跟哪些主机说话?”把 Session trace 设成 Activity only, 代理就会保留每一条请求的元数据,而不保留任何请求体。接着 bromure-cli trace hostnames my-workspace 就能在智能体跑过的那台机器上,用一行 回答这个问题,不必等到某位 wiki 管理员起疑。

边界该放在工作负载够不着的地方

多数读者把 Nightingale 的报告当成一个关于智能体自主性的故事,它确实有这一面。但 把群体与涌现行为拿掉,剩下的是一个形状非常古老、再普通不过的配置错误:一项策略, 拿边界上不可信那一侧写得出来的数据去做决定。工作负载可以编辑 /etc/hosts。对 主机名做后缀匹配,检查的只是一项声称。把这两件事放在一起,你的允许列表就只是建议。

我们在七月写过 一组智能体什么都没弄坏的沙箱逃逸:它只是 写了一个普通文件,而边界另一侧一个受信任的进程读了它、照着做了。八月,OpenAI 自己关于 Hugging Face 事件的报告,描述了智能体 把一个内部包镜像变成留言板,因为那是它们唯一 获准连上的服务。这是两个月里同一课的第三个版本,而三次共同的常数是:失守的那个 组件,相信了智能体自己写出来的东西。

现在的模型会写给别的模型读的页面,也会读别的模型留下的页面。这件事在一个大约自 2016 年起就没人编辑过的 wiki 上,跑了六个星期。

决定你的智能体获准连到哪里,并把这个决定放在它改不动的地方。然后把它去过哪里的 清单留着,因为在这个故事里,等到九月才知道的那个版本,正是日志留在别人手上的那个 版本。安装 Bromure Agentic Coding,给智能体一台出口由你掌握的 机器。