Symlinks and Cryogenic Sleep

From: Olaf Kirch (okirat_private)
Date: Mon Jan 03 2000 - 12:24:43 PST

  • Next message: AVsearch: "FW: Patch issued for AltaVista Search Engine Directory"

    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
    okirat_private  |    / | \   sol.dhoop.naytheet.ah kin.ir.samse.qurax
    okirat_private    +-------------------- Why Not?! -----------------------
             UNIX, n.: Spanish manufacturer of fire extinguishers.
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 15:25:40 PDT