pytest-dev / pytest-dev/pytest

Vulnerable tmpdir handling (CVE-2025-71176)

Open
#13,669 46 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

plugin: tmpdir status: help wanted
Dominant language
Python
Stars
14.5k
Forks
3.4k
Avg merge
2d 9h
Merged PRs (30d)
35

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/_pytest/tmpdir.py around line 156 and review how the predictable pytest-of-user directory is created, checked, and used. Read Python's secure temporary-directory functions and consider the symlink and TOCTOU scenarios described in the issue. Done means temporary-directory setup no longer permits the reported attack while preserving pytest's existing behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
security, testing
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.