CVE-2025-14279: MLflow DNS Rebinding 漏洞分析
MLflow DNS Rebinding 漏洞分析
CVE-2025-14279: MLflow DNS Rebinding 漏洞分析
一、漏洞概述
1.1 漏洞简介
MLflow 是一个开源的机器学习生命周期管理平台,由 Databricks 开发。该漏洞存在于 MLflow REST Server 中,由于缺乏 Origin/Host 头验证,导致服务器容易受到 DNS Rebinding 攻击。
攻击者可以通过恶意网站绕过浏览器的同源策略 (Same-Origin Policy) 保护,对 MLflow REST API 端点执行未授权调用,从而实现对实验数据的查询、更新和删除操作,可能导致数据泄露、数据破坏或数据篡改。
二、DNS Rebinding 攻击原理
2.1 什么是 DNS Rebinding?
DNS Rebinding 是一种利用 DNS 解析机制绕过浏览器同源策略的攻击技术。攻击的核心思想是:
- 攻击者控制一个域名(如
evil.com)及其 DNS 服务器 - 第一次 DNS 查询返回攻击者服务器的 IP(如
1.2.3.4) - 受害者浏览器加载攻击者的恶意页面
- 攻击者将 DNS 记录更改为目标内网 IP(如
127.0.0.1或192.168.1.100) - 当恶意 JavaScript 发起请求时,浏览器认为仍在访问
evil.com(同源),但实际请求发送到了内网服务
2.2 攻击流程图
┌─────────────────────────────────────────────────────────────────────┐
│ DNS Rebinding 攻击流程 │
└─────────────────────────────────────────────────────────────────────┘
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ 受害者 │ │ 攻击者 │ │ MLflow │
│ 浏览器 │ │ DNS/Web │ │ Server │
└────┬─────┘ └────┬─────┘ │ (127.0.0.1) │
│ │ └──────┬───────┘
│ 1. 访问 evil.com │ │
│ ─────────────────────────>│ │
│ │ │
│ 2. DNS 返回 1.2.3.4 │ │
│ <─────────────────────────│ │
│ │ │
│ 3. 加载恶意页面 │ │
│ <─────────────────────────│ │
│ │ │
│ [恶意 JS 等待 DNS TTL 过期] │
│ │ │
│ 4. 再次查询 evil.com DNS │ │
│ ─────────────────────────>│ │
│ │ │
│ 5. DNS 返回 127.0.0.1 ⚠️ │ │
│ <─────────────────────────│ │
│ │ │
│ 6. JS 发送请求到 evil.com │ │
│ (浏览器认为是同源) │ │
│ ─────────────────────────────────────────────────────>│
│ │ │
│ 7. MLflow 返回敏感数据 │ │
│ <─────────────────────────────────────────────────────│
│ │ │
│ 8. JS 将数据发送给攻击者 │ │
│ ─────────────────────────>│ │
│ │ │2.3 为什么 MLflow 受影响?
在 MLflow 3.4.0 及之前的版本中:
- 缺乏 Host 头验证:服务器不验证请求的 Host 头是否合法
- 缺乏 Origin 验证:不检查请求的来源是否在允许列表中
- 无 CORS 保护:允许任意来源的跨域请求访问 API
这使得攻击者可以通过 DNS Rebinding 绕过浏览器的同源策略,对运行在 localhost 或内网的 MLflow 服务发起攻击。
三、漏洞原理详解
3.1 漏洞根因分析
漏洞版本代码 (MLflow ≤ 3.4.0)
在漏洞版本中,MLflow 的 FastAPI 应用直接暴露 REST API,没有任何安全中间件保护:

四、漏洞环境搭建
4.1 环境要求
- Python 3.8+
- MLflow 3.4.0 (漏洞版本)
- 现代浏览器 (Chrome/Firefox)
- 可选:自建 DNS 服务器用于 DNS Rebinding
4.2 安装漏洞版本的 MLflow
# 创建虚拟环境
python -m venv mlflow_vuln_env
cd mlflow_vuln_env
# Windows
.\Scripts\activate
# Linux/Mac
source bin/activate
# 安装漏洞版本
pip install mlflow==3.4.0
4.3 启动 MLflow Server
# 使用 0.0.0.0 以便 DNS Rebinding 攻击可以访问
mlflow server --host 127.0.0.1 --port 5000

4.4 DNS Rebinding 测试域名 (rbndr.us)
本文使用 rbndr.us 服务进行 DNS Rebinding 测试,无需自建 DNS 服务器。
域名格式: <IP1_HEX>.<IP2_HEX>.rbndr.us
测试域名: 72727272.7f000001.rbndr.us
组成部分 | 十六进制 | IP 地址 | 说明 |
第一段 | 72727272 | 114.114.114.114 | 公网 DNS 服务器 IP |
第二段 | 7f000001 | 127.0.0.1 | 本地回环地址 |
工作原理:
- DNS 服务器会在两个 IP 之间随机切换
- 首次解析可能返回 114.114.114.114
- 再次解析可能返回 127.0.0.1
- 浏览器认为是"同源"请求,实际访问了本地服务
验证 DNS Rebinding:
# 多次执行,观察 IP 变化
nslookup 72727272.7f000001.rbndr.us
五、漏洞复现
5.1 使用 rbndr.us 进行DNS Rebinding 攻击
使用 72727272.7f000001.rbndr.us 域名进行真实的 DNS Rebinding 攻击复现。

这里使用get请求进行测试

六、漏洞修复分析
6.1 官方修复 Commit
修复 Commit: b0ffd289e9b0d0cc32c9e3a9b9f3843ae83dbec3
6.2 修复方案概述
MLflow 3.5.0 添加了一个完整的安全中间件层,包括:
- Host 头验证 (DNS Rebinding 防护)
- CORS 保护
- X-Frame-Options 头 (防止点击劫持)
- 安全响应头
6.3 新增安全配置选项
配置项 | CLI 选项 | 环境变量 | 默认值 |
允许的 Host |
|
| localhost, 私有 IP |
CORS 来源 |
|
| localhost:* |
X-Frame-Options |
|
| SAMEORIGIN |
禁用安全中间件 |
|
| false |

6.5 核心补丁代码分析
6.5.1 mlflow/server/fastapi_security.py - 安全中间件核心
这是补丁的核心文件
# mlflow/server/fastapi_security.py (MLflow 3.5.0+)
# 来源: https://github.com/mlflow/mlflow/commit/b0ffd289e9b0d0cc32c9e3a9b9f3843ae83dbec3
from starlette.middleware.cors import CORSMiddleware
class HostValidationMiddleware:
"""
Host 头验证中间件 - 防止 DNS Rebinding 攻击
通过验证 HTTP 请求的 Host 头是否在允许列表中,
阻止来自恶意域名的请求访问内部服务。
"""
def __init__(self, app, allowed_hosts: list[str]):
self.app = app
self.allowed_hosts = allowed_hosts
# 编译通配符模式为正则表达式
self.allowed_host_patterns = [
self._compile_pattern(host) for host in allowed_hosts
]
def _compile_pattern(self, pattern: str):
"""将通配符模式编译为正则表达式"""
import re
# 支持 *.example.com 和 localhost:* 格式
regex = pattern.replace(".", r"\.").replace("*", r".*")
return re.compile(f"^{regex}$", re.IGNORECASE)
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
# 提取 Host 头
headers = dict(scope["headers"])
host = headers.get(b"host", b"").decode("utf-8")
# ✅ 关键修复:验证 Host 头是否在允许列表中
if not self._is_valid_host(host):
# 返回 400 Bad Request,拒绝可疑请求
response = Response(
content="Invalid Host header",
status_code=400
)
await response(scope, receive, send)
return
await self.app(scope, receive, send)
def _is_valid_host(self, host: str) -> bool:
"""检查 Host 是否匹配允许的模式"""
# 移除端口号进行匹配
host_without_port = host.split(":")[0] if ":" in host else host
for pattern in self.allowed_host_patterns:
if pattern.match(host) or pattern.match(host_without_port):
return True
return False
class CORSBlockingMiddleware:
"""
CORS 阻止中间件 - 防止跨域攻击
验证 Origin 头是否在允许列表中,
阻止恶意网站的跨域请求。
"""
STATE_CHANGING_METHODS = {"POST", "PUT", "DELETE", "PATCH"}
def __init__(self, app, allowed_origins: list[str]):
self.app = app
self.allowed_origins = allowed_origins
self.allow_all = "*" in allowed_origins
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
headers = dict(scope["headers"])
origin = headers.get(b"origin", b"").decode("utf-8")
method = scope.get("method", "GET")
# ✅ 关键修复:对状态变更请求验证 Origin
if origin and method in self.STATE_CHANGING_METHODS:
if not self._is_allowed_origin(origin):
response = Response(
content="CORS request blocked",
status_code=403
)
await response(scope, receive, send)
return
await self.app(scope, receive, send)
def _is_allowed_origin(self, origin: str) -> bool:
"""检查 Origin 是否被允许"""
if self.allow_all:
return True
return any(
self._match_origin(origin, allowed)
for allowed in self.allowed_origins
)
class SecurityHeadersMiddleware:
"""
安全响应头中间件 - 防止点击劫持和 XSS
自动添加安全相关的 HTTP 响应头。
"""
def __init__(self, app, x_frame_options: str = "SAMEORIGIN"):
self.app = app
self.x_frame_options = x_frame_options
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
await self.app(scope, receive, send)
return
async def send_wrapper(message):
if message["type"] == "http.response.start":
headers = dict(message.get("headers", []))
# ✅ 添加安全响应头
headers[b"x-content-type-options"] = b"nosniff"
headers[b"x-frame-options"] = self.x_frame_options.encode()
headers[b"x-xss-protection"] = b"1; mode=block"
message["headers"] = list(headers.items())
await send(message)
await self.app(scope, receive, send_wrapper)
def configure_security_middleware(app, config):
"""
配置安全中间件层 - MLflow 3.5.0+ 的核心修复
按正确顺序添加所有安全中间件。
中间件执行顺序(后添加的先执行):
1. SecurityHeadersMiddleware - 添加安全响应头
2. CORSMiddleware - 处理 CORS 预检请求
3. CORSBlockingMiddleware - 阻止非法跨域请求
4. HostValidationMiddleware - 验证 Host 头
"""
if config.disable_security_middleware:
_logger.warning(
"Security middleware is disabled. "
"Only use this when security is handled by a reverse proxy."
)
return
allowed_hosts = config.get_allowed_hosts()
allowed_origins = config.get_allowed_origins()
# ✅ 添加安全响应头中间件
app.add_middleware(
SecurityHeadersMiddleware,
x_frame_options=config.x_frame_options
)
# ✅ 添加 CORS 中间件 (Starlette 标准中间件)
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ 添加 CORS 阻止中间件 (自定义,处理状态变更请求)
app.add_middleware(
CORSBlockingMiddleware,
allowed_origins=allowed_origins
)
# ✅ 添加 Host 头验证中间件
app.add_middleware(
HostValidationMiddleware,
allowed_hosts=allowed_hosts
)
_logger.info(f"Security middleware configured:")
_logger.info(f" Allowed hosts: {allowed_hosts}")
_logger.info(f" Allowed CORS origins: {allowed_origins}")
_logger.info(f" X-Frame-Options: {config.x_frame_options}")6.5.2 mlflow/server/fastapi_app.py - FastAPI 应用集成
补丁修改了 FastAPI 应用创建逻辑,集成安全中间件:
# mlflow/server/fastapi_app.py (补丁修改部分)
from mlflow.server.fastapi_security import configure_security_middleware
def create_app(config):
"""Create FastAPI application with security middleware."""
app = FastAPI(
title="MLflow Tracking Server",
...
)
# ✅ 补丁新增:配置安全中间件
if not config.disable_security_middleware:
configure_security_middleware(
app,
allowed_hosts=config.allowed_hosts,
cors_allowed_origins=config.cors_allowed_origins,
x_frame_options=config.x_frame_options,
)
else:
_logger.warning(
"Security middleware is disabled. "
"Ensure security is handled by your reverse proxy."
)
# Mount Flask app for backward compatibility
app.mount("/", WSGIMiddleware(flask_app))
return app七、总结
CVE-2025-14279 是一个典型的 DNS Rebinding 漏洞,其根本原因在于:
- 缺乏 Host 头验证:服务器接受任意 Host 头,无法防御 DNS Rebinding
- CORS 配置不当:允许任意来源的跨域请求
- 无安全响应头:缺少必要的安全 HTTP 响应头
该漏洞影响 MLflow 3.4.0 及之前的所有版本,攻击者可以通过恶意网站对运行在本地或内网的 MLflow 服务发起未授权访问,窃取或篡改机器学习实验数据。
八、参考链接
MLflow ≤3.4.0因未验证Host头与Origin、缺乏CORS保护,可被DNS重绑定攻击利用,导致本地/内网服务遭未授权API调用。
由 AI 根据全文自动生成,仅供参考,以原文为准