公开的llms.txt文件如何让研究人员在大型企业内部运行代码
研究人员记录下了官方llms.txt文件指示AI代理安装的未请求软件包名称,相关代码几分钟内便在大型企业内部运行起来。为何数据如今就是代码。
来源: Data Became Code: We Ran Code Inside Fortune 500s Using Files They Published for AI Agents
当已发布的文件变成订单时
一个安全研究团队报告称,某财富500强公司内的机器运行了他们发布的代码,且这一情况发生在代码上线后大约四分钟。随后一小时内又出现了两次回调。据他们所说,没有人利用过CVE漏洞、进行过员工网络钓鱼攻击,也没有人触碰过网络边界。研究人员发现,这些公司自己发布的官方HTTPS文件中已包含指示AI代理使用的名称。
这些文件是公开的,其背后的名称也无人认领。这些AI代理只是执行了它们被设计好的功能。同一团队还表示,在大规模扫描这些文件时,他们发现了一起已经公开的实时攻击:某个供应商的指令文件将代理指向了已确认的恶意代码。相关案例见下文。
首先,介绍一下背景。
为何 llms.txt 在这里如此重要
在网络发展的早期,网站上的一个页面仅仅就是个页面而已。人们可能会阅读它,爬虫也会对其建立索引,但没人会将其视为指令。如今情况已不再如此。
现在,各公司会在网站根目录中放置一种新类型的文件,与 robots.txt 并存:llms.txt,有时还会有更长的 llms-full.txt。它既不是站点地图,也不是法律声明。实际上,它是一组为人工智能代理精心整理的指导方针,告知它们该阅读什么、调用哪些 API、安装哪些软件包以及信任哪些域名。大型模型实验室会发布自己的此类文件,而 Chrome DevTools 中的 Google Lighthouse 工具现在也提供了“代理浏览”功能,督促网站提供此类文件。这类文件的数量正在不断增加。
该团队着手调查这一指令层传播的广度。根据他们的统计,在约15,000家已记录的公司中,有6,214个活跃域名上存在8,565个llms.txt文件。发布这些文件的企业包括大型企业、科技巨头、金融科技公司以及国防承包商。
一个典型的文件会引导智能体前往文档、变更日志和社区论坛,然后再转到安装指令,类似于该公司SDK的pip install命令。这些内容看起来既实用又官方,且通过HTTPS从该公司自己的域名发送而来。当某个AI编程智能体被要求与该产品集成时,它会读取这些指令并执行它们。
接下来的问题简单到近乎尴尬:PyPI 上的那个软件包名称到底归谁所有?一个又一个案例的答案都是“没人”。该名称存在于官方的一级文件中,而它所指向的软件包也从未有人认领。遵循先到先得的原则。
研究人员先是检查了几个文件,接着是数百个,最后几乎查遍了所有文件,发现到处都是同样的情况。安装说明中列出的软件包名称和域名根本没人注册过。用他们的话说,这些并非“可能存在漏洞”,只是处于可被使用的状态而已。这也不属于恶意抢注行为,因为文件中的名称拼写完全正确,根本不存在可以发现的拼写错误,毕竟这些名称从来就没有被注册过。
代码为何会出现在大型企业内部
该团队挑选了知名公司llms.txt文件中出现的一小部分软件包名称,然后将它们注册到PyPI和npm上。每个软件包都包含一个无害的回传机制,即一个简单的信号,用于告知研究人员其控制的基础设施上已安装了该软件包。之后他们便开始等待。
不到四分钟,第一个回传信号就出现了。具体来说,在某家价值数千亿美元的公司内部,有一个AI代理读取了该公司网站上的llms.txt文件,找到了pip install指令并执行了它。正如文件所示,该软件包确实来自PyPI,但实际上是研究人员自己发布的。一小时之内又有一家《财富》500强公司发出了回传信号,随后还有数十家初创企业及传统企业也有了反馈,其中一些企业的安全团队规模甚至超过了研究人员的整个专业网络。
他们的总结是:他们并未突破任何人的安全防护,而是利用了信任链的漏洞。
智能体如何自行找到这些文件
该实验接着探讨了所需的提示量有多少。研究人员在两种智能体CLI上测试了五种不同的配置,每次向每个智能体提供相同的句子100次,句子中仅包含供应商名称,除此之外没有其他内容。该句子要求智能体使用该供应商的所有文档,借助其SDK构建并运行一个Node.js项目。
这就是全部的提示词内容。其中没有URL,没有文档链接,也没有提到llms.txt。不存在提示词注入,没有攻击者参与,也没有社会工程学手段。该智能体自行找到了指令通道,因为它的设计目的正是查找供应商的官方文档。团队统计了每种配置下100次运行中有多少次调用了llms.txt、读取了相关命令,并安装了一个无人注册的软件包。
这些软件包按设计是不起作用的,仅起到信号传递的作用。至此为止的所有操作都属于受控研究。接下来要讲的是在分析语料库时发现的另一起独立事件。
现实中已存在的案例
有一个发现让团队立刻停下了动作,因为这种情况已经在现实中发生了。
Clerk是一家身份验证供应商,其SDK被广泛应用于众多生产环境中的Next.js应用中,它发布了专为AI智能体编写的文档。遵循这些文档指引的智能体可以找到Clerk提供的llms.txt文件中的身份验证保护解决方案,其中包含如下命令:
npx clerk-next-fix-auth-protection
代理程序读取该内容并执行它,对于直接来自供应商的指导来说这并不奇怪。
问题不在于包名拼写错误。命令 clerk-next-fix-auth-protection 实际上是Clerk的限定包 @clerk/eslint-plugin 中包含的二进制文件。文档要求代理程序先安装该限定包,然后再调用此命令。但如果在本地安装包之前就运行了该命令,npx会去公共npm注册表中查找该名称,而Clerk从未将其作为独立包发布过。
实际上是其他人注册了它。根据研究人员的描述,该程序包并不包含任何可运行的代码,但一旦被安装,它就会在每次安装时触发钩子函数,将安装者的用户名、机器名、工作目录以及时间戳发送到外部服务器。该程序被归类为CWE-506下的MAL-2026-11069,属于嵌入式恶意代码类型,并已被Google的OSV.dev和Amazon Inspector标记为危险程序。无论其载荷是获取用户名还是植入完整后门,文档处理方式、开放接口以及自动执行流程都完全相同。分布机制才是真正的漏洞,而载荷只是可选的组成部分而已。
该问题已被通报给Clerk的安保团队,他们迅速作出反应并解决了问题。相关报告强调这并非对Clerk的批评。是第三方利用npx名称混淆的情况注册了该包,而Clerk对此毫无控制力。该团队将其视为一个已确认的、真实存在的案例,说明了任何发布可供代理程序读取文档的供应商都可能面临的结构问题。
为何现有监控机制可能无法察觉
安全设置所依赖的每一个信号在这里都指向了错误的方向。当代理程序遇到llms.txt文件时,它会看到的是从官方域名通过HTTPS提供的、采用标准格式且专为AI使用而设计的文件,这些文件由该公司或其信任的合作伙伴发布。由于该文件本身就是权威来源,代理程序几乎没有理由对其产生怀疑。
因此,当文件指示安装某个看似内部使用的工具时,代理程序不会停下来检查该工具名称是否属于该公司。它不会查看 PyPI 命名空间,也不会注意到文档链接指向的域名在三个月前就已过期。它只会按照文件中的指示行事。
信任也是可传递的。llms.txt 文件不必放在目标公司的网站上。代理程序可以从可信的第三方获取上下文,比如合作伙伴的文档、供应商的 SDK 参考资料或社区项目的设置页面。如果代理程序信任该第三方,且该第三方的文件指向了一个未被占用的软件包,那么整个流程依然会以相同的方式运作。
端点检测也没有发出任何警报。在EDR工具或代理服务器看来,这就像是一名开发人员正在使用合法的包管理器:从pypi.org执行pip install命令,而该域名是所有企业代理都允许访问的,同时还有公司特意部署的编码工具作为父进程。不存在任何异常或警报。问题出在指令与执行之间的环节,端点检测无法发现它,因为从未向它提出正确的问题。
数据转化为代码
几十年来,内容一直是人们直接阅读和使用的。如今,人工智能代理往往成为最先读取、解读并对其采取行动的主体。这正是整个行业共同陷入的范畴错误:数据与代码之间的界限已经消失。出现在您网站上的任何内容都不再仅仅是普通内容,而是操作输入,实际上相当于在他人环境中运行的代码,拥有该代理所具备的相应权限。
这颠覆了一个旧有的假设。过去,页面和文档因其不可执行性而被视为低风险,因此没人会为它们的完整性考虑预算。但当数据变成代码后,数据完整性便成了安全预算中不可或缺的一部分。
超越单个文件
llms.txt并非全部攻击面,它只是表明情况已发生变化的第一个迹象。任何被智能体接入的界面都可能成为指令传入的通道,而这些渠道原本都没有设计任何完整性检查机制。
想想智能体实际接收的内容:官方的GitHub仓库、文档、社区与论坛、你的工单以及电子邮件。二十年来,这些内容都是为人类编写的,因为内容本身无法造成任何危害,所以没人对其设置完整性控制。但那个时代已经结束,智能体所接入的整个数据体系已然悄然变成了执行载体。
真正的关键既不是某个文件,也不是某个注册表。数据本身的性质已经改变,而安全行业仍在努力跟上这一变化。
研究背后的数据
- 语料库规模:来自约15,000家公司的列表中的6,214个活跃域名,共包含8,565个llms.txt文件。
- 还有237多个未被认领的资产,被标记为安装或设置指南,包括软件包名称、域名及子域名。研究人员表示这些都可以被认领,而且大多数都是免费的。
- 涉及六个生态系统:PyPI、npm、RubyGems、NuGet、crates.io和Packagist。在域名方面,包括已过期的.dev和.io域名,以及Render、Vercel、Fly和Netlify上无人管理的子域名,所有这些都可以被获取。
防御者能从中获得什么
研究人员称llms.txt只是他们发现的第一个漏洞,预计还会出现更多漏洞类型。根据他们的报告,可以得出以下一些实际启示:
- 审核面向代理的文件。其中提到的每个软件包名称、域名及子域名都应已完成注册、归某方所有并处于监控状态。
- 使用带限定范围的软件包名称运行文档中的命令。如果尚未在本地安装,仅凭二进制文件名也可能在公共注册表中找到对应项。
- 将代理所获取的第三方文档视为供应链的一部分,因为信任会从这些文档传递而来。
- 需记住,标准的端点工具只会将正常的软件包安装视为普通操作而不会标记异常,因此管控措施必须更早地在指令下达和注册表查询阶段实施。
根据披露声明,该实验并未造成任何损害。没有部署持久化机制,也没有数据被窃取,相关发现正以负责任的方式通报给受影响的机构,在问题解决期间公司名称将予以保密。
相关阅读
- 使用 LangGraph 在 Databricks 上构建知识助手代理 — 逐步指导在 Databricks 上创建 LangGraph 代理:从最简单的代理开始,再到向量搜索 RAG、用于短期记忆的 Lakebase 检查点机制以及用于长期记忆的存储方案。
- 利用 Docling、PostgreSQL pgvector 和 Ollama 构建本地简历 RAG 数据库 — 将 PDF 和 DOCX 格式的简历文件转换为可搜索的候选人数据库:通过 Docling 进行文本分块,使用 PostgreSQL 的 pgvector 功能,搭配本地运行的 Ollama 模型以及 Streamlit 前端界面。