Bugtraq mailing list archives

Symlinks and Cryogenic Sleep


From: okir () MONAD SWB DE (Olaf Kirch)
Date: Mon, 3 Jan 2000 21:24:43 +0100


Hi all,

when you're dealing with files in /tmp that are supposed to be re-opened
(rather than opened once and then discarded) there's an established
way to do it which goes like this:

        if (lstat(fname, &stb1) >= 0 && S_ISREG(stb1.st_mode)) {
                fd = open(fname, O_RDWR);
                if (fd < 0 || fstat(fd, &stb2) < 0
                 || ino_or_dev_mismatch(&stb1, &stb2))
                        raise_big_stink()
        } else {
                /* do the O_EXCL thing */
        }

Accepted wisdom has it that this protects you from symlink attacks.

When trying to explain this to someone, I noticed that this is not quite
what it does. It protects your application against symlinks to files
that exist *before the call to lstat*.

This sounds like I'm nitpicking, and my first reaction also was to try
to find some convincing handwaving argument to dispel my concerns.

However, consider an average setuid root application, written by a
good-intentioned author like yours truly, using the above kind of code.
Assume you want to perform a symlink attack on this application, and
you've got lots of time on your hands. You create a regular file in
/tmp (the one your targetted application is going to look at). When
the application reaches the critical section of code between the
lstat and the open, you stop it by sending it a SIGSTOP. You record
the device and inode number of your /tmp file, remove it, and wait.

Seconds, days or maybe even weeks later, somebody creates an interesting
file with exactly the same inode (and device) number as the one you
used with my setuid program. You now create a symlink in /tmp, pointing
to that interesting file, and send my setuid application a SIGCONT.
Zap, there goes the file.

Sounds silly? Here are some reasons why this attack may not be that
esoteric at all:

 -      All symlink attacks can be improved by running them on an
        NFS mounted directory (easy for applications that heed
        the TMPDIR environment variable). In terms of file system
        race conditions, NFS acts as a kind of slo-mo glue.

 -      Just like you can improve your chances of racing a particular
        /tmp file access by running unlink/symlink in a tight loop,
        you can `step' through an application by sending it SIGSTOP/SIGCONT
        in a tight loop.

 -      It's not that hard to detect whether the targetted application is
        in the critical section of code. On Linux, it's very easy because
        /proc/$pid/stat will give you the instruction pointer. On other OSes
        it may be harder, but not impossible--for instance a lookup
        of /tmp/foo (as done by lstat()) will change the directory's
        atime.

 -      If you have no, or a very large quota, you can increase the
        likelihood of a certain inode number being reused by first claiming
        as many inodes as you get, and then free the one you want someone
        interesting to allocate.

 -      There are network services whose main job it is to create and
        remove interesting files--e.g. the NIS yppasswd daemon will
        create a temporary file, copy most of /etc/shadow to it, update
        your password entry, and replace the original /etc/shadow with it.
        Repeat until it uses an inode number you like.

All of this doesn't make it a practical attack yet, but it surely
demonstrates that the supposedly secure code shown above is far from
secure.

Comments? Suggestions?

A happy new year to everyone,
Olaf

--
Olaf Kirch         |  --- o --- Nous sommes du soleil we love when we play
okir () monad swb de  |    / | \   sol.dhoop.naytheet.ah kin.ir.samse.qurax
okir () caldera de    +-------------------- Why Not?! -----------------------
         UNIX, n.: Spanish manufacturer of fire extinguishers.



Current thread: