Know every vulnerabilitybefore it knows you.
DevGuard continuously monitors your dependencies and alerts you when CVEs like this one affect your stack — with real-time threat intelligence built for developers.
GHSA-38xv-hf3p-h7mq
Summary
Multiple backends, when given a specially crafted object to copy, can escape the backend confinement.
| Backend | Keep/Close | Per-backend severity |
|---|---|---|
| sftp | Medium | Real filesystem escape, fires under default encoding. |
| smb | Low-Medium | Escapes to a different SMB share the credential can reach. |
| ftp | Low | Real, leading-.. overshoot PoC is partly neutralized by encoding; escape bounded to at/below the login base. |
| webdav | Low | Server-side ACLs are the real boundary. |
| b2 | Low | Same-account sibling bucket crossing on a flat keyspace. |
| swift | Low | Same, container. |
| qingstor | Low | Same. |
| oracleobjectstorage | Low | Same. |
| internetarchive | Low | IA items are owner-writable only; confined to user's own items. |
| storj | Low | Can retarget a different bucket in the same access grant. |
| filelu | Low | Confined to the user's own account. |
| shade | Low | Confined to the user's own drive. |
| sia | Low | siad API password already grants full-daemon access. |
Root cause
rclone core does not sanitize .. in a source object's Remote() - verified: nothing in fs/march, fs/sync, fs/list, or fs/operations rejects .. segments before the name reaches the destination backend's Put/Update/Mkdir. Confinement is therefore each backend's responsibility, and these backends join root + remote without a check.
This divides into two classes:
-
Bucket based backends -
bucket.Split(path.Join(f.root, rootRelativePath)):backend/b2/b2.go:404,backend/swift/swift.go:464,backend/qingstor/qingstor.go:198,backend/oracleobjectstorage/oracleobjectstorage.go:245,backend/internetarchive/internetarchive.go:1016,backend/smb/smb.go:885,backend/storj/fs.go:289.path.Joincollapses..on the standard (ASCII) form before encoding is applied (e.g.FromStandardPath(path.Join(...))atbackend/b2/b2.go:1641), soEncodeDotnever gets the chance to neutralize the...lib/bucket.Joindoes not clean paths (keeps..as a literal key segment);path.Joindoes.backend/s3,backend/azureblob,backend/googlecloudstoragealready usebucket.Joinand are therefore not affected.
-
Path based backends -
path.Join(root, remote)onto a real path:- sftp:
remotePath = path.Join(f.absRoot, f.opt.Enc.FromStandardPath(remote))(backend/sftp/sftp.go:2497). Default encoding isencoder.Display(==Standard), andFromStandardPathshort-circuits to a pass-through in that mode, so..survives;f.absRootis absolute, sopath.Join("/home/user/root", "../../../../etc/passwd")->/etc/passwd. - webdav:
filePathatbackend/webdav/webdav.go:426-432. - ftp:
path.Join(f.root, remote)at ~14 sites (e.g.backend/ftp/ftp.go:1247). - filelu, shade, sia: analogous joins.
- sftp:
Precondition that limits reachability
For any of these to fire, a source must hand rclone a Remote() containing raw ... That is only possible when:
- the source is a flat-keyspace object store (not a filesystem - a local/sftp/smb source cannot represent
../../xas one directory entry), and - the offending key was written with native, non-rclone tooling - rclone's own writer applies
EncodeDotand rewrites a..segment to fullwidth.., so you cannot create such a key through rclone.
rclone's source-side listing does pass a natively-planted raw .. key through unchanged (verified for b2: remote := file.Name[len(prefix):] after ToStandardPath, backend/b2/b2.go:858,867). The reports never establish this precondition; it is the same omission across every member of the class.
Example attack
# Step 1 - attacker, using NATIVE S3 tooling (NOT rclone) on a source the victim ingests from:
aws s3api put-object --bucket shared-drop --key '../../victim-backups/pwned.txt' --body evil.txt
# Step 2 - victim's ordinary ingest:
rclone copy s3-drop:shared-drop b2:victim-uploads/incoming
# path.Join("victim-uploads/incoming", "../../victim-backups/pwned.txt") = "victim-backups/pwned.txt"
# -> lands in the victim's victim-backups bucket instead of under incoming/
The blast radius is the victim's own account (a bucket/share/path the configured credential already reaches) - integrity misdirection, not a cross-tenant or confidentiality breach. sftp/smb are the exception in reach (server filesystem / other share), still bounded by the login's own permissions.
Precedent
This is the same class as the already-fixed local backend advisory https://github.com/rclone/rclone/security/advisories/GHSA-7p4m-qxvv-g567, which added (*Fs).localPath returning errPathEscapes for names resolving outside the root (backend/local/local.go:819-826). That fix was justified because the destination was the operator's own OS filesystem; the same reasoning extends (at lower severity) to sftp/smb.
Upload your own SBOM in CycloneDX 1.6 or higher (JSON) directly here to check your vulnerabilities.
Drag and drop some file here, or click to select
The vulnerability can be exploited over the network without needing physical access. It is difficult for an attacker to exploit this vulnerability and may require special conditions. An attacker does not need any special privileges or access rights. The attacker needs the user to perform some action, like clicking a link. The impact is confined to the system where the vulnerability exists. There is a high impact on the integrity of the data.
Exploitation attempts have been detected. Elevated vigilance and prompt remediation are advised.
The exploit probability is very low. The vulnerability is unlikely to be exploited in the next 30 days.
We did not find any exploit available. Neither in GitHub repositories nor in the Exploit-Database.
Browse More
Continuously monitor your dependencies and get alerted when vulnerabilities like this one affect your stack.
Checkout DevGuard