6.3 Medium
CVSS3
Attack Vector
LOCAL
Attack Complexity
HIGH
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
NONE
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H
3.3 Low
CVSS2
Access Vector
LOCAL
Access Complexity
MEDIUM
Authentication
NONE
Confidentiality Impact
NONE
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:L/AC:M/Au:N/C:N/I:P/A:P
0.0005 Low
EPSS
Percentile
14.8%
The implementation of std::fs::remove_dir_all()
in the Rust standard library is vulnerable to a time-of-check to time-of-use link replacement attack. This applies to all versions of Rust before 1.58.1.
The documentation of std::fs::remove_dir_all()
guarantees that the function does not follow symbolic links:
> Removes a directory at this path, after removing all its contents. Use carefully!
> This function does not follow symbolic links and it will simply remove the symbolic link itself.
The vulnerable implementation for Windows is in library/std/src/sys/windows/fs.rs. For other platforms it is in library/std/src/sys_common/fs.rs. Both use a remove_dir_all_recursive()
helper function which does the actual recursion and deletion. It opens directory by the given path and iterates the directory entries. For each directory entry it checks if the entry is a directory and recurses into it if it is. If it is not it is deleted using std::fs::remove_file()
. On the way back up the now empty directories are deleted using std::fs::remove_dir()
There are two problems with this implementation if the attacker has write access to a directory which is being deleted by the privileged process:
The type of a directory entry is checked and it is being recursed into if it is a directory. There is a short time window between the check and the opening of the subdirectory which an attacker can exploit by replacing the subdirectory with symlink causing the symlink to be followed.
The path given to std::fs::remove_dir_all()
is extended with subentry paths which are then used to process subdirectories and delete directory entries. Paths are resolved by the operating system each time they are passed to a system call. If the attacker can replace a descendent directory of the directory passed to remove_dir_all()
while a subdirectory of it is being processed with a symlink he can cause that symlink to be followed in subsequent filesystem operations.
A proof-of-concept code demonstrating the vulnerability is attached.
std::fs::remove_dir_all()
in a privileged process or any other security-senstitive context.std::fs::remove_dir_all()
is only used on directories not accessible to processes from other security contexts.If the attacker has write access to a directory which is being deleted by the privileged process using remove_dir_all()
he can trick the process to delete any sensitive files or directory subtrees that the privileged process can.
6.3 Medium
CVSS3
Attack Vector
LOCAL
Attack Complexity
HIGH
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
NONE
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H
3.3 Low
CVSS2
Access Vector
LOCAL
Access Complexity
MEDIUM
Authentication
NONE
Confidentiality Impact
NONE
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:L/AC:M/Au:N/C:N/I:P/A:P
0.0005 Low
EPSS
Percentile
14.8%