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.
26b6b10)1. 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.
3. The middleware trusts the header verbatim — middleware/file.go:41:
func ContextHint() gin.HandlerFunc {
return func(c *gin.Context) {
if c.GetHeader(dbfs.ContextHintHeader) != "" { // X-Cr-Context-Hint
util.WithValue(c, dbfs.ContextHintCtxKey{}, uuid.FromStringOrNil(c.GetHeader(dbfs.ContextHintHeader)))
}
c.Next()
}
}
4. DBFS restores cached navigator state on a hint hit — dbfs.go:745 (ContextHintTTL = 5 * 60, dbfs.go:34). On a miss it arms PersistState; the closure fires in DBFS.Recycle() at end of request.
5. Persisted share state carries the loaded shareRoot + share row — share_navigator.go:72/:85. RestoreState reinstates n.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) and RemainDownloads <= 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:
func (n *shareNavigator) To(ctx context.Context, path *fs.URI) (*File, error) {
if n.shareRoot == nil { // restored state => NOT nil => Root() skipped
root, err := n.Root(ctx, path)
...
}
...
}
The single-file-share branch is also affected: it calls latestSharedSingleFile, which fetches n.fileClient.GetByID(n.share.Edges.File.ID) straight from the restored share with no revalidation (share_navigator.go).
8. A failed download hook does not block URL issuance — pkg/filemanager/manager/entity.go:250:
if err := m.fs.ExecuteNavigatorHooks(ctx, fs.HookTypeBeforeDownload, file); err != nil {
m.l.Warning("Failed to execute navigator hooks: %s", err) // logged, NOT fatal
}
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.
Setup: one share owner; one recipient (a second free account, or anonymous if the default anon group keeps share-download). Recipient knows the share URL (and password, if any).
GET /api/v4/file?uri=<share-uri> HTTP/1.1
Host: target
Response includes context_hint and each file's path.POST /api/v4/file/url HTTP/1.1
Host: target
X-Cr-Context-Hint: <context_hint>
Content-Type: application/json
{"uri":["<known-shared-file-uri>"]}
(cache MISS → Root runs → PersistState armed → Recycle writes shareNavigatorState to KV under navigator_state_<hint>_share.)X-Cr-Context-Hint, same URI).
(cache HIT → RestoreState sets shareRoot → To skips Root → IsValidShare never runs → signed entity URL returned.)file/content/:id/... validates only the signature.
Expected: step 4 returns ErrShareNotFound / ErrShareLinkExpired.
Actual: step 4 returns a signed, downloadable URL.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.
RestoreState, re-run inventory.IsValidShare and re-compare the current share.Password before trusting cached shareRoot; or bind the cached state to an authorization version that changes on any share edit/delete/download-limit change.navigator_state_* entries when a share is edited or deleted.HookTypeBeforeDownload failures as blocking for share-backed downloads.file/url with the same hint must fail.| Software | From | Fixed in |
|---|---|---|
github.com/cloudreve/Cloudreve/v4
|
- | 4.0.0-20260606032813-26b6b1044b02.x |
A security vulnerability is a weakness in software, hardware, or configuration that can be exploited to compromise confidentiality, integrity, or availability. Many vulnerabilities are tracked as CVEs (Common Vulnerabilities and Exposures), which provide a standardized identifier so teams can coordinate patching, mitigation, and risk assessment across tools and vendors.
CVSS (Common Vulnerability Scoring System) estimates technical severity, but it doesn't automatically equal business risk. Prioritize using context like internet exposure, affected asset criticality, known exploitation (proof-of-concept or in-the-wild), and whether compensating controls exist. A "Medium" CVSS on an exposed, production system can be more urgent than a "Critical" on an isolated, non-production host.
A vulnerability is the underlying weakness. An exploit is the method or code used to take advantage of it. A zero-day is a vulnerability that is unknown to the vendor or has no publicly available fix when attackers begin using it. In practice, risk increases sharply when exploitation becomes reliable or widespread.
Recurring findings usually come from incomplete Asset Discovery, inconsistent patch management, inherited images, and configuration drift. In modern environments, you also need to watch the software supply chain: dependencies, containers, build pipelines, and third-party services can reintroduce the same weakness even after you patch a single host. Unknown or unmanaged assets (often called Shadow IT) are a common reason the same issues resurface.
Use a simple, repeatable triage model: focus first on externally exposed assets, high-value systems (identity, VPN, email, production), vulnerabilities with known exploits, and issues that enable remote code execution or privilege escalation. Then enforce patch SLAs and track progress using consistent metrics so remediation is steady, not reactive.
SynScan combines attack surface monitoring and continuous security auditing to keep your inventory current, flag high-impact vulnerabilities early, and help you turn raw findings into a practical remediation plan.