Skip to content

GHSA-v3vw-pvpg-chwh

CVE Information

With --remove-source-files the sender unlinks each source file once it has been transferred. successful_send() resolved that unlink (and its same-file safety re-stat) by path -- a plain do_stat/do_lstat and do_unlink on the name, relative to the process CWD that change_pathname() had chdir'd into (so on Cygwin, where the CWD is re-resolved by name, even the chdir'd anchor was path-based). An unprivileged user who controls a component of the source tree races a parent directory from a real directory to a symlink->outside AFTER the file was scanned/sent, so the post-send unlink is redirected and the sender deletes a file OUTSIDE the served module / source tree.

Reachable two ways: a client pulling from a daemon module with refuse options not blocking --remove-source-files (the daemon, possibly running as root, does the delete), and a higher-authority local sender -- the canonical case rsync --remove-source-files -a /src/ remote: run by root over a tree with an attacker-controlled component, which becomes an arbitrary-file-deletion-as-root primitive (denial of service, and a privilege-escalation stepping stone by removing a security-relevant file). Under --copy-links the local sender's path-based safety re-stat also followed the planted parent and leaked the redirected target's dev/ino/size/mtime (the dev/ino compare still prevented unlinking the wrong file, but the metadata leaked).

Fix: resolve the removal's parent through the secure held-dirfd walk anchored at the served module root (daemon) or the transfer-root CWD (local sender), so the per-component O_NOFOLLOW walk refuses a flipped parent on every platform that provides openat()/AT_FDCWD (a pre-AT_FDCWD system falls back to the legacy path-based unlink); confine the safety re-stat through the same held ancestor-dirfd stack (and secure_relative_open() for an uncacheable deep path); and only compute/send the per-file dev/ino when --remove-source-files is actually in effect. Non-daemon senders without --remove-source-files are unaffected.

Test: sender-remove-source-secure (parent-flip race; the unlink must not escape the served module).

Affected: rsync 3.4.3 and earlier; fixed in 3.5.0. (Precise introduced-in ranges being finalised.)