-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Default cache directory is vulnerable to symlink attacks #2484
Comments
We talked about using the home directory when we implemented the cache feature, but we chose to not "pollute" that directory with temporary files. I find it difficult to see what a symlink attack could mean for RuboCop in terms of dangerous changes to files. RuboCop never writes to an existing file. It either reads from a cache file, named something like b67f67fa8e38ecf2503eff17dc2a44fd, or it creates it. So the worst thing that could happen is cache files being written to a place where they shouldn't be. So I'm not sure that we need to do anything. Please explain, if you see something that I don't. If we're going to do something, there are other ways. Since the cache files are personal -- they're stored under |
At best, there's a race condition between the checking of the temp file's existence and the writing of a new one. So if a malicious user can create a symlink for one of the cache names after the file existence is checked before write, the target file would be overwritten with the permissions of the running user. As far as I know this is pretty difficult to exploit and I don't think a separate user could influence the contents of the file except through social engineering, but is theoretically possible. I believe that opening the file with mode Worse, a malicious user can create a Marshal-formatted file in the appropriate location before the target user's rubocop cache directory is created, which will then get evaluated and can result in arbitrary code execution with the permissions of the executing user. The only surefire protection against this that I can think of would be to use JSON as a serialization format. Maybe there can also be a check for file/directory permissions before loading to make sure it's owned by the current user & is not writable by anyone else, but filesystem ACLs might make that annoying to check and I'm not sure if it covers all the cases. Both of these problems would be moot if the cache weren't in a predictable directory name under /tmp, or if the cache isn't turned on by default and there's a warning against turning it on for a shared system. That would also mean that future caching algorithm changes wouldn't matter so much from a security perspective. Both OS X and Linux distributions have relatively standard locations for exactly this type of thing so the home directory isn't polluted. For Linux, it's $XDG_CACHE_HOME or ~/.cache, as defined in http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html. For OS X, it seems to be ~/Library/Caches/. Unrelatedly, if the default temp directory stays under /tmp, I'd also suggest naming the default directory something like |
The default location for RuboCop's result cache is `/tmp`, which on the vast majority of systems is a world-writable directory. This means that a malicious user might be able to create a symlink to either redirect RuboCop's output to an unintended location, or cause RuboCop to read malicious input. Previous work[1,2] introduced protection against such symlink attacks by symlinks to be present in any cache locations. However, often CI setups explicitly rely on the ability to symlink cache locations, so that persistent results can be placed in shared storage, and symlinked to a predictable location on build machines[3]. This commit adds a new configuration option, `AllowSymlinksInCacheRootDirectory`, which lets a user permit symlinks if they are certain that their cache location is secure. [1] rubocop#2484 [2] rubocop#2516 [3] rubocop#3005
) The default location for RuboCop's result cache is `/tmp`, which on the vast majority of systems is a world-writable directory. This means that a malicious user might be able to create a symlink to either redirect RuboCop's output to an unintended location, or cause RuboCop to read malicious input. Previous work[1,2] introduced protection against such symlink attacks by symlinks to be present in any cache locations. However, often CI setups explicitly rely on the ability to symlink cache locations, so that persistent results can be placed in shared storage, and symlinked to a predictable location on build machines[3]. This commit adds a new configuration option, `AllowSymlinksInCacheRootDirectory`, which lets a user permit symlinks if they are certain that their cache location is secure. [1] #2484 [2] #2516 [3] #3005
…bocop#3199) The default location for RuboCop's result cache is `/tmp`, which on the vast majority of systems is a world-writable directory. This means that a malicious user might be able to create a symlink to either redirect RuboCop's output to an unintended location, or cause RuboCop to read malicious input. Previous work[1,2] introduced protection against such symlink attacks by symlinks to be present in any cache locations. However, often CI setups explicitly rely on the ability to symlink cache locations, so that persistent results can be placed in shared storage, and symlinked to a predictable location on build machines[3]. This commit adds a new configuration option, `AllowSymlinksInCacheRootDirectory`, which lets a user permit symlinks if they are certain that their cache location is secure. [1] rubocop#2484 [2] rubocop#2516 [3] rubocop#3005
The default temporary directory used by ResultCache is predictable and /tmp is world-writable by default, so using rubocop with caching on a system shared with other users is vulnerable to a symlink attack, as described here.
Because the cache needs to persist across program runs, I think the easiest option would be to default the cache directory to something like
~/.cache/rubocop
or the current directory, since they should not be world-writable by default.The text was updated successfully, but these errors were encountered: