TL;DR
2026-05-07 Microsoft 安全团队公开了 AI Agent 框架安全研究系列的第一篇:在自家 Semantic Kernel(GitHub 27k+ stars)里发现两个可被提示词注入直接触发的主机级 RCE——CVE-2026-26030(In-Memory Vector Store 的 eval() 注入 + AST 黑名单绕过)和 CVE-2026-25592(SessionsPythonPlugin 误暴露 DownloadFileAsync 导致容器沙箱逃逸)。两条漏洞的共性:AI 模型不是安全边界,框架对模型输出的信任才是漏洞面。修复无需改架构,升级依赖即可(Python SDK ≥ 1.39.4、.NET SDK ≥ 1.71.0)。本文拆解两个漏洞的攻击链、黑名单绕过的四个技术点、以及「模型层 + 主机层」双层防御的具体落地。
为什么这次值得关注:从「内容污染」到「代码执行」
提示词注入(Prompt Injection)在大多数讨论里还是「聊天机器人被骗」——模型说了不该说的话。但 Semantic Kernel 的两个 CVE 展示了 2026 年的真实威胁模型:
提示词注入
│ 框架把模型输出映射成工具调用(tool calling)
▼
工具参数被注入污染
│ 框架信任解析后的参数
▼
执行原语(eval() / 文件写入)
│
▼
主机级 RCE
Microsoft 研究团队的原话:AI 模型本身没有问题——它只是把自然语言解析成工具 schema,行为完全符合设计。漏洞在于框架和工具如何信任被解析的数据。攻击者通过提示词注入控制工具参数,就等于拿到了一条通往 eval() 或文件系统的执行原语。
一条提示词就能在运行 Agent 的设备上启动 calc.exe——不需要浏览器漏洞、不需要恶意附件、不需要内存破坏。Agent 只是「按设计工作」:理解意图、选工具、传参数。
CVE-2026-26030:In-Memory Vector Store 的 eval() 注入
背景:酒店搜索 Agent
Microsoft 构建了一个演示用「酒店搜索」Agent:
- 创建 In-Memory Vector 集合存酒店数据;
- 暴露
search_hotels(city=...)函数给模型(tool calling); - 用户问「Find hotels in Paris」→ 模型调用
search_hotels(city="Paris")。
漏洞点 1:不安全的字符串插值
Semantic Kernel 的默认过滤函数用 Python lambda 实现,并在 eval() 里执行:
# 用户输入 "Paris" → 生成的过滤表达式
new_filter = "lambda x: x.city == 'Paris'"
问题在于 kwargs[param.name](Paris)是 AI 模型可控的输入,且未消毒。这是一个经典的注入 sink:攻击者通过提示词注入让模型输出一个「特殊城市名」,闭合掉引号,追加任意 Python 逻辑:
# 恶意输入闭合引号 + 追加代码
city = "Paris'); import os; os.system('calc'); #"
# 拼出来的 lambda:
lambda x: x.city == 'Paris'); import os; os.system('calc'); #"
漏洞点 2:可绕过的 AST 黑名单
框架开发者其实预料到了 RCE 风险,实现了一个校验器:先把过滤字符串解析成 AST(抽象语法树),检查有没有危险节点,通过后才执行。
思路是对的,但动态语言里的黑名单天然脆弱。Microsoft 的 exploit prompt 构造了以下绕过,四条同时满足:
| # | 绕过点 | 具体手法 |
|---|---|---|
| 1 | 危险名字不在黑名单 | payload 用的属性不在 ast.Name / ast.Attribute 黑名单里 |
| 2 | 结构检查被欺骗 | 整体包在合法 lambda 里,isinstance(tree.body, ast.Lambda) 通过——恶意代码只是 lambda 的函数体 |
| 3 | 空 __builtins__ 无关紧要 |
eval(..., {"__builtins__": {}}) 想移除内置函数,但 payload 从 tuple() 开始(tuple 不需要 builtins),沿 Python 类型系统(__class__ → __mro__ → __subclasses__ → BuiltinImporter)爬上去动态加载 os |
| 4 | ast.Subscript 没检查 |
黑名单只查 ast.Name/ast.Attribute,但 obj['__class__'](下标访问)产生 ast.Subscript 节点,完全绕过检查 |
最终 payload 以合法 lambda 形式逃过 AST 校验,在 eval() 里动态加载 os.system(),启动任意 shell 命令。
修复(四层防护)
升级 Python semantic-kernel 依赖到 ≥ 1.39.4。官方修复采用四层防护,消除把 lambda filter 变成可执行代码所需的每种逃逸原语:
- 过滤函数不再允许用户构造任意 Python 表达式(改为安全 DSL/预编译查询);
- AST 校验升级:处理
Subscript、Call、属性链等所有节点类型; - 执行环境隔离:不再在
eval()里执行用户可控字符串; - 默认拒绝 + 显式允许列表。
自查是否受影响
以下条件全部满足才受影响:
- 使用 Python 版 Semantic Kernel < 1.39.4;
- 使用了 In-Memory Vector Store 且暴露了带参数过滤的搜索函数;
- 搜索参数可由 AI 模型/最终用户输入控制。
CVE-2026-25592:SessionsPythonPlugin 沙箱逃逸
背景:容器边界当安全边界
Semantic Kernel 内置 SessionsPythonPlugin,让 Agent 在 Azure Container Apps 动态会话(隔离的云沙箱)里安全执行 Python。安全模型完全依赖这个边界:代码跑在隔离沙箱里,碰不到 Agent 进程所在的主机。沙箱内外传数据靠 UploadFile / DownloadFile 辅助函数(运行在主机侧)。
漏洞:一个 [KernelFunction] 属性把文件写入变成 AI 可控工具
在 .NET SDK 里,DownloadFileAsync 被误标了 [KernelFunction] 属性——这个属性把它正式注册成模型可调用的工具,附带完整参数 schema:
[KernelFunction] // ← 误标:向 AI 模型暴露为可调用工具
public async Task DownloadFileAsync(string localFilePath, ...)
{
await File.WriteAllBytesAsync(localFilePath, ...); // 无路径校验
}
localFilePath 参数(决定 File.WriteAllBytes() 把数据写到主机哪个位置)现在完全由 AI 控制。没有路径校验、没有目录限制、没有消毒。攻击者不需要复杂 hypervisor 漏洞,只需要提示模型去调用它。
(注:upload_file() 有反向的类似问题——接受任意本地文件路径,允许注入把主机的 SSH 密钥/凭据读出到沙箱。)
攻击链三步
Step 1: 注入指令 → 模型调用 ExecuteCode 在沙箱内生成恶意脚本
(此时 payload 被关在沙箱里,无害)
│
▼
Step 2: 注入指令 → 模型调用 DownloadFileAsync(localFilePath=主机 Startup 目录)
(下载沙箱脚本 → 写入主机 Windows\Start Menu\Programs\Startup)
│
▼
Step 3: 用户下次登录 → 脚本自动执行 → 主机完全沦陷
对应 MITRE ATLAS 技术:AML.T0051 (LLM Prompt Injection) 级联成 AML.T0016 (Obtain Capabilities)。
修复
升级 .NET SDK 到 ≥ 1.71.0。核心修复:移除 [KernelFunction] 属性,让函数对 AI 模型不可见——攻击链的第一步就断了。Agent 现在只能被开发者代码显式调用。
修复版本与自查对照表
| SDK | 受影响版本 | 修复版本 | 自查动作 |
|---|---|---|---|
| Semantic Kernel Python | < 1.39.4 | ≥ 1.39.4 | pip show semantic-kernel 查版本 |
| Semantic Kernel .NET | < 1.71.0 | ≥ 1.71.0 | dotnet list package 查版本 |
| In-Memory Vector Store 用户 | 所有旧版本 | 同上 | 检查是否暴露带参数过滤的搜索函数 |
升级不是重写:Microsoft 明确说明修复只是移除 AI 模型的自主调用能力,Agent 架构不用动。升级后回归测试重点:① 业务侧工具(你自己注册的 [KernelFunction])不受影响;② 内建插件(Vector Store 过滤、SessionsPythonPlugin)行为不变但模型不再能自主触发文件写入。
漏洞窗口期的追溯排查:如果 Agent 曾在受影响版本运行,需要回溯: 1. 界定窗口:部署受影响版本 → 升级到修复版的时间段; 2. 主机层狩猎:窗口期内是否有异常子进程、异常外连、Startup 目录出现新文件、凭据被访问的痕迹; 3. 若发现可疑活动,按主机失陷处理:轮换 Agent 可访问的凭据与 token,排查主机可达的其他系统。
Microsoft 在博客里提供了对应的 Advanced Hunting 查询模板(KQL),可扫「Semantic Kernel Agent 主机 spawn 可疑子进程」和「.NET 宿主 spawn 可疑子进程」两类模式。
常见误区表:Agent 安全防御的五个错误认知
| 误区 | 为什么错 | 正确做法 |
|---|---|---|
| 「系统提示词写『忽略不可信指令』就够了」 | LLM 是概率模型,任何系统提示词都造不出确定性执行边界;编码注入(如盲文指令)能绕过模式匹配 | 指令级防御只是第一层,工具侧校验 + 主机层检测必须有 |
| 「模型输出只是文本,不会造成真实危害」 | Agent 接了工具后,模型输出会被映射成工具调用——参数污染 = 执行原语 | 把工具参数当攻击者输入,做输入校验/白名单 |
| 「框架是微软的,肯定安全」 | 微软自家框架照样出两个 RCE(CVE-2026-25592/26030),官方也承认模型不是安全边界 | 依赖库升级 + 自己的安全检查,谁家都不豁免 |
| 「黑名单能挡住危险操作」 | Python 动态性让黑名单可被绕过:obj['__class__'] 产生 ast.Subscript 节点,绕过只查 Name/Attribute 的校验 |
默认允许 + 白名单,而不是默认拒绝 + 黑名单 |
| 「沙箱/容器 = 绝对隔离」 | 沙箱边界依赖「主机侧辅助函数不被模型调用」,一个 [KernelFunction] 误标就废掉整个边界 |
边界必须双层:沙箱本身 + 暴露给模型的最小工具面 |
同类漏洞图谱:这不是孤例
CVE-2026-25592 / CVE-2026-26030 属于 2025-2026 年集中爆发的一类「框架层 Agent RCE」:
| 漏洞/事件 | 目标 | 本质 |
|---|---|---|
| CVE-2025-53773 | GitHub Copilot | 源码注释/Issue 里的恶意指令禁掉用户确认,授予无限制 Shell |
| CVE-2026-25592 | Semantic Kernel | [KernelFunction] 误标把文件写入暴露给模型 → 沙箱逃逸 |
| CVE-2026-26030 | Semantic Kernel | 模型可控参数进 eval() → AST 黑名单绕过 → RCE |
| Check Point 2026-02 披露 | Claude Code | 仓库配置文件投毒 → 命令执行 |
| OWASP GenAI Q1 2026 | 多个框架 | 8 起重大事件仅 1 个 CVE,其余是过度授权/供应链/注入 |
共性是:框架把模型输出映射到工具时缺乏信任边界。微软研究系列的标题点破了一切——"When prompts become shells"。防御者视角:Agent 框架升级不能只追功能,要追安全补丁。
间接注入:不需要直接对话的攻击面
CVE-2026-26030 的演示里攻击者直接构造 prompt;但真实世界的触发路径更多是间接注入——攻击者不需要和你的 Agent 对话:
攻击者 ──网页/文档/邮件/评论里藏指令──▶ Agent 读取外部内容
│
▼
指令进入模型上下文(被当作任务)
│
▼
模型调用 search_hotels / DownloadFileAsync
│
▼
执行原语被触发
2026 年 4 月 Google Security 和 Forcepoint X-Labs 的遥测已经确认:对手开始在公开网页上投放隐藏指令,专门劫持浏览型 Agent、编码助手和企业 Copilot。这意味着即使你的 Agent 只处理内部数据,只要它碰过任何外部来源的内容,上述两个 CVE 的攻击链就有触发面。
防御落地:模型层 + 主机层双层
Microsoft 的核心建议:AI 模型不是安全边界。防御必须跨两层做关联:
模型层(意图检测)
- 元提示词(meta-prompt)约定工具使用权限:哪些工具可自主调用、哪些需人工确认;
- 内容安全过滤器:对工具参数做输入校验(类型、长度、路径格式);
- 参数白名单:限制
localFilePath等参数只能落在允许目录; - 对高危险工具(文件写入、命令执行、凭据访问)默认不给模型暴露,或加人工审批。
主机层(执行检测)
模型层被绕过是迟早的事(Microsoft 原话:提示词注入「不太可能被完全解决」),主机层必须兜底:
- 端点检测:Agent 进程突然 spawn 子进程、写 Startup 目录、发起异常外连 → 告警;
- 工具调用审计日志(防篡改):记录每次工具调用的参数和结果;
- 最小权限:Agent 进程用非特权账号运行,文件系统写权限最小化;
- 沙箱/容器跑 Agent:即便被注入,爆炸半径限于沙箱。
自查清单
- [ ] 用的 Semantic Kernel 版本:Python ≥ 1.39.4 / .NET ≥ 1.71.0?
- [ ] 所有
[KernelFunction]暴露的工具,其参数是否被当作攻击者输入处理? - [ ] 有没有工具参数能到达
eval()/exec()/ 文件写入 / 命令执行? - [ ] Agent 进程权限是否最小化(非 root、目录写权限受限)?
- [ ] 工具调用有没有审计日志 + 异常告警?
- [ ] 有无容器/沙箱边界,注入后爆炸半径多大?
启示:把每个工具参数当成攻击者输入
两个 CVE 的共同根因不是模型不够安全,而是框架把「解析后的工具参数」当成了可信数据。这跟 Web 安全早期把用户输入直接拼 SQL 是同一类错误——只是现在的「用户输入」是自然语言,攻击者甚至不需要直接对话,只要让 Agent 处理恶意网页/文档/评论(间接注入)就能触发。
对任何 AI Agent 开发者,最实用的一句话:所有从模型传到工具的参数,默认都是攻击者可控输入。工具侧的校验(路径、类型、范围、权限)必须像对待 Web 请求体一样严格。模型层防注入,主机层防执行,两层都有才算及格。
原始出处:Microsoft Security Blog《When prompts become shells: RCE vulnerabilities in AI agent frameworks》(2026-05-07,Microsoft Defender Security Research Team:Uri Oren / Amit Eliahu / Dor Edry);CVE-2026-25592 / CVE-2026-26030(Semantic Kernel,均已修复);CTF 演练:github.com/amiteliahu/AIAgentCTF(CVE-2026-26030)。
本文由 admin 原创,转载请注明出处。
评论
0