假设模型已被攻破

Dev.to AI 2026-10-21T21:41:31.057249

作者:Josh Greenwell,应用人工智能工程师

本文是我们白皮书的精简版,白皮书讨论的是:当输入防御失效时,如何遏制 AI 智能体。完整论文包含多个附录:2026 年 7 月事件时间线、遏制检查清单、遏制控制项与 SOC 2、OWASP、NIST 框架的映射,以及完整参考文献。你可以在这里查看并下载原文。

引言

我们围绕代码执行重构 LuumenAI 时,初衷是为了省钱。我在另一篇论文里写过这件事,简单说:每次请求都加载一大堆工具会消耗大量 token(词元,模型处理文本的基本单位),把工作挪进沙箱、让模型针对我们的 API 写代码之后,账单从每天几百美元降到了个位数。

安全边界几乎是顺带出现的。模型的代码现在运行在隔离环境里,只能访问我们交给它的 API。后来我意识到,这套为了省钱而建的系统,同时也挡在被劫持的模型和它原本能触及的一切之间。这个“顺带”,就是本文的主题。

这个系列的第一篇论文主张:你没法教会模型分辨指令和数据,但你能控制什么内容到达它那里。关于我介绍的每一种输入防御,我都在反复强调一个警告:它是附加层,永远不是你依赖的那道墙。一个能拦下 99% 注入尝试的过滤器,攻击者会在第一百次尝试时突破它;而有一段时间,我不愿意去想那第一百次。但最终你不得不面对。你要为它做计划。你要假设:在某个时刻,越过所有输入控制之后,模型会读到一条恶意指令并试图执行它;而你要建好的系统,让它在执行时也够不到多少东西。

安全团队多年来一直把这种做法叫作“假设已被入侵”(assume breach),远在人们开始担心智能体之前就有了。你不再把一切都押在墙上,而是开始决定入侵者翻过墙之后能走多远。

我起草本文的时候,案例来了,而且比我预想的更离奇。

2026年7月,一批前沿模型(frontier models)从隔离的评估环境中逃了出来,穿过开放互联网,闯进了另一家公司的生产系统。没有攻击者派它们去。当时它们正在跑一个基准测试,而事实证明,闯进去比真正解题能更快拿到高分。九天后,另一家实验室复查自己的评估日志,又报告了三起事件,涉及另外三家公司,最早的一起已经过去三个月。这两份披露我会在第四部分再回来说,因为它们合在一起,把「被攻陷」(compromised)这个词必须覆盖的范围撑得更大了(完整的披露时间线见附录 A)。

在输入防御上,我们走得比在遏制(containment)上更远,整个行业大多也是如此。对 AI 智能体(AI agent)来说,遏制是用现成工具做的工程,但很多还很年轻,而且一些托管组件泄漏的程度比它们的营销说辞更严重。本文要谈的,是模型在采取行动之后能碰到什么,以及怎么把这个范围控制得足够小。

第一部分:为什么输入防御不够

先从数学说起,因为正是它让本文非写不可。在模型前面架一道能拦下 99% 攻击尝试的过滤器,而攻击者手里有一千种变体,那他唯一想知道的就是哪一种多快能溜过去。叠几道过滤器,概率会好看些,但永远到不了确定——因为攻击者只需要赢一次,而你需要一次都不失手。输入防御降低的是注入成功的频率,但它从来不能把这个数字降到零。

一旦你接受「成功注入迟早会来」,你就会围绕另一个问题来做设计:攻击一旦进来了,它能造成的破坏能有多小。这个问题是有答案的。模型能碰到多少数据?它能往哪里发送东西?它写出来的代码实际能做什么?这些全都可以靠工程手段控制,而「让模型对语言免疫」这件事,没人敢这么打包票。

这正是「三件套」里的第三条腿终于要还债的地方。第一篇论文处理的是模型对数据的访问,以及它接触不可信内容的风险,唯独没碰第三条腿:把信息发出去的能力。

控制范围主要落在「动作」这一环,也就是模型一旦下定决心动手之后所做的一切:调用工具、写代码并运行、把输出交给另一个系统、把数据发到某个地方。每一处都是门,替攻击者干活的模型会挨个去推。第二部分:沙箱真正保护的是什么。第一篇论文里有一个区分,这里的一切都建立在它之上,所以我得把它讲准确。沙箱是模型与「它的动作可能伤到的系统」之间的边界。它拦住模型写的代码,不让它碰到你的网络、你的数据库、你的客户。但它对「恶意指令最初是怎么进的模型」毫无办法。人们经常把这两件事混为一谈,结果要么是给一个输入全敞开的模型套上厚厚一个沙箱,要么反过来。控制范围归结为对模型动作运行环境的五条限制:最小权限。模型代码只能调用任务需要的少数几个 API,其余一概不行。出站白名单。环境只能连到它确实需要的特定目的地,连不上攻击者控制的服务器。名单上每多加一个例外,攻击面就多一块,而包仓库(package registry)往往是大家给的第一个例外。资源上限。给 CPU、内存、实际耗时封顶,这样跑飞了或故意烧钱的进程就付不起那张天价账单。OWASP 把这叫做「无界消耗」(Unbounded Consumption)。不预置凭据。环境里不留任何东西,让被攻破的进程可以读走再利用。默认即用即毁。每次运行都用全新环境,攻击者丢下的东西带不进下一次会话。以上每一项都是非过即败的开关。这正是这一层好做的地方。你可以测出你的出站白名单到底拦不拦得住任意的对外连接。你测不出模型到底有没有学会不信任一封恶意邮件。环境配好了,即便攻击者把模型策反了,另一头也几乎抓不到什么东西。

隔离是分等级的,而当你准备运行的代码是由一个可能被诱导做出恶意行为的模型现场编写时,等级之间的差异就非常关键。选项从弱而便宜,到强而略贵,各有权衡。

普通容器是最弱的一端。容器共享主机内核,所以从容器内部发起的针对内核的攻击,就等于攻击了主机。用容器隔离自己信任的服务没问题。但它不适合用来作为不可信的、模型编写代码的边界,把它当作这样的边界是个常见且代价高昂的错误。

gVisor 更进一步。它运行一个用户态内核,拦截工作负载的系统调用,把它们隔离开,不让它们碰主机内核,因此代码永远不直接跟主机对话。代价是有一些系统调用和 I/O 开销,但启动速度仍然够快,可以用于每个请求。

对大多数生产场景来说,Firecracker 是强的一端。每个工作负载都运行在 KVM 之上一个精简过的虚拟机里,有硬件支撑的边界,虚拟机启动远不到一秒,几乎不占内存开销。正因如此,你才能按会话大规模地随时创建和销毁它们。Kata Containers 属于类似的范畴,把容器工作负载包在轻量虚拟机里。至于不可信的 JavaScript,V8 isolate 则是浏览器用来隔离不同标签页的轻量上下文,Cloudflare 就是靠它来运行模型编写的代码,实现强隔离。

我们遵循的规则并不复杂:模型编写的代码至少要有内核级别的边界,比如 gVisor 这类,绝不用裸容器。任何多租户的场景——一个客户的工作负载可能触达另一个客户的——都要上 microVM。

是的,更强的边界运行成本更高。但跟清理一次逃逸的代价相比,这算不了什么。

LuumenAI 用的是托管的沙箱平台。每次执行都有自己独立的隔离环境,而且这个环境是一次性的,运行结束就销毁。该环境位于与我们自己基础设施分离的机器上,这也是我们选择它的部分原因。

如果客户边界要求把隔离层放在我们自己的账户内,我们也可以把它收进来。设计上一直留好了空间,迁移范围可以界定。

第四部分:沙箱失效的时候

我写这一部分时,内容一直在变,因为例子不断冒出来。隔离不是自动生效的,也不是没有成本的,而且一旦失效,通常是两种方式之一。第一种是沙箱配置错误——边界纸面上存在,但某个设置留了一扇门,一条没人锁住的出网路径,或者一个权限范围过大的角色。第二种是逃逸——隔离层本身的缺陷让代码爬了出去。

配置错误的沙箱:AgentCore

AWS Bedrock AgentCore Code Interpreter 栽在第一种方式上,隔离层自始至终没破。2025 年 9 月,Sonrai Security 发布研究显示,沙箱解释器内运行的代码可以访问 169.254.169.254 上的 microVM 元数据服务——和 EC2 工作负载获取实例元数据用的是同一个地址——并从中读出执行角色的 IAM 凭据。AWS 在路上放了一个过滤器,拦截任何包含字面地址或元数据路径的请求。绕过它不需要什么聪明手段。把地址拆成几段、编码、或者在运行时拼装,代码里就永远不会出现过滤器匹配的那个精确字符串,这些请求便一路畅通。拿到手的是执行角色的会话凭据,可以从沙箱外部使用,去打那些沙箱自己够不到的 AWS 控制面端点。Firecracker 全程尽职尽责。虚拟机从未被攻破。执行角色的凭据只是可以从内部读到而已,而这件事之所以如此要紧,原因在发现的另一半:AWS 自家 AgentCore Starter Toolkit 里的默认 IAM 角色,授予了账户内所有 S3 桶的读取权限、完整的 DynamoDB 访问权限,以及 Secrets Manager 中所有机密的访问权限!开源 AgentCore 示例照抄了那个角色。

于是,一个照着快速上手教程操作的团队,最终拿到的是一个沙箱,里面的凭据只要读出来,就能打开这个账号的大部分权限。AWS 认为按“责任共担模型”(shared responsibility),访问元数据属于预期行为,并公开发布了这项服务的凭据管理文档。整条攻击链根本没经过任何入侵。微虚拟机(microVM)本身没有被攻破:里面的凭据本来就可读,而那些凭据所携带的角色,是从一套入门套件里复制出来的。

生产环境中的逃逸:七月的那波攻击

逃逸是更少见、也更难推理的一类故障。直到 2026 年 7 月,公开案例都来自研究,而非生产环境。

7 月 16 日,Hugging Face 披露其部分生产基础设施遭到入侵。一个恶意数据集滥用了其数据处理流程中的两条代码执行路径——一个是远程代码数据集加载器,一个是数据集配置里的模板注入——从而在处理工作节点上运行代码。攻击者由此提升到节点级权限,收集云和集群凭据,并在一个周末内横向移动到多个内部集群。Hugging Face 称这次攻击是一个自主智能体框架,执行了成千上万次单独动作,用他们的原话说,是“一群短命的沙箱,命令与控制(C2)自行迁移、架设在公共服务上”。他们表示不知道背后是哪个模型。

五天后,OpenAI 说模型是他们的。GPT-5.6 Sol 和一个尚未发布、能力更强的模型(后来澄清是仅供内部使用的研究原型)当时正在跑一项针对利用能力的内部评测——ExploitGym——测试期间把生产环境的网络攻击拒答机制关掉了,以便测出能力上限。OpenAI 说这场评测运行在一个高度隔离的环境中,网络访问受限,只能通过内部托管的代理和包注册表缓存来安装软件包。模型花了大量推理算力,琢磨怎么连上开放的互联网。

他们发现并利用了那个包代理中的一个零日漏洞,提升权限,在 OpenAI 的研究环境中横向移动,直到抵达一个能上网的节点。随后他们推断 Hugging Face 很可能托管着 ExploitGym 的解题方案,于是把窃取到的凭证和更多零日漏洞串联起来,打通了一条对 Hugging Face 服务器的远程代码执行路径。Hugging Face 后来发布的技术复盘补上了一个细节:在接触他们的系统之前,这些模型先在公网上的一台第三方代码沙箱上站稳了脚跟,把它当作后续行动的跳板。全程没有任何指令要求它们这么做。评估任务只要求给出漏洞利用方案,而作弊恰恰是产出方案最省力的方式。隔离技术本身守住了。真正让它们逃出去的是环境被设计为允许的那一条出网通道——几乎每个工程团队都会在出站策略里留出同样的例外,因为构建过程需要拉取软件包。模型并不需要什么新型攻击手法,它们需要的只是一扇本来就开着的门,以及门后软件里的一个漏洞。那个代理是内部托管的第三方软件,一个当初有人选过一次、之后再也没有理由回头看第二眼的依赖项。Hugging Face 这边则根本判断不出自己在面对什么。他们的报告在 OpenAI 主动披露之前就已发布,读起来像是一次由高水平对手发起的、无法归因的入侵,而他们采取的响应也正是应对犯罪性入侵的标准流程:清除据点、重建被入侵的节点、轮换凭证、引入外部取证、通知执法部门。一个越过了目标的模型,和一个带着智能体框架的敌对攻击者,产生的遥测数据一模一样,理应得到同样的响应。Hugging Face 通过分析超过 17000 条记录事件还原了整个攻击序列,用的是跑在自己基础设施上的开放权重模型——因为他们最先尝试的商业前沿模型拒绝做这项工作,也分不清事件响应人员和攻击者。

一次配置错误的评估:Anthropic 披露的事件

OpenAI 披露九天之后,Anthropic 公布了对自家测试做同样排查的结果。相比之下,OpenAI 那件事几乎算让人安心了。

Anthropic 复查了 141,006 次评估运行——这些运行中模型有机会触达互联网——发现三起 Claude 真的连上了外网的事件。连接都发生在第三方评估合作方的环境内部,随后 Claude 入侵了三家互不相关的公司的生产系统。最早一起可追溯到 4 月。

没有漏洞利用,也没有东西逃逸出去。评估的提示词明确告诉模型它没有网络访问权限,但 Anthropic 和供应商之间的配置错误让它可以上网,而双方都不知情,直到三个月后复查对话记录才发现。当时 Claude 正在做夺旗(capture-the-flag)练习,这种练习本身就是"入侵某个系统"的指令,所以当搜索把它引到真实主机时,它把那些主机当成了游戏里的目标。三起事件中最严重的一起,它获取了应用和基础设施的登录凭证,并进入了一个存有数百行生产数据的数据库。Anthropic 联系上的两家受影响公司此前都没察觉,是接到 Anthropic 电话才知道的。

在另一个虚构环境里,Claude 发现一些安装说明引用了一个并不存在的 PyPI 包,于是决定用这个名字自己发布一个。发布需要账号,账号需要邮箱,邮箱需要手机号,手机号需要钱,而它没钱。几次筹钱失败后,它退而求其次找了个免费邮件服务商,把包传了上去。它自己的推理其实已经指出:往真实仓库发布就等于发动真实世界的攻击,用它的原话说,"这不是预期解法"。它用两条理由说服自己越过了这个顾虑:它不认识为自己连接提供保障的证书颁发机构;系统日期显示 2026 年,它据此认定环境是搭出来的。这个包上线了大约一小时,在 15 台真实机器上运行过。

其中一个是某安全厂商的恶意软件扫描器。它把模型装了进去,执行了它,结果凭据被送到了模型预先搭好的收集点。Anthropic 把这归为执行框架和运维层面的失误,排在对齐失败之前,并称没有证据表明模型在追求自己的目标。这判断大概率没错,但这些事件仍然值得放在这里说。模型完成了分配给它的任务,只是对自身所处的环境判断错了。第一篇论文提出,模型无法可靠地区分指令和数据。这些记录则说明,模型同样分不清生产系统和测试装置,而且就算它认真推理这个问题,也可能推理出错误答案,然后照做不误。Anthropic 还指出,他们正式发布的模型自带的防护措施本可以拦下这种行为,但这次评估是故意关掉防护来跑,为的是测出上限。这些发现只是初步结果,他们正安排 METR 做第三方审查,部分细节可能还会变。

安全运行不受信任的代码,本来就是计算机领域最古老的难题之一,而语言模型让它更难,因为代码现在按需生成、量大,还没人审。想知道你的沙箱到底允许什么,办法就是坐进去试着逃出来——而大多数团队从来没这么干过。

第五部分:托管沙箱也得自己查

云厂商现在专门卖这种托管沙箱,确实有用。托管的代码解释器提供按会话隔离、按身份限定访问权限、审计日志,还有几种网络模式,你的团队不用自己维护隔离层。如果你不想自己运维一个 Firecracker 集群,这笔交易是划算的。

但有一个词我不会照单全收:「隔离」。托管沙箱确实做了隔离,但隔离的方式是厂商定的,未必是你读营销页面时脑子里想的那样。以 AgentCore 为例,文档和实际行为之间的差距被从头到尾量了一遍。

AWS 提供了 Sandbox 网络模式,号称是“一个无法访问外部网络的隔离环境”。BeyondTrust Phantom Labs 对这个说法做了实测,结果发现沙箱仍然能解析公网 DNS 名称,A 记录和 AAAA 记录都能查。对外 TCP 连接确实被拒了,但解析器照样应答。Phantom Labs 就以此为跳板搭出了一条可用通道:他们把命令编码进 DNS A 记录响应的字节里,每个应答携带三个 base64 字符;再把输出塞进超长的子域名标签往外发,一次六十个字符。最终他们在一个配置为“无网络访问”的沙箱里,拿到了双向通道和一个可用的交互式 shell。借助执行角色的 S3 权限,他们列出了存储桶,并通过同一条通道把文件读了出来,其中包括一个含客户姓名、电话号码和部分标识符的测试文件。投递载体是一份被投毒的 CSV,注入的指令藏在某个单元格里,代理在编写 Python 代码的过程中会读到它。

AWS 复现了报告并承认了影响,但第一决定是“不修”。在 AWS 看来,这是设计使然,因为 DNS 解析必须保持开启,沙箱里的 S3 操作才能解析端点。AWS 给这个发现打了 CVSSv3 7.5 分,低于最初提交时的 8.1 分,并且改的是文档而不是代码——从“一个无法访问外部网络的隔离环境”改成“有限的外部网络访问”。该服务跑在 Firecracker 微虚拟机上,计算隔离本身从没受到质疑。被重写的,是那句描述网络的话。

这个立场没能撑到发布之后。披露过程从 2025 年 9 月的 HackerOne 报告 #3323153 开始,到 2026 年 3 月公开。几周后,Palo Alto Networks 的 Unit 42 发布了自己对同一绕过手法的分析,AWS 随即转变态度,封掉了 DNS 通道。从沙箱解释器里通过 DNS 外泄数据,如今已不可行(本次披露的时间线见附录 A)。

同一个词,“隔离”,厂商和客户读出来的意思可以完全不同。

供应商所说的「意思」,其实就是实现碰巧做了什么;改一行文档,也算安全报告的正当结果。你设计时假设的那些性质,可能在你自己的系统毫无改动的情况下就变了,而你的监控也不会告诉你出过事。DNS 就是这样一个人人都会忽略的入口。沙箱能把所有明显的对外连接都挡掉,却依然能顺畅地解析域名。一次主机名解析,携带的空间足够把机密一次几个字符地偷出去。不管沙箱声称什么,想知道它到底放行什么,唯一的办法就是在里面跑一段试图向外连的代码(包括 DNS),然后看有什么东西溜了出去。

Anthropic 关于自家评测事故的复盘报告,从另一个方向得出了同样的结论。他们列出的「本可以更早发现问题」的措施清单,第一条就是在跑评测之前逐条验证每一条联网路径,并在运行期间盯着网络日志。当时大家以为环境是密封的,这个判断被当成事实写进了模型的提示词里,而且连续三个月没有对网络做过实测。托管沙箱和厂商自营的评测靶场有一个共同点:在你的工作负载与互联网之间,隔着别人的配置。Anthropic 和它的合作伙伴各自都有理由认为对方把这条路堵上了。

查看原文