Skip to content

Nextcloud app-layer authority drift: Circles keyId blind SSRF, WebDAV absolute-path locks, viewer-config leaks, and optional-guard fail-opens

Source: hourly offensive-security scan of GitHub Security Advisories, 2026-09-18 (unreviewed wave published 2026-09-18T03:30Z).

One theme ties the durable records: bundled/first-party apps reimplement the platform's security machinery and drift from it. Nextcloud core ships SSRF protections, per-user path confinement, and per-board/per-file authorization — but Circles opts its key-fetch out of the SSRF guard, the DAV plugin resolves identity from the request URI instead of the session, Deck's config API skips the board-ownership check every sibling route performs, Photos builds an owner's album listing from the viewer's folder config, and Approval's freshness check only runs when the caller volunteers the parameter. Each is a separate audit axis that generalizes far beyond Nextcloud.

Advisory table

App / component GHSA CVE Sev. Boundary
Circles GHSA-h4g5-x94v-qxqg CVE-2026-77164 medium Remote-instance signature verification fetches the attacker-supplied keyId URL before trust in the remote instance is established, and explicitly allows local/private addresses for this request, bypassing core SSRF protections. Public unauthenticated POST /apps/circles/event/ and POST /apps/circles/incoming/ reach the path → unauthenticated server-side GET to internal addresses. Response body not returned → blind SSRF reachability oracle
DAV (WebDAV locking) GHSA-3vwx-p83m-w7p5 CVE-2026-82980 medium Lock/unlock handlers resolve the file from the absolute request URI without verifying the path segment matches the authenticated session user → any authenticated user locks another user's files (blocking PUT/MOVE/DELETE and editor saves) and the app returns the lock token to unauthorized callers, enabling removal of other users' client (token-based) locks
Deck GHSA-c82v-5g56-99px CVE-2026-77170 medium Board-scoped config API accepts arbitrary boardId with no ownership/manage-permission check → any authenticated user writes configuration keys on boards they cannot see
Photos GHSA-fp2m-3769-mjc9 CVE-2026-82985 medium Filter-based "smart albums" build the file listing from the viewing user's photosSourceFolders search configuration rather than the album owner's → a share recipient discovers names/IDs/metadata of owner files in folders never intended for the shared album
Approval GHSA-w4mj-3r4x-wxm3 CVE-2026-82982 medium Approve/reject etag freshness check is enforced only when the etag parameter is present and non-empty → omit the field entirely and a MITM/modifier gets approval recorded for a file version nobody reviewed
Team Folders + Workspace GHSA-v94x-5qwj-fqpj CVE-2026-77169 medium API/REST-only delegated administrators for team folders bypass folder-level authorization — the delegated-scope restriction is not applied on the REST entry point

Tracked without publication (sparse mirrors or no reusable operator workflow): HGiga OAKlouds unauthenticated deserialization RCE + relative-path arbitrary file read (GHSA-7qh3-jh73-mv66/CVE-2026-93467, GHSA-3q98-96r7-4g5v/CVE-2026-93468, detail-free vendor product), django-page-cms staff-readable page content + five CSRF-exempt admin mutation views (GHSA-v2qc-hf9w-393g/CVE-2026-93455, GHSA-5j3h-q5f7-g2vq/CVE-2026-93456), NASA CryptoLib SDLS receiver selects the Security Association from the in-frame SPI without verifying the SA is authorized for the frame's GVCID (GHSA-64r5-qp28-crvm/CVE-2026-79954 — a clean SPI-to-context identity-binding pattern worth revisiting if space-link targets enter scope), Acronis backup-plugin insecure-file-permissions LPE trio (GHSA-hrq3-qgq7-jm9x/CVE-2026-87886), M365 Copilot / Azure Database for PostgreSQL / Azure AI Foundry / Azure Arc / Logic Apps / Dataverse / Fabric / Container Registry Patch-Tuesday-style GHSA sync wave with no actionable detail, snappy-java + go-openapi/swag parser overflows, redis-parser RESP DoS, Poppler/GPAC memory-safety singles, WordPress RT Mega Menu/Brizy stored-XSS singles.

Why this is worth an operator page

  • App-level fetch paths routinely opt out of the platform's SSRF guard. Circles needed an outbound key fetch and whitelisted local/private ranges for it, silently defeating core protections on two unauthenticated POST endpoints. Audit rule on any framework with a central SSRF helper: enumerate outbound HTTP calls and check each one calls the helper; explicit "local addresses allowed" exceptions on a request whose URL (or a fragment of it, like a keyId) is attacker-controlled are top-tier blind-SSRF candidates. Pair with the Sept 17 Nuxt OG Image outer-status oracle pattern — a blind endpoint like this still yields an internal reachability oracle from response/error differentials.
  • Alternate protocol surfaces re-resolve identity from the request, not the session. The WebDAV lock handlers trusted the absolute URI path to identify the file owner context. Same axis as the July 28 wp-admin public-namespace triad and the Sept 17 Keycloak versioned-route family: REST, WebDAV, OCS, and app-specific endpoints over the same object store each re-implement authorization, and the copy drifts. Decision-table probe: for every object-scoped action on the primary API, run the equivalent on each alternate protocol surface as a different low-privilege user and compare verdicts. Bonus: a lock token returned to a non-owner converts a write-availability bug into lock-management takeover.
  • Config/setting endpoints are the least-audited sibling of resource CRUD. Deck's card/board CRUD checks permissions; the config key on the same board checks nothing. When enumerating any resource API, list the auxiliary sub-resources (config, settings, labels, favorites, activity, comments) and test ownership on each — they were usually added later by a different commit author.
  • Wrong-principal configuration application is a metadata oracle. Photos answered "which of the owner's files match this filter?" using the viewer's source-folder scope. Wherever a shared, saved, or templated artifact is evaluated (not stored) at read time, ask whose configuration the evaluator injects. A share normally considered read-scoped becomes a targeted enumeration primitive against the owner's tree.
  • "Optional security parameter" is fail-open. Approval's etag check ran only if the client sent the etag. Any control gated on the presence of a request field — nonces, confirmation tokens, dry-run flags, checksums, version guards, second factors — must be tested with the field absent, not just forged. This is the parameter-omission twin of the null==null Sept 5 AVideo MFA bypass.

Validation workflow (authorized scope only)

Two disposable accounts, owned callbacks

Use a lab Nextcloud instance (or explicitly authorized target) with two synthetic users, synthetic files/albums/boards, and owned no-content callbacks. Never lock or modify real users' files, never enumerate real owners' trees, never probe internal services outside the authorized scope, and never intercept or replay a real approval flow.

  1. Circles-class keyId SSRF: as an unauthenticated caller, POST /apps/circles/event/ with a keyId pointing at an owned callback; record the outbound GET (User-Agent, source IP, whether it follows redirects). Then, only within authorized scope, a private-range canary to confirm the local-address allowance; capture reachability booleans via timing/error differentials plus the outer response — no response-body claims. Positive: server-side GET to attacker-selected URL fires before any trust establishment (error text often confirms parse-then-fetch ordering).
  2. WebDAV lock decision table: users A and B, one marker file each. B sends LOCK/UNLOCK against A's absolute DAV path; then attempt lock-listing to see if A's token is disclosed. Evidence: lock state change on A's file + token value presence in B's response (redact in reports). Negative control: same op against B's own file.
  3. Deck config sibling-audit: user B PUTs a synthetic config key on user A's board ID while B's board-list shows no such board. Positive: 2xx and key readable by A; compare against card-create on the same board (expected 403) to prove the drift is sub-resource-specific.
  4. Photos viewer-config oracle: owner seeds two folders, shares one filter-based smart album with the viewer; viewer sets photosSourceFolders to include the unshared folder and reads the album. Evidence: presence-only — filename/ID of the owner's unshared marker appears. Never harvest beyond seeded markers.
  5. Optional-parameter omission: in the lab Approval flow, approve with a stale/omitted etag and record whether the decision is accepted. This is a decision-table test (send/stale/omit × accept/reject), never a MITM of a real approval.
  6. Delegated-admin REST parity: provision a Workspace-delegated admin scoped to folder X; exercise folder Y over REST and over the UI. Positive: REST accepts, UI rejects — the entry-point drift table is the deliverable.

Reporting heuristic: title by the drifted control — "core SSRF guard bypassed by app-level keyId fetch", "DAV path resolved without session-owner check", "board-config sub-resource skips ownership", "album evaluated with viewer's folder scope", "freshness check skipped when parameter omitted" — and always include the primary-surface vs alternate-surface verdict table. These are unreviewed mirrors: confirm exact app versions and endpoint behavior against the linked advisories and the instance itself before reporting.