-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
Starting around https://github.com/pytest-dev/pytest/blob/main/src/_pytest/tmpdir.py#L156, there are some heuristics to make sure that the tmpdir is safe:
temproot = Path(from_env or tempfile.gettempdir()).resolve()
user = get_user() or "unknown"
# use a sub-directory in the temproot to speed-up
# make_numbered_dir() call
rootdir = temproot.joinpath(f"pytest-of-{user}")
try:
rootdir.mkdir(mode=0o700, exist_ok=True)
except OSError:
# getuser() likely returned illegal characters for the platform, use unknown back off mechanism
rootdir = temproot.joinpath("pytest-of-unknown")
rootdir.mkdir(mode=0o700, exist_ok=True)
# Because we use exist_ok=True with a predictable name, make sure
# we are the owners, to prevent any funny business (on unix, where
# temproot is usually shared).
# Also, to keep things private, fixup any world-readable temp
# rootdir's permissions. Historically 0o755 was used, so we can't
# just error out on this, at least for a while.
uid = get_user_id()
if uid is not None:
rootdir_stat = rootdir.stat()
if rootdir_stat.st_uid != uid:
raise OSError(
f"The temporary directory {rootdir} is not owned by the current user. "
"Fix this and try again."
)
This is vulnerable to symlink attacks, and probably TOCTOU attacks (see e.g. https://owasp.org/www-community/vulnerabilities/Insecure_Temporary_File). The linux kernel has protections for some of these in the fs.protected_hardlinks
and fs.protected_symlinks
sysctls, but they violate POSIX and are not enabled by default (though most distros enable them). In any case, they are linux-only.
For a proof of concept, create a symlink as some other user from /tmp/pytest-of-youruser
to a directory you own (this will require fs.protected_symlinks
to be disabled). Run pytest as yourself, and it will start writing junk to the directory that the attacker chose. It will also change the permissions on the directory he chose.
Obviously whoever wrote this was aware of these issues, but maybe not of how hard they can be to fix completely. This can be made a little bit better by telling stat()
not to follow symlinks, but I would instead recommend getting a random base directory in a secure way (using the python tempdir functions) rather than using a predictable name.
There are just too many ways it can go wrong.