CVE-2026-88016: rclone: Directory metadata (chmod/chown/chtimes) applied through a planted symlink in rclone local --links escapes the destination
Summary With -l/--links, rclone's local backend recreates a source .rclonelink object as a real symlink at the destination verbatim (preserved by design for faithful backups). Directory-metadata application, however, does not go through the os.Root sandbox and does not use NOFOLLOW syscalls. A local Directory always has translatedLink=false, so when the destination path already exists as a planted symlink, rclone applies chmod/chown/chtimes through that symlink to a target outside the destination tree. An attacker who controls the source contents (malicious/compromised remote, shared bucket) obtains attacker-valued chmod/chown/chtimes of an arbitrary path outside the backup destination.
Root Cause - MkdirMetadata (backend/local/local.go:895) calls f.lstat (=os.Lstat, local.go:465) on the destination path. On a pre-planted symlink, os.Lstat succeeds, so the errors.Is(err, os.ErrNotExist) branch (local.go:896) that would create a real directory via the os.Root-guarded f.Mkdir is not taken. Instead a Directory is built directly on the symlink path. - writeMetadataToFile runs raw os.Chown (backend/local/metadata.go:131) and os.Chmod (metadata.go:158); setTimes runs raw os.Chtimes (backend/local/local.go:1318). - The CVE-2024-52522 NOFOLLOW fix (os.Lchown/lChmod/lChtimes) is gated on if o.translatedLink (metadata.go:128/150, local.go:1315). A Directory (newDirectory→newObject with no .rclonelink suffix) is never translatedLink, so it always takes the raw following branch. The CVE-2026-54572 os.Root fix covers only content writes, not metadata syscalls.
Impact Attacker-controlled chmod/chown/chtimes (values taken from the source directory's mode/uid/gid/mtime) applied to any file or directory outside the destination. chtimes (mtime) escape works with just --links and default flags; chmod/chown escape additionally needs --metadata. When rclone runs as root with --metadata and a source uid=0, the chown primitive reaches the CVE-2024-52522 privilege-escalation ceiling (take ownership of an out-of-tree path).
Proof of Concept mkdir -p /src /dest run 1: source object pwn.rclonelink whose body = /home/victim/secret.d printf '/home/victim/secret.d' > /src/pwn.rclonelink rclone sync --links /src /dest # plants /dest/pwn -> /home/victim/secret.d attacker swaps source pwn to a real directory with chosen metadata: rm /src/pwn.rclonelink ; mkdir -p /src/pwn/keep ; chmod 777 /src/pwn rclone sync --links --metadata /src /dest # MkdirMetadata sees /dest/pwn exists (symlink) -> # chmod 0777 applied THROUGH it to /home/victim/secret.d ls -ld /home/victim/secret.d # => drwxrwxrwx (outside dir, attacker-chosen mode) A single-run PoC is achievable against directory-based object sources (drive/onedrive-class) that satisfy both ReadDirMetadata and CanHaveEmptyDirectories and can present pwn.rclonelink and pwn/ simultaneously. Local→local uses the two-run backup model (same repeated-backup model as CVE-2024-52522 and CVE-2026-54572). Verified end-to-end against the real fs/sync.Sync engine on HEAD: the two-run backup backdated the outside target's mtime and chmod'd it 0777 while os.Root correctly blocked the content-copy of pwn/keep — isolating the metadata gap.
Attack Chain 1. Entry. Victim runs rclone copy/sync --links [--metadata] <untrusted-remote>: /dest. Attacker controls source contents. - Guard: none — --links copying an untrusted remote is a documented, supported operation. 2. Plant symlink. Source serves pwn.rclonelink with body = absolute outside path; rclone recreates dst/pwn → outside. - Guard: Fs.symlink routes creation through os.Root.Symlink (local.go:~1552). - Bypass proof: os.Root creates the link verbatim by design (commit 1154afe); the upstream os.Root fix's test TestSymlinkEscapeWriteThroughBlocked confirms only write-through is refused, the link is planted. 3. Deferred dir-metadata fires after transfers. Source presents non-empty dir pwn; setDelayedDirModTimes (sync.go:1002) runs strictly after stopTransfers() (sync.go:988) — after the symlink is planted. - Guard: MkdirMetadata would create a real dir via os.Root-guarded f.Mkdir (local.go:897) inside its errors.Is(err, os.ErrNotExist) branch. - Bypass proof: os.Lstat (local.go:465) on the existing symlink returns success, so the ErrNotExist branch (local.go:896) is NOT taken; f.Mkdir/os.Root never runs. Empirically os.IsNotExist(err)=false for the planted symlink. 4. Sink follows the symlink. CopyDirMetadata→MkdirMetadata→writeMetadataToFile runs os.Chown/os.Chmod (metadata.go:131/158); DirSetModTime→setTimes runs os.Chtimes (local.go:1318) — all on o.path="dst/pwn" with translatedLink=false. - Guard: CVE-2024-52522 NOFOLLOW branch (os.Lchown/lChmod/lChtimes). - Bypass proof: that branch is gated on if o.translatedLink (metadata.go:128/150, local.go:1315); a Directory always has translatedLink=false, so the raw following branch runs. POSIX-confirmed: chmod 777/touch on a symlink path change the target's mode/mtime. 5. Impact. chmod/chown/chtimes on an attacker-chosen path outside the destination, with attacker-controlled values.
Bypass Evidence - if o.translatedLink gates verified verbatim on v1.75.0 at metadata.go:128/150 and local.go:1315; os.Chown/os.Chmod/os.Chtimes on the else branch at metadata.go:131/158 and local.go:1318. - newDirectory→newObject (local.go:581/589/596) never sets the .rclonelink suffix → translatedLink=false for all directories. - MkdirMetadata skip branch: os.Lstat succeeds on planted symlink → errors.Is(err, os.ErrNotExist) false at local.go:896 → guarded f.Mkdir skipped. - Real fs/sync.Sync E2E on HEAD: TestDirMetadataThroughPlantedSymlink (outside dir → 0777), TestDirSetModTimeThroughPlantedSymlink (mtime set, default-on), TestE2ETwoRunBackup (backdated outside target while content-copy blocked by os.Root). All PASS. Control TestControlContentWriteBlocked confirms harness fidelity.
Affected Versions <= 1.75.0. Vulnerable code present on latest release tag v1.75.0 and HEAD (5629f26); git log v1.75.0..HEAD -- backend/local/metadata.go backend/local/local.go is empty (no post-release fix).
Suggested Fix Route directory metadata through os.Root when TranslateSymlinks is set (use fchmodat(ATSYMLINKNOFOLLOW)/Lchown/UtimesNanoAt(ATSYMLINKNOFOLLOW) on the rel path within the root), and/or extend MkdirMetadata to detect that the pre-existing destination path is a symlink and refuse to apply following-metadata — mirroring the CVE-2024-52522 NOFOLLOW branch that currently exists only for translatedLink objects.
--- Reported by zx (Jace) — GitHub: @manus-use
Other sources
rclone is a command-line program to sync files and directories to and from different cloud storage providers. Prior to 1.75.1, when backend/local runs with --links, a source .rclonelink object can plant a symlink in the destination and later directory metadata is applied through that path. MkdirMetadata, writeMetadataToFile, and setTimes operate when Directory.translatedLink=false, so os.Chown, os.Chmod, os.Chtimes, and birth-time handling can bypass os.Root confinement and follow the symlink. An attacker controlling source contents can therefore apply selected ownership, permissions, modification times, or birth times to a file or directory outside the destination, with --metadata required for chmod and chown while modification time is applied by the normal directory workflow. This issue is fixed in version 1.75.1.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
go/github.com/rclone/rcloneto a version that resolves this vulnerability.Fixed in 1.75.1 - Upgrade
Upgrade to a fixed release to a version that resolves this vulnerability.
Fixed in 1.75.1 - Compensating control
Avoid using `--links` with untrusted remotes/sources when running `rclone` with `--metadata`, since directory metadata (`chmod`/`chown`/`chtimes`) can be applied through a planted symlink outside the destination tree.
- Compensating control
If possible, do not use `--metadata` with `--links` against untrusted sources; the material states `chtimes` escape works with just `--links`, while `chmod`/`chown` escape additionally needs `--metadata`.
Event History
Frequently Asked Questions
What conditions are required for exploitation?
An attacker must control source contents and be able to provide a .rclonelink object that plants a symlink in the destination. The affected operation must use the local backend with --links enabled; changing ownership or permissions additionally requires --metadata.
Which metadata can be changed outside the destination?
The attacker can cause selected ownership, permissions, modification times, or birth times to be applied to a file or directory outside the destination. Modification times can be applied through the normal directory workflow, while chmod and chown require --metadata.
Who is exposed?
Deployments that copy or sync attacker-controlled source content to a local destination using rclone local --links are exposed. The impact extends beyond the intended destination because metadata operations can follow a planted symlink despite os.Root confinement.
What is the remediation?
Upgrade rclone to version 1.75.1, which fixes the issue. If upgrading is not immediately possible, avoid running the local backend with --links on untrusted source content; also avoid --metadata where possible to prevent chmod and chown changes.