LWN.net Logo

Seunshare, /tmp directories, and the "sticky" bit

By Jake Edge
March 2, 2011

The /tmp directory has been an unceasing source of security problems going back decades; there are still regular reports of vulnerabilities from insecure usage of temporary files. Part of the problem is that /tmp (and /var/tmp) are shared resources that can be written to by any process, which allows attackers to use various race conditions (typically time-of-check-to-time-of-use (TOCTTOU) races) in insecurely written programs to elevate their privileges. It is a bit ironic, then, that a utility specifically geared toward running a program with a private /tmp directory (for application sandboxing) would run afoul of a somewhat different kind of temporary file vulnerability—one that was long-ago excised by the advent of "sticky" directories. But that is just what Tavis Ormandy found.

The basic problem is that insecure programs often open files in /tmp after checking to see whether the file exists. In the window between the time that the test is done and the time that the file is opened, a malicious program can swap in a file of its choosing (or, more likely, a symbolic or hard link to a file of its choosing). When that happens, the buggy program is operating on a file that it does not expect and that can cause all manner of mayhem. For normally privileged programs, that mayhem is largely restricted, but for setuid programs, it can lead to full system compromise.

Long ago, attackers could use the world-writable attribute of /tmp to delete files that were created by setuid programs. The attacker could then replace the file with a link, and when a privileged program re-opened the file—something that is, in general, a bad practice with temporary files—it would be opening a file of the attacker's choice. But, the advent of the "sticky" bit as applied to directory permissions closed that loophole by only allowing the file owner (or root) to delete a file in a sticky directory. Since that time, lots of code has been written with a sticky /tmp directory in mind.

As part of its efforts to use SELinux to provide application sandboxes, Red Hat created the seunshare utility. That utility will run a command with alternate /tmp and home directories, along with a given SELinux context. seunshare will "unshare" the default mount namespace (so that the command has its own view of the filesystem hierarchy), mount the specified directories over top of /tmp and the home directory, and instruct the kernel to execute the command in the (optionally) given SELinux context. Since the temporary directory specified is under the control of the user, it doesn't necessarily have the sticky bit set, which leads to the vulnerability.

In Ormandy's example, he uses ksu to show how the /etc/passwd file could be overwritten by running ksu under seunshare. There are likely other setuid programs that make the assumption that their temporary files are in sticky directories, and quite possibly some where the consequences could be more severe than just trashing the password file. So a mechanism that was meant to provide more security actually left a hole behind. Unfortunately, this is not an uncommon occurrence in the security realm.

This particular case also shows the value of disclosing security vulnerabilities. Ormandy reported the bug back in September and, though there was a flurry of discussion about it, that discussion died off in late November (at least in the bug report). Things didn't pick up again until Ormandy posted a request for an update, along with notice that he was ready to publish an advisory, on February 18. Hearing no complaint, he did so on February 23.

After that, the discussion picked up again, with solutions being proposed, though no fix is yet available for Fedora or RHEL. One has to wonder how long this potential local privilege escalation might have languished had Ormandy not released his advisory. As a temporary mitigation, Ormandy suggests removing the setuid bit from seunshare or restricting access to it. The solution that Dan Walsh has proposed removes the -t tmpdir argument to seunshare and instead mounts a tmpfs on /tmp (with the sticky bit set). Presumably that will be released in the near future.

There has been an attempt to harden the behavior of sticky directories to try to avoid some of the longstanding /tmp directory problems—though that would not have thwarted this particular vulnerability because it relies on the directory being sticky. There has been resistance to that effort because it is seen as something of an ugly hack to work around badly written code, so it has not made it into the mainline (though Ubuntu and other kernels do have that hardening). But temporary file vulnerabilities of various sorts still rear their head with depressing frequency. We will undoubtedly see others crop up in the future.


(Log in to post comments)

Checking for sticky bit

Posted Mar 3, 2011 10:36 UTC (Thu) by epa (subscriber, #39769) [Link]

I suppose if a program is written with the assumption that /tmp has the sticky bit set, then this assumption should be embedded in the code.

Simply checking the permissions on /tmp before you start is not good enough because it has all the usual race conditions. There would need to be some additional flags to open() to specify 'I expect the containing directory to have the following permissions'.

Checking for sticky bit

Posted Mar 3, 2011 14:28 UTC (Thu) by nix (subscriber, #2304) [Link]

Unfortunately to handle all cases you need to check more than that. You need to verify that *all containing directories* also either have those permissions or are not writable by the euid, or the attacker can just rename the whole subtree out from under you and create a new one that doesn't have the sticky bit set. (This is rarely a problem with /tmp, but is the reason why e.g. sshd requires that your home directory not be writable by group or other before it will look at your ~/.ssh directory.)

Checking for sticky bit

Posted Mar 3, 2011 15:08 UTC (Thu) by epa (subscriber, #39769) [Link]

You need to verify that *all containing directories* also either have those permissions or are not writable by the euid, or the attacker can just rename the whole subtree out from under you and create a new one that doesn't have the sticky bit set.
Perhaps the problem is the use of filenames in the API rather than descriptors. If you first open() the directory to get an fd for that directory, and then create a file relative to that directory, you wouldn't have to worry about renaming attacks. This is the reason why file descriptors exist rather than passing around filenames everywhere, but it hasn't been taken to its logical conclusion and applied everywhere.

(If there is a variant of open() or creat() that takes a directory as a file descriptor, please educate me.)

Checking for sticky bit

Posted Mar 3, 2011 15:15 UTC (Thu) by RobSeace (subscriber, #4435) [Link]

> (If there is a variant of open() or creat() that takes a directory as a file descriptor, please educate me.)

man 2 openat

Seunshare, /tmp directories, and the "sticky" bit

Posted Mar 4, 2011 4:31 UTC (Fri) by docwhat (subscriber, #40373) [Link]

I'm confused why when ksu was run, it didn't get it's own /tmp....

There was a mini-kernel os called vsta which worked this way. Every user saw the /tmp directory as their own. Changing the euid changed the contents of the /tmp directory.

Seunshare, /tmp directories, and the "sticky" bit

Posted Mar 8, 2011 6:58 UTC (Tue) by laptop006 (subscriber, #60779) [Link]

This can be done nicely in SELinux, or even without it (ab)using PAM. I don't know if it was widely publicised though.

Russell Coker did give a few presentations on this that may be online.

Copyright © 2011, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds