Date: Tue, 21 May 1996 20:54:18 -0700 (PDT) From: Tom Samplonius <tom@uniserve.com> To: "Chris J. Layne" <coredump@nervosa.com> Cc: freebsd-security@freebsd.org Subject: Re: [linux-security] Things NOT to put in root's crontab (fwd) Message-ID: <Pine.BSF.3.91.960521203607.17971A-100000@haven.uniserve.com> In-Reply-To: <Pine.BSF.3.91.960521160107.3734E-100000@onyx.nervosa.com>
next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, 21 May 1996, Chris J. Layne wrote: > I think this applies to our cleanup of /tmp in /etc/rc I think it doesn't. Our rm removes links, not files pointed to by links. So: cd /tmp ln -s /etc/passwd thing rm thing will remove the link, not /etc/passwd. We are not dependant on find to produce a "valid" list of files, so that rm does not remove something important. Tom > == Chris Layne ======================================== Nervosa Computing == > == coredump@nervosa.com ================ http://www.nervosa.com/~coredump == > > ---------- Forwarded message ---------- > Date: Tue, 21 May 1996 13:10:36 -0400 (EDT) > From: Zygo Blaxell <zblaxell@myrus.com> > To: linux-security@tarsier.cv.nrao.edu, best-of-security@suburbia.net > Subject: [linux-security] Things NOT to put in root's crontab > > Sigh. Here are several things I've just removed from /etc/crontab on > every RedHat Linux system I can get my hands on. They contain security > holes related to the use of 'find' and 'rm' to expire old files in /tmp > and other places. > > It seems that awareness of this type of security problem is rather low, > so I'll explain the class of problem and how to fix it. > > >From Redhat's /etc/crontab file: > ># Remove /var/tmp files not accessed in 10 days > >43 02 * * * root find /var/tmp/* -atime +3 -exec rm -f {} \; 2> /dev/null > > > ># Remove /tmp files not accessed in 10 days > ># I commented out this line because I tend to "store" stuff in /tmp > ># 41 02 * * * root find /tmp/* -atime +10 -exec rm -f {} \; 2> /dev/null > > > ># Remove formatted man pages not accessed in 10 days > >39 02 * * * root find /var/catman/cat?/* -atime +10 -exec rm -f {} \; 2> /dev/null > > > ># Remove and TeX fonts not used in 10 days > >35 02 * * * root find /var/lib/texmf/* -type f -atime +10 -exec rm -f {} \; 2> /dev/null > > Folks, do NOT use 'find' on a public directory with '-exec rm -f' as root. > Period. Ever. Delete it from your crontab *now* and finish reading the > rest of this message later. > > * PROBLEM DISCUSSION AND EXPLOITATION > > The immediate security problem is that 'rm' doesn't check that > components of the directory name are not symlinks. This means that you > can delete any file on the system; indeed, with a little work you can > delete *every* file on the system, provided that you can determine the > file names (though you might be limited to deleting files more than ten > days old). > > First, create the directories and file: > > /tmp/hacker-fest/some/arbitrary/set/of/path/names/etc/passwd > > where all but the last component is a directory. Be ready to > replace 'etc' with a symlink to '/etc', so that: > > /tmp/hacker-fest/some/arbitrary/set/of/path/names/etc -> /etc > > i.e. the path components of the file name will point to a file named > 'passwd' in a different directory. > > If the replacement operation occurs between when 'find' sets {} to > "/tmp/hacker...etc/passwd" and when 'rm' calls unlink on > "/tmp/hacker...etc/passwd", then rm will in fact delete '/etc/passwd', > and not a file in /tmp. Deleting other files is left as an exercise. > > The race condition is really easy to win. Create a directory with 400 > path components, like this: > > /tmp/hacker-fest/a/a/a/a/a/a/a.../a/a/a/etc/passwd (1) > > Then arrange for each of the 'a' components to be a symlink to a > directory somewhere near the bottom of a similar tree. For example, > > /tmp/hacker-fest/a > > could be a symlink to > > /tmp/hacker-fest/b/b/b/b/b/b/b/b/b/.../b/b/b/b/b/b/a > > which could be a symlink to > > /tmp/hacker-fest/c/c/c/c/c/c/.../c/c/c/c/c/c/c > > and so on. In fact, *each* path component can be a symlink up to about > 8 levels or so. Any operation such as stat(), open(), lstat(), etc. > on one of these pathnames will cause the kernel to follow each and every > symlink. The difference between lstat() and stat() in this case is that > lstat() will not follow the *last* symlink. > > This will make lstat() and friends *extremely* slow, on the order of > several *minutes* per lstat() operation, because each lstat() is now > reading in several thousand inodes and disk blocks. If you fill each > directory with several hundred entries, then create the entry you want, > then delete the others, you force the kernel to waste its time reading > kilobytes of empty directory blocks--in fact, you can make one stat() or > unlink() operation read almost the entire disk in an order designed to > maximize disk head motion if you know what you're doing. If you have an > NFS, CDROM, or floppy-disk filesystem handy, you can get *weeks* per > lstat(). > > Of course, 'find' will normally see the first symlink and stop. To > prevent this, you rename the original directory (at (1) above) and > create another directory with the same name and about 5000 empty files, > some of which have the same name as files you want to delete. Note that > these 5000 empty files can all be hard links to the same file, to save > precious inodes for more of those symlinks. > > 'find' will spend considerable time iterating through these 5000 files. > When it does (you'll be able to tell because the atime of the directory > changes as find reads it), put the directory with the millions of > symlinks at (1) back with a couple of rename operations. Some versions > of 'find' will not be adversely impacted by this, but 'rm' definitely > will. > > It is usually sufficient to simply create the 400-component-long > directory, put 5000 files in it, wait for the atime of the directory to > change, then do the rename so that 'rm' follows a symlink. I used this > technique to remove /etc/crontab as a test case. > > If you have: > > /tmp/hacker-fest/a/a/a/a/a/.../a/etc/passwd (and 5000+ other files) > /tmp/hacker-fest/a/a/a/a/a/.../a/usr > > where 'usr' is a symlink to '/usr', you can get some implementations of > find to start recursing through /usr as well. > > * OTHER PROBLEMS WITH THIS CRONTAB > > A user can set the atime of any file they own to an arbitrary value, and > that programs like zip, tar, and cpio will do this for you > automatically; this makes 'atime' an almost useless indicator of when a > file was last used ('mtime' has the same problem). Either the file will > be deleted too early, because it was extracted from an archive using a > program that preserves timestamps, or users can set the atime to well > into the future and use /tmp space indefinitely. The later of ctime (to > detect writes) and atime (to detect reads; must check that atime is not > in the future) is a good indicator of when a file was last used. > > Miscellaneous bugs: the use of '*' means that files in a directory > named '.foo' will never be cleaned (and you can prevent 'find' from > working at all by putting more than 1020 files in /tmp). There are > subdirectories of /var/catman that aren't properly handled by the 'find' > command given (local and X11). You can't delete a directory with > 'rm -f'. > > In other words, not only is RedHat's /etc/crontab a major security hole, > it doesn't actually work properly, either. :( > > * FIXES > > The easiest way to fix this is to get rid of the find/rm stuff > completely. If you need a garbage collector, try our LRU garbage > collection daemon at the URL given below. > > Adding a system call that sets a flag that prevents a process from being > able to ever follow a symlink would be non-portable, but efficient and > effective. > > The next easiest way to fix this is to replace 'rm' with a program that > does not follow symlinks. It must check that each filename component in > turn by doing an lstat() of the directory, chdir() into the directory, > and further lstat()s to check that the device/inode number of '.' is > the same as the directory's device/inode number before chdir(). The > parameter of the 'unlink' or 'rmdir' system call must not contain a > slash; if it does, then the directory name before the slash can be > replaced by a symlink to a different directory between verification of > path components and the actual unlink() call. > > Another way to fix this is with a smarter version of find. A smart > find does the chdir() and lstat() checks to make sure that it never > crosses a symlink, and calls the program in 'exec' using a filename > with no directory components, relative to the current directory. > Thus, to delete: > > /tmp/hacker-fest/a/a/a/a/a/.../etc/passwd > > find first carefully (checking for attempts to exploit race conditions > before and *after* each chdir()) chdir()s into > > /tmp/hacker-fest/a/a/a/a/a/.../etc > > and will fail if any of the components is a symlink, plugging the hole > described above. After verifying that the '.../etc' is really a > subdirectory of /tmp, and not some random point on the filesystem, find > exec's the command: > > rm -f ./passwd > > which is secure as long as '.' isn't in your PATH. Note the leading > './' to prevent rm from interpreting the filename as a parameter. > > Note: this is in *addition* to the checks that find already makes to > determine whether a file is a symlink *before* chdir()ing into it. It must > make sure that components of the path that have *already* been tested > are not replaced with symlinks or renamed directories *after* find has > started processing subdirectories of them. > > Note that the 'smart' find without the post-chdir symlink tests won't > work. While smart-find is processing: > > /tmp/hacker-fest/a/a/a/a/* > > you can rename > > /tmp/hacker-fest/a/a/a/a > > to > > /tmp/hacker-fest/a/a/b (note: one less pathname component) > > and eventually smart-find will 'cd ..', but since the current directory > of find has moved, '..' will move as well, and eventually smart-find > will be one level too high and can start descending into other > subdirectories of '/'. To help this along you may need to create: > > /tmp/hacker-fest/usr > /tmp/hacker-fest/var > etc. > > * SAFE LRU GARBAGE COLLECTION > > Our LRU /tmp garbage collector daemon is available at > <URL:http://www.ultratech.net/~zblaxell/admin_utils/filereaper.txt>. It > is implemented in perl5. It depends on a Linux-specific 'statfs()' > system call to monitor available free space, so non-Linux people will > need to do a port (send me patches and I'll incorporate them). > > Our garbage collector: > handles the above security problems correctly, > handles pathnames more than 1024 characters, > uses smarter last-access estimates than just atime or ctime, > can support "permanent" subdirectories, > handles files, symlinks, directories, devices, mount points correctly, > can support minimum age of files (e.g. no files < 1 day old), > deletes oldest files first, > deletes files only when disk space is low, > and responds in less than ten seconds to low disk space conditions. > > Our garbage collector works on any directory where files can gracefully > disappear at arbitrary times, such as /var/catman, /tmp, /var/tmp, > TeX font directories, and our HTTP proxy cache. One directory where > the garbage collector doesn't work very well is /var/spool/news; we > had to hack things up a bit to fix the article databases when article > files disappear. > > -- > Zygo Blaxell. Former Unix/soft/hardware guru, U of Waterloo Computer Science > Club. Current sysadmin for Myrus Design, Inc. 10th place, ACM Intl Collegiate > Programming Contest Finals, 1994. Administer Linux nets for food, clothing, > and anime. "I gave up $1000 to avoid working on windoze... *sigh*" - Amy Fong > >
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.3.91.960521203607.17971A-100000>