CVE-2026-55640: Nextcloud MCP Server: Unauthenticated `POST /webhooks/nextcloud` allows arbitrary vector data deletion when `WEBHOOK_SECRET` is unset ( default )
Summary The POST /webhooks/nextcloud endpoint has no authentication by default: WEBHOOKSECRET defaults to None and is never required by startup validation. When unset, the receiver accepts any unauthenticated POST. The userid is taken directly from the attacker-supplied payload and passed to Qdrant, allowing an unauthenticated attacker to delete or corrupt vector embeddings for any user.
Details Vulnerable file: nextcloudmcpserver/vector/webhookreceiver.py, function handlenextcloudwebhook(), lines 55-67
Root cause 1: Auth check is guarded by if secret: - skipped entirely when WEBHOOKSECRET is unset.
Root cause 2: webhooksecret: str | None = None in config - no startup validator enforces it, even when vector sync is enabled.
Trusted field: payload["user"]["uid"] in webhookparser.py is used as-is for all Qdrant operations - no cross-check against an authenticated session.
webhookreceiver.py, lines 55-67: python secret = getsettings().webhooksecret # None by default if secret: # skipped entirely when unset ... validate Bearer header ... else: warnmissingsecretonce() # just logs, still processes webhookparser.py, line 57: python userid = payload["user"]["uid"] # attacker-controlled PoC No credentials required. Works on any deployment where WEBHOOKSECRET is not explicitly set (the default). json POST /webhooks/nextcloud Content-Type: application/json
{ "event": { "class": "OCP\\Files\\Events\\Node\\BeforeNodeDeletedEvent", "node": { "path": "/victim/files/Notes/any.md", "id": 12345 } }, "user": { "uid": "victim" }, "time": 0 } Result: Qdrant deletes all vector embeddings for victim doc 12345 with no authentication. Attacker can loop over doc IDs for mass deletion. All user targets accepted.
Impact + Anyone on the network with access to port 8000 - no credentials needed. + Attacker can delete or trigger re-index of any user's vector embeddings in Qdrant by spoofing user.uid in the payload. + Mass-sending delete events for all doc IDs destroys the entire semantic search index for all users, requiring a full re-scan to recover.
Recommend Fix 1. Enforce WEBHOOKSECRET at startup ( file configvalidators.py ) python if vectorsyncenabled and not settings.webhooksecret: raise ConfigurationError( "WEBHOOKSECRET must be set when vector sync is enabled" ) 2. Reject requests when secret is unset ( file webhookreceiver.py ) python secret = getsettings().webhooksecret if not secret: return JSONResponse({"status": "unavailable"}, statuscode=503) provided = request.headers.get("authorization", "").encode() if not hmac.comparedigest(provided, f"Bearer {secret}".encode()): return JSONResponse({"status": "unauthorized"}, statuscode=401)
Other sources
Nextcloud MCP Server is a production-ready MCP server that connects AI assistants to a Nextcloud instance. Prior to 0.117.2, the POST /webhooks/nextcloud endpoint in nextcloudmcpserver/vector/webhookreceiver.py has no authentication by default because WEBHOOKSECRET defaults to None and startup validation does not require it. When WEBHOOKSECRET is unset, handlenextcloudwebhook() accepts unauthenticated requests. The payload["user"]["uid"] field parsed in nextcloudmcpserver/vector/webhookparser.py is attacker-controlled and is used without an authenticated-session cross-check for Qdrant operations, allowing a network attacker to delete or trigger re-indexing of vector embeddings for any user and to destroy the semantic search index by sending forged deletion events. This issue is fixed in version 0.117.2.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/nextcloud-mcp-serverto a version that resolves this vulnerability.Fixed in 0.117.2 - Upgrade
Upgrade
nextcloud_mcp_serverto a version that resolves this vulnerability.Fixed in 0.117.2 - Configuration
Enforce that WEBHOOK_SECRET is set at startup using nextcloud_mcp_server/config_validators.py (startup validator). If WEBHOOK_SECRET is unset, do not accept unauthenticated requests to POST /webhooks/nextcloud.
nextcloud_mcp_server (vector sync / Nextcloud webhook receiver) WEBHOOK_SECRET = set to a non-empty secret when vector_sync is enabled - Configuration
In handle_nextcloud_webhook() (webhook_receiver.py, lines 55-67), when vector_sync_enabled is true and settings.webhook_secret is not set, reject requests instead of processing. Current logic shows: `if not secret: ... JSONResponse({'status':'unavailable'}, status_code=503)` (and unauthorized on Bearer mismatch). Ensure the receiver returns 401/503 and does not reach Qdrant operations when WEBHOOK_SECRET is unset.
nextcloud_mcp_server/vector/webhook_receiver.py (handle_nextcloud_webhook) authentication behavior when webhook_secret is unset = reject
Event History
Frequently Asked Questions
Who can exploit this issue?
Any network attacker able to send requests to the POST /webhooks/nextcloud endpoint can exploit it when WEBHOOK_SECRET is unset. No authentication, privileges, or user interaction are required.
Are default deployments affected?
Yes. WEBHOOK_SECRET defaults to None, and startup validation does not require a value, so the webhook endpoint has no authentication by default.
What can an attacker do through the exposed endpoint?
An attacker can forge webhook deletion events and control the user UID in the payload. This can delete vector embeddings or trigger re-indexing for any user, including destruction of the semantic search index.
What should be done if upgrading is not immediately possible?
Set WEBHOOK_SECRET to prevent unauthenticated webhook requests. Restrict network access to the webhook endpoint so untrusted network clients cannot reach it.
How can I determine whether an instance is affected?
Instances running versions before 0.117.2 are affected if WEBHOOK_SECRET is unset. Review the deployment environment or configuration for a configured WEBHOOK_SECRET value and verify the installed version.