home *** CD-ROM | disk | FTP | other *** search
/ The Hacker's Encyclopedia 1998 / hackers_encyclopedia.iso / hacking / unix / crontabn.txt / text0000.txt < prev   
Encoding:
Text File  |  2003-06-11  |  10.1 KB  |  240 lines

  1. Sigh.  Here are several things I've just removed from /etc/crontab on
  2. every RedHat Linux system I can get my hands on.  They contain security
  3. holes related to the use of 'find' and 'rm' to expire old files in /tmp
  4. and other places.
  5.  
  6. It seems that awareness of this type of security problem is rather low,
  7. so I'll explain the class of problem and how to fix it.
  8.  
  9. >From Redhat's /etc/crontab file:
  10. ># Remove /var/tmp files not accessed in 10 days
  11. >43 02 * * * root find /var/tmp/* -atime +3 -exec rm -f {} \; 2> /dev/null
  12. >
  13. ># Remove /tmp files not accessed in 10 days
  14. ># I commented out this line because I tend to "store" stuff in /tmp
  15. ># 41 02 * * * root find /tmp/* -atime +10 -exec rm -f {} \; 2> /dev/null
  16. >
  17. ># Remove formatted man pages not accessed in 10 days
  18. >39 02 * * * root find /var/catman/cat?/* -atime +10 -exec rm -f {} \; 2> /dev/null
  19. >
  20. ># Remove and TeX fonts not used in 10 days
  21. >35 02 * * * root find /var/lib/texmf/* -type f -atime +10 -exec rm -f {} \; 2> /dev/null
  22.  
  23. Folks, do NOT use 'find' on a public directory with '-exec rm -f' as root.
  24. Period.  Ever.  Delete it from your crontab *now* and finish reading the
  25. rest of this message later.
  26.  
  27. * PROBLEM DISCUSSION AND EXPLOITATION
  28.  
  29. The immediate security problem is that 'rm' doesn't check that
  30. components of the directory name are not symlinks.  This means that you
  31. can delete any file on the system; indeed, with a little work you can
  32. delete *every* file on the system, provided that you can determine the
  33. file names (though you might be limited to deleting files more than ten
  34. days old).
  35.  
  36. First, create the directories and file:
  37.  
  38.     /tmp/hacker-fest/some/arbitrary/set/of/path/names/etc/passwd
  39.  
  40. where all but the last component is a directory.  Be ready to 
  41. replace 'etc' with a symlink to '/etc', so that:
  42.     
  43.     /tmp/hacker-fest/some/arbitrary/set/of/path/names/etc -> /etc
  44.  
  45. i.e. the path components of the file name will point to a file named
  46. 'passwd' in a different directory.
  47.  
  48. If the replacement operation occurs between when 'find' sets {} to
  49. "/tmp/hacker...etc/passwd" and when 'rm' calls unlink on
  50. "/tmp/hacker...etc/passwd", then rm will in fact delete '/etc/passwd',
  51. and not a file in /tmp.  Deleting other files is left as an exercise.
  52.  
  53. The race condition is really easy to win.  Create a directory with 400
  54. path components, like this:
  55.  
  56.     /tmp/hacker-fest/a/a/a/a/a/a/a.../a/a/a/etc/passwd    (1)
  57.  
  58. Then arrange for each of the 'a' components to be a symlink to a
  59. directory somewhere near the bottom of a similar tree.  For example,
  60.  
  61.     /tmp/hacker-fest/a
  62.  
  63. could be a symlink to
  64.  
  65.     /tmp/hacker-fest/b/b/b/b/b/b/b/b/b/.../b/b/b/b/b/b/a
  66.  
  67. which could be a symlink to
  68.  
  69.     /tmp/hacker-fest/c/c/c/c/c/c/.../c/c/c/c/c/c/c
  70.  
  71. and so on.  In fact, *each* path component can be a symlink up to about
  72. 8 levels or so.  Any operation such as stat(), open(), lstat(), etc.
  73. on one of these pathnames will cause the kernel to follow each and every
  74. symlink.  The difference between lstat() and stat() in this case is that
  75. lstat() will not follow the *last* symlink.
  76.  
  77. This will make lstat() and friends *extremely* slow, on the order of
  78. several *minutes* per lstat() operation, because each lstat() is now
  79. reading in several thousand inodes and disk blocks.  If you fill each
  80. directory with several hundred entries, then create the entry you want,
  81. then delete the others, you force the kernel to waste its time reading
  82. kilobytes of empty directory blocks--in fact, you can make one stat() or
  83. unlink() operation read almost the entire disk in an order designed to
  84. maximize disk head motion if you know what you're doing.  If you have an
  85. NFS, CDROM, or floppy-disk filesystem handy, you can get *weeks* per
  86. lstat().  
  87.  
  88. Of course, 'find' will normally see the first symlink and stop.  To
  89. prevent this, you rename the original directory (at (1) above) and
  90. create another directory with the same name and about 5000 empty files,
  91. some of which have the same name as files you want to delete.  Note that
  92. these 5000 empty files can all be hard links to the same file, to save
  93. precious inodes for more of those symlinks.
  94.  
  95. 'find' will spend considerable time iterating through these 5000 files.
  96. When it does (you'll be able to tell because the atime of the directory
  97. changes as find reads it), put the directory with the millions of
  98. symlinks at (1) back with a couple of rename operations.  Some versions
  99. of 'find' will not be adversely impacted by this, but 'rm' definitely
  100. will.
  101.  
  102. It is usually sufficient to simply create the 400-component-long
  103. directory, put 5000 files in it, wait for the atime of the directory to
  104. change, then do the rename so that 'rm' follows a symlink.  I used this
  105. technique to remove /etc/crontab as a test case.  
  106.  
  107. If you have:
  108.  
  109.     /tmp/hacker-fest/a/a/a/a/a/.../a/etc/passwd (and 5000+ other files)
  110.     /tmp/hacker-fest/a/a/a/a/a/.../a/usr
  111.  
  112. where 'usr' is a symlink to '/usr', you can get some implementations of
  113. find to start recursing through /usr as well.
  114.  
  115. * OTHER PROBLEMS WITH THIS CRONTAB
  116.  
  117. A user can set the atime of any file they own to an arbitrary value, and
  118. that programs like zip, tar, and cpio will do this for you
  119. automatically; this makes 'atime' an almost useless indicator of when a
  120. file was last used ('mtime' has the same problem).  Either the file will
  121. be deleted too early, because it was extracted from an archive using a
  122. program that preserves timestamps, or users can set the atime to well
  123. into the future and use /tmp space indefinitely.  The later of ctime (to
  124. detect writes) and atime (to detect reads; must check that atime is not
  125. in the future) is a good indicator of when a file was last used.
  126.  
  127. Miscellaneous bugs:  the use of '*' means that files in a directory
  128. named '.foo' will never be cleaned (and you can prevent 'find' from
  129. working at all by putting more than 1020 files in /tmp).  There are
  130. subdirectories of /var/catman that aren't properly handled by the 'find'
  131. command given (local and X11).  You can't delete a directory with
  132. 'rm -f'.
  133.  
  134. In other words, not only is RedHat's /etc/crontab a major security hole,
  135. it doesn't actually work properly, either.  :(
  136.  
  137. * FIXES
  138.  
  139. The easiest way to fix this is to get rid of the find/rm stuff
  140. completely.  If you need a garbage collector, try our LRU garbage
  141. collection daemon at the URL given below.
  142.  
  143. Adding a system call that sets a flag that prevents a process from being
  144. able to ever follow a symlink would be non-portable, but efficient and
  145. effective.
  146.  
  147. The next easiest way to fix this is to replace 'rm' with a program that
  148. does not follow symlinks.  It must check that each filename component in
  149. turn by doing an lstat() of the directory, chdir() into the directory,
  150. and further lstat()s to check that the device/inode number of '.' is
  151. the same as the directory's device/inode number before chdir().  The
  152. parameter of the 'unlink' or 'rmdir' system call must not contain a
  153. slash; if it does, then the directory name before the slash can be
  154. replaced by a symlink to a different directory between verification of
  155. path components and the actual unlink() call.
  156.  
  157. Another way to fix this is with a smarter version of find.  A smart
  158. find does the chdir() and lstat() checks to make sure that it never
  159. crosses a symlink, and calls the program in 'exec' using a filename
  160. with no directory components, relative to the current directory.  
  161. Thus, to delete:
  162.  
  163.     /tmp/hacker-fest/a/a/a/a/a/.../etc/passwd
  164.  
  165. find first carefully (checking for attempts to exploit race conditions
  166. before and *after* each chdir()) chdir()s into
  167.     
  168.     /tmp/hacker-fest/a/a/a/a/a/.../etc
  169.  
  170. and will fail if any of the components is a symlink, plugging the hole
  171. described above.  After verifying that the '.../etc' is really a
  172. subdirectory of /tmp, and not some random point on the filesystem, find
  173. exec's the command:
  174.  
  175.     rm -f ./passwd
  176.  
  177. which is secure as long as '.' isn't in your PATH.  Note the leading
  178. './' to prevent rm from interpreting the filename as a parameter.
  179.  
  180. Note: this is in *addition* to the checks that find already makes to
  181. determine whether a file is a symlink *before* chdir()ing into it.  It must
  182. make sure that components of the path that have *already* been tested
  183. are not replaced with symlinks or renamed directories *after* find has
  184. started processing subdirectories of them.
  185.  
  186. Note that the 'smart' find without the post-chdir symlink tests won't
  187. work.  While smart-find is processing:
  188.  
  189.     /tmp/hacker-fest/a/a/a/a/*
  190.  
  191. you can rename
  192.  
  193.     /tmp/hacker-fest/a/a/a/a
  194.  
  195. to
  196.  
  197.     /tmp/hacker-fest/a/a/b    (note: one less pathname component)
  198.  
  199. and eventually smart-find will 'cd ..', but since the current directory
  200. of find has moved, '..' will move as well, and eventually smart-find
  201. will be one level too high and can start descending into other
  202. subdirectories of '/'.  To help this along you may need to create:
  203.  
  204.     /tmp/hacker-fest/usr
  205.     /tmp/hacker-fest/var
  206.     etc.
  207.  
  208. * SAFE LRU GARBAGE COLLECTION
  209.  
  210. Our LRU /tmp garbage collector daemon is available at
  211. <URL:http://www.ultratech.net/~zblaxell/admin_utils/filereaper.txt>.  It
  212. is implemented in perl5.  It depends on a Linux-specific 'statfs()'
  213. system call to monitor available free space, so non-Linux people will
  214. need to do a port (send me patches and I'll incorporate them).
  215.  
  216. Our garbage collector:
  217.     handles the above security problems correctly,
  218.     handles pathnames more than 1024 characters, 
  219.     uses smarter last-access estimates than just atime or ctime,
  220.     can support "permanent" subdirectories,
  221.     handles files, symlinks, directories, devices, mount points correctly,
  222.     can support minimum age of files (e.g. no files < 1 day old),
  223.     deletes oldest files first,
  224.     deletes files only when disk space is low,
  225.     and responds in less than ten seconds to low disk space conditions.
  226.  
  227. Our garbage collector works on any directory where files can gracefully
  228. disappear at arbitrary times, such as /var/catman, /tmp, /var/tmp,
  229. TeX font directories, and our HTTP proxy cache.  One directory where
  230. the garbage collector doesn't work very well is /var/spool/news; we
  231. had to hack things up a bit to fix the article databases when article
  232. files disappear.
  233.  
  234. -- 
  235. Zygo Blaxell.  Former Unix/soft/hardware guru, U of Waterloo Computer Science 
  236. Club.  Current sysadmin for Myrus Design, Inc.  10th place, ACM Intl Collegiate
  237. Programming Contest Finals, 1994.  Administer Linux nets for food, clothing, 
  238. and anime.  "I gave up $1000 to avoid working on windoze... *sigh*" - Amy Fong
  239.  
  240.