当Prompt变成Shell:我读微软关于AI-Agent框架RCE漏洞的一篇研究
这篇文章是我基于微软安全团队原始研究做的中文转写与技术整理。文中配图均保留自原博客,并按原始论证链路穿插到正文中,方便直接在博客里阅读和复盘。
我读完微软这篇文章后,一个最直接的感受是:AI Agent 的安全问题,已经不能再被理解成“模型说错话”这么简单了。
一旦模型被接上插件、代码执行器、搜索能力、文件读写能力、向量检索能力,它面对的就不再只是“内容风险”,而是执行风险。这篇研究最有价值的地方,不是又证明了 prompt injection 很危险,而是它把一个很多人嘴上都在说、但落到工程里常常模糊不清的判断讲透了:
当自然语言能稳定映射到工具参数时,Prompt 就可能成为 Shell 的前置层。
微软这次选取的研究对象是 Semantic Kernel。如果把 LangChain、CrewAI、Semantic Kernel 这类框架视为 Agent 世界里的“操作系统层”,那它们一旦在“模型输出 → 工具参数 → 本地执行”这条链路上出现设计缺口,影响就不会停留在单个 demo,而是会蔓延到大量建立在其上的 AI 应用。
我先说结论:这篇研究真正想提醒我们的是什么?
在我的理解里,这篇文章想传达三件事:
- 模型本身并不是漏洞点。
模型是在按设计工作:理解语言、匹配工具、填充参数。 - 真正的漏洞常出现在框架和工具层对“模型产出”的过度信任。
只要某个参数最终进入eval()、文件写入、下载、命令调用、路径拼接,这条链就可能变成 exploit path。 - LLM 不是安全边界。
你不能把“模型应该不会这么做”当成防护策略,必须用传统安全工程的思路去审视 Agent:输入校验、最小权限、路径约束、执行隔离、端点检测、运行时审计,一个都不能少。
背景:微软在 Semantic Kernel 里找到了两条非常典型的漏洞链
微软在研究 Semantic Kernel 时披露了两个核心漏洞:
CVE-2026-26030:In-Memory Vector Store相关路径可被提示词注入驱动,最终演变为主机级 RCE。CVE-2026-25592:SessionsPythonPlugin暴露出的文件写入能力可被模型调用,进而把“沙箱里生成的恶意脚本”落到宿主机危险路径,实现逃逸后的持久化与执行。
文章最震撼的一句其实是:只靠一条 prompt,就足以让运行 Agent 的设备弹出 calc.exe。
这并不是浏览器 0day、内存破坏或者恶意附件造成的,而是 Agent 做了它“被允许做”的事:解析自然语言、选工具、把参数送进代码路径。
图 1:微软给出的整体示意图,展示了 CVE-2026-26030 如何从本地模型交互一路走到代码执行。
代表性案例:CVE-2026-26030 如何把提示词注入推进到 RCE
这个案例之所以值得反复看,是因为它特别像很多团队日常会写出来的“正常功能”。
微软为了演示这条链,构建了一个“酒店查询 Agent”:
- 用
In-Memory Vector Store存放酒店数据; - 对外暴露
search_hotels(city=...)这样的查询函数; - 当用户说“帮我找巴黎的酒店”时,模型就会调用这个搜索插件;
- 插件再做过滤与 embedding 相似度计算,最终返回结果。
这个设计本身看起来完全合理,甚至很典型。
图 2:微软构造的 Semantic Kernel 酒店查询 Agent,背后接了 In-Memory Vector collection。
问题 1:默认过滤逻辑把模型控制的参数直接拼进了 Python lambda
微软指出,默认过滤函数最终会形成类似下面这种逻辑:
- 输入正常城市名时,会生成一个等价于
lambda x: x.city == 'Paris'的表达式; - 这个表达式随后进入
eval()执行。
图 3:原文中的默认过滤函数定义。
看到这里,问题其实已经很明确了:
一旦 kwargs[param.name] 完全由模型控制,且没有严格清洗,那么这就是一个经典的注入落点。
也就是说,攻击者不需要“攻破模型”,只需要通过提示词注入让模型把一个恶意字符串作为 city 参数传进去,就有机会把“数据查询”改写成“可执行表达式”。
问题 2:开发者意识到了风险,但 blocklist 思路依然挡不住动态语言
文章里提到,框架其实并不是完全没防护。它做了几层检查:
- 只允许 lambda 表达式;
- 解析 AST,阻断明显危险标识符;
- 执行时移除一部分内建函数能力。
图 4:原文给出的 blocklist / AST 校验实现。
但微软团队也很直白地点出了问题:
在 Python 这种动态语言里,用 blocklist 去防“表达式级代码注入”,天然就脆。
因为攻击者可以:
- 用你没列进黑名单的属性名绕过;
- 用不同语法路径重新拿回危险能力;
- 通过类型系统、类层级、运行时特性,间接摸到本该被禁掉的对象;
- 利用 AST 检测盲区(例如某些下标访问、替代写法)绕过节点级检查。
这也是我特别认同这篇文章的一点:它不是在骂某一行代码写得蠢,而是在指出一种错误的防守范式。
攻击是怎么成立的?
微软展示了它们设计的恶意 prompt。核心目标不是“让模型输出敏感内容”,而是逼着模型去调用 search_hotels(),并把恶意参数塞进去。
图 5:原文中的恶意 prompt 示例,要求 Agent 以恶意参数调用 search_hotels。
随后,Agent 真的触发了对应的工具调用。
图 6:模型最终发起了携带恶意参数的 search_hotels 调用。
接下来发生的事情,就是整个漏洞链里最关键的一步:
- 恶意参数逃逸出原本的模板字符串;
- 进入
eval()执行环境; - 再借助 Python 的类型系统和导入器链路,重新拿到执行系统命令所需的能力;
- 最终触发任意命令执行。
换句话说,这不是“模型自己学会了攻击”,而是:
模型帮攻击者把一条精心构造的输入,成功送达了本来就危险的执行上下文。
为什么原有防护没挡住?
微软把绕过原因拆得很清楚。我把它整理成更适合工程复盘的四点:
1)危险名字列得不全
攻击链里用到的一些关键属性/方法,根本不在 blocklist 里。只要黑名单不完整,攻击者就总能找到新的路径。
2)结构检查通过了,但语义是恶意的
攻击 payload 仍然被包装成“合法 lambda 表达式”,因此“只允许 lambda”这条规则在结构上是通过的。
3)清空 __builtins__ 不等于安全
哪怕把内建函数删掉,只要攻击者还能从对象图、类型系统、类层级、导入器链路里摸回危险能力,保护就只是表面上的。
4)AST 检查存在盲区
如果校验只盯着 ast.Name、ast.Attribute 这类节点,而忽略其他访问路径,那么攻击者完全可以换一种写法继续往前走。
这个漏洞后来怎么修?
微软在负责任披露后,Semantic Kernel 团队做了四层加固。我觉得这部分是最值得开发者直接抄作业的:
- AST 节点白名单:只允许比较、布尔逻辑、算术、字面量等安全构造;
- 函数调用白名单:不是“出现调用就算了”,而是继续核验“调的是不是被允许的函数”;
- 危险属性阻断:重点封堵类层级遍历和对象逃逸常用路径;
- 名称节点限制:把裸标识符压缩到最小范围,例如只允许 lambda 参数本身。
这背后的思路非常重要:
不要问“还能补哪些黑名单”,而要问“这个执行面里到底允许什么”。
也就是说,真正有效的路线通常不是 blocklist,而是 allowlist + capability minimization。
谁会受影响?
微软给出的判断条件很清楚。如果你的 Agent 同时满足以下条件,就需要重点看:
- 使用 Python 版
semantic-kernel; - 版本早于
1.39.4; - 使用了
In-Memory Vector Store,并依赖其过滤能力作为搜索插件后端。
如果命中这些条件,最直接的动作就是升级到 1.39.4 或更高版本。
但我觉得这篇文章另一个很有价值的提醒是:修补之后,还得追问“在修补之前有没有已经被打过”。
所以微软建议把排查重点放到“脆弱版本部署开始”到“升级完成”之间的窗口里,去看:
- Agent 宿主进程是否生成过异常子进程;
- 是否有异常外连;
- 是否出现异常持久化痕迹;
- 是否生成过可疑脚本或工具链落地文件。
这其实已经完全回到了传统主机安全与检测响应的范畴。
第二条链:CVE-2026-25592 把“沙箱内执行”推成“宿主机写文件”
如果说前一个漏洞强调的是“参数注入走到 eval()”,那第二个漏洞更像是Agent 工具暴露边界错误带来的后果。
Semantic Kernel 自带一个 SessionsPythonPlugin,它允许 Agent 在 Azure Container Apps 的动态会话沙箱里执行 Python 代码。正常安全模型是这样的:
- 代码运行在隔离容器里;
- 容器有自己的文件系统;
- 宿主机与容器之间有边界;
UploadFile/DownloadFile这类能力本来只是帮助跨边界搬运数据。
表面上看,这个设计也很合理。
真正的问题:一个本不该暴露给模型的下载函数,被注册成了可调用工具
在 .NET SDK 里,DownloadFileAsync 被错误地加上了 [KernelFunction] 属性。这意味着它被正式宣传给了模型:
- 这是一个可调用工具;
- 这是它的参数 schema;
- 你可以用它。
当这一步发生之后,localFilePath 这种直接决定“宿主机把文件写到哪里”的参数,也就变成了模型可控输入。
于是问题就从“沙箱很安全”一下子变成了:
攻击者不需要打穿 hypervisor,不需要利用容器逃逸,只需要让模型替他调用这个工具。
第二条攻击链是怎么串起来的?
微软把这条链拆成了三个步骤,我觉得非常适合作为你审计自家 Agent 时的 checklist。
第一步:在沙箱里生成恶意载荷
攻击者先通过 prompt injection,诱导 Agent 使用 ExecuteCode 工具,在容器内生成脚本或 payload。
这时候载荷还被关在沙箱里,单独看似乎没什么问题。
第二步:再利用下载工具,把沙箱里的文件写到宿主机危险路径
随后,攻击者继续注入一条指令,让模型调用 DownloadFileAsync,把刚才生成的文件拉到宿主机指定目录。
Agent 随后发起了真正的下载调用。
在微软的例子里,这个目标路径指向了 Windows 的 Startup 目录。这样一来,文件虽然最初诞生于容器,但最终落点已经是宿主机的自启动位置。
第三步:等待宿主机执行
当用户下次登录时,脚本被自动执行,完整 RCE 也就落地了。
这条链条最值得警惕的地方在于:
沙箱本身不一定被“攻破”了,但它的价值被暴露给模型的桥接工具直接抵消了。
所以很多团队今天谈 Agent 隔离时,容易把注意力过多放在“代码在哪个容器里跑”,却忽略了一个更根本的问题:
模型到底还能调用哪些跨边界工具?这些工具的参数是不是完全可信?
这个问题后来怎么补?
Semantic Kernel 的修复很直接,也很有代表性。
首先,它把根因切掉了:移除 [KernelFunction],让这个函数不再暴露给模型。
一旦模型再也看不到这个工具,prompt injection 自然也就没法直接打到这条危险路径上。
其次,它又补了传统工程防线:
- 对下载目标路径做规范化处理;
- 做目录白名单匹配;
- 保证目标路径必须落在允许范围内。
图 12:原文说明,对上传/下载都增加了 opt-in 式路径保护。
如果你的 Agent 使用的是早于 1.71.0 的 Semantic Kernel .NET SDK,那么这条问题链就需要重点关注。
我读完之后最认同的一句话:LLM 不是安全边界
这一点,微软在文中讲得非常彻底。
过去 Web 安全里,我们早就知道:
- 不可信输入不能直接进 SQL;
- 不可信输入不能直接进文件系统 API;
- 不可信输入不能直接进命令执行路径。
今天 Agent 时代,本质上只是把“输入形式”从表单、URL、JSON,换成了自然语言。但它依然是不可信输入。
也就是说,只要模型能影响:
- 文件路径;
- 下载目标;
- shell 命令;
- 过滤表达式;
- 代码执行器入参;
- 插件函数参数;
那这些值就必须被视为攻击者可控数据。
微软把这个问题归纳得很准确:
- 这不是 AI 模型自己“出 bug”;
- 而是 Agent 架构和工具设计让“模型驱动执行”变成了可滥用能力;
- 一旦工具连接到真实系统,prompt injection 的影响就会从“聊天机器人误答”升级成“文件写入、数据窃取、RCE”。
从防守视角看,我认为这篇文章给了 5 个可立即落地的检查点
1)把所有模型可控参数都当成 attacker-controlled input
不要因为参数来自 tool call schema,就默认它“比普通用户输入更可信”。本质上它仍是模型中转后的不可信数据。
2)优先做 allowlist,而不是继续堆 blocklist
尤其在 Python、JavaScript 这类动态语言里,表达式执行和对象访问能力非常灵活,单靠黑名单很容易被绕开。
3)重新审视“本不该给模型”的工具
很多问题不是出在核心业务逻辑,而是某个辅助方法、桥接函数、下载函数、调试函数、批处理函数,意外地被注册成了可调用工具。
4)把端点检测接回 Agent 宿主
如果 Agent 理论上只是做检索、归档、搜索、问答,那它突然:
- 拉起
cmd.exe/powershell.exe; - 写入启动目录;
- 产生异常子进程;
- 发起不符合业务语义的外连;
这就应该被当成高优先级安全事件看待。
5)不要把“有沙箱”当成万灵药
真正要问的是:
- 模型是否能调用跨越沙箱边界的工具?
- 工具是否能接触宿主机路径?
- 参数有没有最小化、白名单化和路径规范化?
- 下载/上传是否被限制在明确的受控目录内?
原文最后还给了一个 CTF,这个设计挺有意思
微软把其中一个脆弱案例打包成了一个可交互的 CTF,核心目标是让研究人员在受控环境里亲手感受:
- prompt injection 怎么越过“语言层”;
- 工具调用怎么成为真正的 exploit primitive;
- 为什么“让模型调工具”这件事,本质上已经进入系统安全问题域。
原始 CTF 地址:
最后总结一下
如果只看标题,很多人可能会把这篇文章理解成“微软又报了两个 CVE”。
但在我看来,这篇文章真正重要的意义是:它把 Agent 安全里一个正在被快速放大的现实讲清楚了——
当 Prompt 可以稳定驱动工具调用时,语言就不再只是语言,它已经开始接近执行入口。
所以今天讨论 AI Agent 安全,已经不能只盯着:
- jailbreak 成不成功;
- 回复有没有越权;
- 模型有没有泄露提示词。
更要盯着:
- prompt 是否能改写工具参数;
- 工具参数是否进入危险 sink;
- 框架是否把内部 helper 暴露成了模型工具;
- 隔离边界是否被“桥接函数”无声打通;
- 宿主机侧是否具备足够的运行时检测能力。
如果你的团队正在做 Agent 化改造,我会建议把这篇研究当成一份非常典型的审计样本:不是为了照抄某个 exploit,而是为了重新定义你的安全建模方式。















