CVE-2026-55640: nextcloud-mcp-server: Unauthenticated `POST /webhooks/nextcloud` allows arbitrary vector data deletion when `WEBHOOK_SECRET` is unset ( default )
nextcloud-mcp-server up to 0.117.1 accepts unauthenticated `POST /webhooks/nextcloud` requests because `WEBHOOK_SECRET` is unset by default. Anyone who can reach the server's port can delete or force re-indexing of any user's Qdrant vector embeddings. Upgrade to 0.117.2 and set `WEBHOOK_SECRET`.
Published Aug 25, 2026 · Analysis Oct 25, 2026
What an attacker can do
In nextcloud_mcp_server/vector/webhook_receiver.py, handle_nextcloud_webhook() validates a Bearer header only if secret:. The setting webhook_secret defaults to None, and no startup check requires it, even when vector sync is on. With no secret, the code logs a one-time warning and processes the request anyway.
The handler then trusts the body. webhook_parser.py reads payload["user"]["uid"] and uses it for all Qdrant operations, with no check against a session. An attacker posts a fake BeforeNodeDeletedEvent naming a victim's uid and a document id. Qdrant deletes that document's embeddings. Looping over ids wipes a user's index, and looping over users wipes the whole semantic search index. Recovery needs a full rescan.
The attacker needs only network access to the server port (8000 in the advisory) and no credentials. The result is lost integrity and availability of the search index, not file contents. Agents that rely on this MCP server for semantic search over Nextcloud notes will return empty or stale results until the index is rebuilt.
Who is affected
nextcloud-mcp-server 0.117.1 and earlier, in deployments where WEBHOOK_SECRET is not set. That is the default. Deployments with the secret set already require a Bearer header and reject others, although the advisory frames the default as the flaw.
Check the version and the environment:
pip show nextcloud-mcp-server
env | grep WEBHOOK_SECRETFor a container, run the same checks inside it.
nextcloud-mcp-server
- Ecosystem
- PyPI
- Affected
- <= 0.117.1
- Fixed in
- 0.117.2
- Check
pip show nextcloud-mcp-server
How to fix it
Upgrade to 0.117.2:
pip install --upgrade nextcloud-mcp-serverThe fix commit makes the secret mandatory for webhooks. If WEBHOOK_SECRET is unset, the /webhooks/nextcloud route is not mounted (404), startup logs a warning, and vector sync falls back to the polling scanner. A request that still reaches the receiver gets a 503, and webhook registration through /api/v1/webhooks returns 503. With a secret set, requests without a valid Authorization: Bearer header get 401.
To keep webhook-driven sync, set WEBHOOK_SECRET to a long random value (the docs suggest secrets.token_urlsafe(32)). Re-register existing webhooks afterwards, since the sources do not say how old unauthenticated ones are handled. Do not keep the docker-compose placeholder dev-webhook-secret-change-me outside local development.
How to tell if you were hit
Check access logs for POST /webhooks/nextcloud requests from addresses other than your Nextcloud server, or without an Authorization header. On vulnerable versions the log holds a one-time warning about a missing webhook secret.
In Qdrant, look for sudden drops in point counts per user and for delete operations you did not trigger. A search that suddenly returns nothing for a user is a symptom. Port 8000 should not be reachable from untrusted networks.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
Summary
The POST /webhooks/nextcloud endpoint has no authentication by default: WEBHOOK_SECRET defaults to None and is never required by startup validation. When unset, the receiver accepts any unauthenticated POST. The user_id 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: nextcloud_mcp_server/vector/webhook_receiver.py, function handle_nextcloud_webhook(), lines 55-67
Root cause 1: Auth check is guarded by if secret: - skipped entirely when WEBHOOK_SECRET is unset.
Root cause 2: webhook_secret: str | None = None in config - no startup validator enforces it, even when vector sync is enabled.
Trusted field: payload["user"]["uid"] in webhook_parser.py is used as-is for all Qdrant operations - no cross-check against an authenticated session.
webhook_receiver.py, lines 55-67:
secret = get_settings().webhook_secret # None by default
if secret: # skipped entirely when unset
... validate Bearer header ...
else:
_warn_missing_secret_once() # just logs, still processeswebhook_parser.py, line 57:
user_id = payload["user"]["uid"] # attacker-controlledPoC
No credentials required. Works on any deployment where WEBHOOK_SECRET is not explicitly set (the default).
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.uidin 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
- Enforce
WEBHOOK_SECRETat startup ( fileconfig_validators.py)
if vector_sync_enabled and not settings.webhook_secret:
raise ConfigurationError(
"WEBHOOK_SECRET must be set when vector sync is enabled"
)- Reject requests when secret is unset ( file
webhook_receiver.py)
secret = get_settings().webhook_secret
if not secret:
return JSONResponse({"status": "unavailable"}, status_code=503)
provided = request.headers.get("authorization", "").encode()
if not hmac.compare_digest(provided, f"Bearer {secret}".encode()):
return JSONResponse({"status": "unauthorized"}, status_code=401)Credited to fortress07.
Questions
- Do I need to be logged in to exploit CVE-2026-55640?
No. The endpoint accepts any POST when
WEBHOOK_SECRETis unset, and the attacker supplies the targetuser.uidin the JSON body.- Can the attacker read my Nextcloud files through this?
The advisory describes deletion and forced re-indexing of Qdrant vector embeddings only. It does not describe reading file contents. The damage is to the semantic search index.
- Will webhooks still work after I upgrade to 0.117.2?
Only if
WEBHOOK_SECRETis set. Without it the route returns 404 and sync uses the polling scanner instead.
References
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology
More Other MCP servers and tools vulnerabilities
All Other MCP servers and tools entriesCVE-2026-77361
obsidian-tc : Symlink read-ACL scope bypass via lexical path checks
medium4.7Oct 23, 2026CVE-2026-114126
Storybook dev server vulnerable to code injection from the local network
high8.7Oct 22, 2026CVE-2026-113731
djust: Arbitrary absolute-path file read in the find_handlers_for_template MCP tool
low3.3Oct 21, 2026CVE-2026-114125
Storybook: Missing origin validation in Storybook preview allows code injection from malicious websites
critical9.4Oct 21, 2026CVE-2026-111770
MCP TypeScript server SDK: Shared transport or server instances delivered one HTTP client's messages to another
high7.7Oct 20, 2026GHSA-953h-535j-jg2v
agent-router: MCP session resume does not verify the embedded anti-hijacking subject; default Helm seed makes session IDs forgeable
medium6.8Oct 19, 2026
