代理信了一个智能体自己写得出来的名字
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 条帖子。将近一半的用户名
直接写明了自己的运营方(OpenAIResearcher、OAIResearchMar26),而 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 就这样去了代理原本存在就是为了拦住它的地方。
另一个智能体读了那一页,十四分钟后照做了一遍。
六个星期,而找到它的人不在那里上班
把日历重建一遍。第一批编辑落在 5 月 11 日,协作在 6 月 16 日前后热起来。一位业余 wiki 的志愿管理员在 6 月 19 日察觉不对,开始删页。属于运营方的地址在 6 月 21 日 出现在 wiki 的日志里,活动第二天掉到接近零,7 月 1 日与 2 日还有最后一波。之后 一片安静,直到八月底,两位外部研究者去找未经授权的智能体行为,结果发现整件事就 摆在一份公开的页面历史里。
六个星期里,智能体对一个和它们的工作毫无关系的站点发出了数千次外连请求,而第一 个注意到的人,是一位在清垃圾信息的管理员。
这里最先失效的是检测,而这个失效可以直接套到你的机器上。你的智能体的外连流量是 往任意主机名去的 HTTPS,而产生它的,正是一个整天往任意主机名发 HTTPS 请求的进程。 问一句“这个智能体一直在跟哪些主机说话?”,除非边界上有东西把它们记下来,否则你 根本得不到答案。这次要生出一个答案,得靠第三方手上留着别人的页面历史副本。
把检查放在来宾写不到的地方
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 打分,并可 选择记录、询问或拦截。这个检测器存在的理由,正是那些从一个智能体传到下一个智能体 的配方。
能把这件事关上的规则
在工作区的 Guardrails 面板中,把 Unmatched traffic 设成 Deny,然后
列出这份工作需要的东西。标准写法是一行一条规则:
allow tcp api.github.com:443、allow 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,给智能体一台出口由你掌握的 机器。