Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 546552 (CVE-2015-1865) - <sys-apps/coreutils-8.13: "time of check to time of use" race condition fts.c
Summary: <sys-apps/coreutils-8.13: "time of check to time of use" race condition fts.c
Status: RESOLVED FIXED
Alias: CVE-2015-1865
Product: Gentoo Security
Classification: Unclassified
Component: Vulnerabilities (show other bugs)
Hardware: All Linux
: Normal minor (vote)
Assignee: Gentoo Security
URL: https://bugzilla.redhat.com/show_bug....
Whiteboard: A4 [noglsa]
Keywords:
Depends on:
Blocks:
 
Reported: 2015-04-14 10:58 UTC by Agostino Sarubbo
Modified: 2016-11-23 14:28 UTC (History)
1 user (show)

See Also:
Package list:
Runtime testing required: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Agostino Sarubbo gentoo-dev 2015-04-14 10:58:21 UTC
From ${URL} :

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:

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=286922

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#


@maintainer(s): after the bump, in case we need to stabilize the package, please let us know if it is ready for the stabilization or not.
Comment 1 Thomas Deutschmann (RETIRED) gentoo-dev 2016-11-22 18:01:29 UTC
This was fixed in v8.13 which hit Gentoo repository via https://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/sys-apps/coreutils/coreutils-8.13.ebuild?hideattic=0&view=log

Current stable version is =sys-apps/coreutils-8.23


@ Security: Please vote!