The OpenNET Project

Symlinks and Cryogenic Sleep

<< Previous INDEX Search src Set bookmark Go to bookmark Next >>
Date: Mon, 3 Jan 2000 21:24:43 +0100
From: Olaf Kirch <okir@MONAD.SWB.DE>
Subject: Symlinks and Cryogenic Sleep

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))
	} 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

 -	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

Comments? Suggestions?

A happy new year to everyone,
Olaf Kirch         |  --- o --- Nous sommes du soleil we love when we play  |    / | \   sol.dhoop.naytheet.ah    +-------------------- Why Not?! -----------------------
         UNIX, n.: Spanish manufacturer of fire extinguishers.

<< Previous INDEX Search src Set bookmark Go to bookmark Next >>

Inferno Solutions
Hosting by

Закладки на сайте
Проследить за страницей
Created 1996-2021 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру