🛡️ CVE-2026-55495 — cloudreve

🟡 CVSS 4.3 — Medium ✅ No Known Exploit CWE-22 NVD
4.3
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Cloudreve: Path Traversal in WOPI PUT_RELATIVE Allows Arbitrary File Creation in Owner Account

Summary

Cloudreve's WOPI PUT_RELATIVE handler treats X-WOPI-SuggestedTarget as a path, not a filename. It splits the header on / and joins the segments onto the source file's directory with URI.JoinRaw, which feeds Go's url.JoinPath. url.JoinPath resolves ./.. segments, so a slash-bearing target such as a/../../evil.docx collapses to a location outside the source file's directory. The lower-level upload path then validates only the final, already-cleaned basename (evil.docx), which is harmless, and checks ownership against the *resolved ancestor* — which is still the same user's drive.

A WOPI access token is bound to exactly one file (the route enforces fileId == session.FileID with a 403 otherwise). PUT_RELATIVE escapes that per-file scope: a token issued for one file can create (and, conditionally, overwrite) files elsewhere in the same account.

Root cause (verified at 26b6b10)

1. Token is single-file scoped (the boundary being escaped)middleware ViewerSessionValidation:

```go

fileId := hashid.FromContext(c)

if fileId != session.FileID { // 403 — token is bound to ONE file

c.Status(http.StatusForbidden); c.Abort(); return

}

```

Route: wopi := noAuth.Group("file/wopi", middleware.HashID(hashid.FileID), middleware.ViewerSessionValidation()); wopi.POST(":id", controllers.ModifyFile)POST /api/v4/file/wopi/:id?access_token=<token>.

2. PUT_RELATIVE dispatchrouters/controllers/wopi.go:

```go

case wopi.MethodPutRelative: // X-WOPI-Override: PUT_RELATIVE

err = service.PutContent(c, true)

```

3. SuggestedTarget joined as a pathservice/explorer/viewer.go:

```go

fileName, _ := wopi.UTF7Decode(c.GetHeader(wopi.SuggestedTargetHeader)) // X-WOPI-SuggestedTarget

fileUriParsed, _ := fs.NewUriFromString(fileUri)

if strings.HasPrefix(fileName, ".") { /* treat as extension */ }

fileUri = fileUriParsed.DirUri().JoinRaw(fileName).String() // <-- path join, not basename

...

subService := FileUpdateService{ Uri: fileUri }

res, err := subService.PutContent(c, lockSession)

```

4. JoinRaw splits on / and normalizes via url.JoinPathpkg/filemanager/fs/uri.go:

```go

func (u *URI) Join(elem ...string) *URI {

newUrl, _ := url.Parse(u.U.String())

return &URI{U: newUrl.JoinPath(/* PathEscape each elem */ ...)} // JoinPath cleans ./ and ../

}

func (u *URI) JoinRaw(elem string) *URI {

return u.Join(strings.Split(strings.TrimPrefix(elem, Separator), Separator)...)

}

```

PathEscape leaves . unescaped (it is in the unreserved set), so .. segments survive into JoinPath, which resolves them. URI.Name() returns path.Base(path.Clean(path)) — the cleaned basename.

5. Upload checks ownership of the resolved ancestor and validates only the clean basenamepkg/filemanager/fs/dbfs/upload.go:

```go

ancestor, err := f.getFileByPath(ctx, navigator, req.Props.Uri) // URI already traversal-normalized

...

if _, ok := ctx.Value(ByPassOwnerCheckCtxKey{}).(bool); !ok && ancestor.OwnerID() != f.user.ID {

return nil, fs.ErrOwnerOnly // same-user -> passes

}

...

if err := validateNewFile(req.Props.Uri.Name(), req.Props.Size, policy); err != nil { // checks "evil.docx" only

return nil, err

}

```

validateFileName rejects / \ : * ? " < > | and bare ./.. — but the traversal is already gone by the time it sees the basename.

Validation performed

Independent validation against commit 26b6b10 in a clean sandbox.

Source-verified (static): the full chain confirmed verbatim — single-file-scoped token (403 on mismatch) → PUT_RELATIVE dispatch → DirUri().JoinRaw(SuggestedTarget)url.JoinPath normalization → ancestor ownership check (same-user passes) → basename-only validation of the cleaned name.

Dynamic (control-flow executed): the full binary is not buildable offline here (modules behind an unreachable Go proxy, embedded frontend, DB). I built and ran a harness using the real Go net/url stdlib plus the verbatim Join/JoinRaw/DirUri/Path/Name/PathEscape/shouldEscape and the validateFileName gate, driving the same transformation PUT_RELATIVE performs. Source = cloudreve://my/folder/current.docx:

```

SuggestedTarget resolved URI final basename validator

"copy.docx" cloudreve://my/folder/copy.docx "copy.docx" ACCEPT

"a/../../evil.docx" cloudreve://my/evil.docx "evil.docx" ACCEPT <- ESCAPED to /

"a/../../../top.docx" cloudreve://my/top.docx "top.docx" ACCEPT <- ESCAPED to /

"sub/evil.docx" cloudreve://my/folder/sub/evil.docx "evil.docx" ACCEPT <- different subdir

".pdf" cloudreve://my/folder/current.pdf "current.pdf"

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs low-level privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality none, integrity low, availability none.

Weakness class

CVE-2026-55495 is classified as CWE-22: Path Traversal. A file path built from user input is not confined to the intended directory, letting an attacker reach files elsewhere on the filesystem.

Affected software

CVE-2026-55495 is recorded against 3 packages.

  • github.com/cloudreve/cloudreve/v3
  • github.com/cloudreve/cloudreve/v4
  • unknown

Timeline and source

Published on 24 July 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.

References

github.com (Web)
github.com (Web)
github.com (Package)
github.com (Web)

Details

Severity Medium
CVSS Score 4.3
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
CWE CWE-22
Public Exploit ✅ No
Source NVD
Published 2026-07-24
Updated 2026-08-12
Modified 2026-07-24
Fix URL N/A

Affected Packages

Software From version Fixed in
github.com/cloudreve/cloudreve/v3
github.com/cloudreve/cloudreve/v4
unknown

Similar Threats

Vulnerability Monitoring

Track new vulnerabilities in cloudreve

CVE-2026-55495 is rated CVSS 4.3 Medium. BotEraser monitors your WordPress installation and notifies you when software you use appears in our vulnerability database.

Set Up Free Alerts →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.