MCP 服务器的 ANSI 转义注入:人类看不见、模型照样读
研究指出,攻击者可借助 ANSI 转义序列在 MCP 响应中嵌入隐藏指令,让 AI 智能体执行未授权操作,同时绕过人工审…
近期一篇安全研究提出了一类针对 Model Context Protocol(MCP)服务器的新型注入手法——ANSI 转义序列注入(ANSI Escape Sequence Injection,简称 AESI)。攻击者将不可见的终端控制码混入服务器返回的文本,渲染后的人类审核者看到的是「干净」输出,而语言模型读到的却是包含隐藏指令的原始字节流,并据此执行越权操作。该研究将这一缺陷归类为「输出净化失败」(output-neutralization failure),并指出同类问题此前已在 Kubernetes kubectl(CVE-2021-25743)与 Git(GHSA-7jjc-gg6m-3329)中出现。
什么是 ANSI 转义序列
ANSI 转义序列诞生于 1970 年代,原本用于统一不同厂家硬件终端的控制语法:发送以 ESC(十六进制 0x1B)开头的控制码,即可移动光标、清屏、改色或抹除一行。对终端而言这些字节不显示为文字,但对直接读取原始字节的语言模型来说,它们和任何普通文本没有区别。研究特别强调了两类控制码:
- 隐藏类:让某段文字对终端用户不可见。
- 屏幕与光标类:清屏、移动光标、擦行,可以伪装或抹除本应被看到的输出。
正是「渲染隐藏、原文可见」这一错位,构成了 AESI 的攻击前提。
攻击一:直接拉取型 AESI
许多 MCP 服务器会暴露「抓取 URL 并把内容返回给模型」的工具,例如网页摘要、文档读取等。攻击流程如下:
- 攻击者在自控 URL 中预先布置含 ANSI 转义与隐藏指令的内容。
- 该 URL 作为参数被传入抓取类工具。
- 服务器取得内容后,直接转发到一个「模型可消费字段」(model-consumable field)。
- 模型读到隐藏指令并依其行事。
研究特别指出,「模型可消费字段」并不等于整个 MCP 响应:真正能被代理(agent)摄入的只有 result.content[].text(工具结果)、result.contents[].text(资源读取)、result.messages[].content.text(提示模板)等位置。落在其它字段的载荷不构成利用,这与传统 Web 中的跨提示注入(cross-prompt injection)本质相同——恶意指令搭便车进入数据通道,而非源自用户自身的提示。
攻击二:存储型 AESI
存储型 AESI 使用同样的载荷,但投递机制更危险:攻击者先通过一个入口(笔记、评论、记录等)将载荷写入存储,再由另一个入口在后续读取时触发。三个特性使其危害高于直接拉取型:
- 持久性:载荷存入后会在后续会话中反复生效,可能伤及其他用户。
- 解耦:写入与读取可以是不同的入口,甚至使用不同协议——通过普通 HTTP 写入的数据,可能在使用同一记录的 MCP 工具读取时再次出现。
- 延迟暴露:写入时一切正常,注入只有在稍后某次读取把存储文本拉进模型上下文时才被激活。
研究强调,这些「先写后读」的路径无法从参数名、工具描述或协议类型推断出来,唯一的可靠识别方式是实际探测,而这正是 DAST(动态应用安全测试)扫描器的用武之地。
安全影响与自动检测
文章将 AESI 的可能后果归纳为四类:
- 未授权操作:模型按隐藏指令调用工具、泄露数据、修改分析结果或产出受控输出,且仍处于代理原有权限范围内。
- 绕过人在环路:终端隐藏可使用户复核与审批环节被欺骗,肉眼看到的内容无害,模型却已执行了隐含任务。
- 日志与审计污染:清屏、光标移动与擦行等控制符可篡改终端或日志查看器的呈现,妨碍事后取证。
- 持久化感染:在存储型场景下,载荷长期驻留并反复触发,影响面持续扩大。
文中提出的检测方案来自 Bright Security 的 DAST 扫描器:以黑盒方式对运行中的 MCP 服务发送构造请求,并直接检查真实响应中是否存在未净化的 ANSI 控制码。由于攻击是否成立完全取决于服务器在运行时如何转发数据,源码级分析难以胜任,只有针对在线实例的端到端探测才能给出可靠结论。针对直接拉取型与存储型 AESI,扫描器会分别验证:返回内容在渲染后是否隐藏了指令、写入后再通过模型字段读取时是否会回放载荷。
