bug: prevent tmpsh temporary-directory symlink escape#93
Open
ALX99 wants to merge 2 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
.local/bin/tmpshcreates its working directory at the predictable path/tmp/$$.tmpshwithmkdir -p. A local process can pre-create that path as a symlink to another directory beforetmpshstarts.mkdir -paccepts a symlink to an existing directory, andcdthen follows it, so the interactive shell opens in the attacker-selected directory rather than a private temporary directory.Severity and impact
This is a local security and correctness bug. On a multi-user system, another user can predict or race the process ID and redirect
tmpshinto a directory they control. Even on a single-user system, a stale or deliberately created path can make the command operate in the wrong directory. Commands run in the supposedly disposable shell may therefore read, modify, or delete unrelated files.Evidence
Relevant implementation on
master:.local/bin/tmpshsetsdir=/tmp/$$.tmpsh.mkdir -p "$dir", which succeeds when that path is already a symlink to an existing directory.cd "$dir"follows the symlink and starts${SHELL:-/bin/sh}in the symlink target.The regression test deterministically preserves the wrapper process ID across
exec, pre-creates/tmp/$$.tmpshas a symlink to a directory containing a sentinel file, and uses a non-interactive helper shell to detect whether the sentinel is visible.On the original implementation, the regression test exits with status 1 after the helper starts in the symlink target. With the fix, the helper starts in a newly created
mktempdirectory and the test exits successfully.This is a real defect rather than a hypothetical path-handling concern because the unsafe path is predictable, the symlink is followed deterministically, and no race is required for the included reproduction.
Reproduce
Setup:
Create an attacker-selected directory and helper shell, then preserve the wrapper PID across
execso the symlink name exactly matches the path used bytmpsh:Actual result on
master: the helper starts in$sandbox/victim, seessentinel, and exits non-zero.Expected result:
tmpshcreates a private, unique temporary directory and never enters the pre-created symlink target.The committed regression test automates this procedure without adding a standalone repro-only artifact.
Root cause
The script derives a temporary-directory name directly from its process ID and uses
mkdir -prather than atomically creating a new directory. Process IDs are predictable, andmkdir -pdoes not reject an existing symlink to a directory.Fix
Replace
/tmp/$$.tmpshplusmkdir -pwith:mktemp -datomically creates a unique directory and fails rather than reusing an attacker-controlled path. RespectingTMPDIRalso uses the caller's configured temporary-file location while preserving/tmpas the fallback. The existing cleanup trap remains unchanged.Validation
Commands and results:
bash tests/tmpsh-secure-tempdir.test.shagainst the original implementation: failed with exit status 1, proving that the helper entered the pre-created symlink target.bash tests/tmpsh-secure-tempdir.test.shagainst the fixed implementation: passed; the helper did not enter the victim directory and the generated temporary directory was removed.shellcheck .local/bin/tmpsh tests/tmpsh-secure-tempdir.test.sh: not executed becauseshellcheckis not installed in the available validation environment.Regression test added:
tests/tmpsh-secure-tempdir.test.sh.Scope
This PR only changes how
tmpshcreates its temporary working directory and adds focused regression coverage. It does not alter shell selection, cleanup semantics, command tracing, unrelated scripts, dependencies, or repository architecture.