SecurityTracker.com
    Home    |    View Topics    |    Search    |    Contact Us    |   

SecurityTracker
Archives


 


Category:   OS (UNIX)  >   OpenBSD Virtual File System (VFS) Vendors:   OpenBSD
OpenBSD Virtual File System Permits Race Conditions That Allow Local Users to Crash the Server or Potentially Obtain Root-Level Privileges
SecurityTracker Alert ID:  1001677
SecurityTracker URL:  http://securitytracker.com/id/1001677
CVE Reference:   GENERIC-MAP-NOMATCH   (Links to External Site)
Date:  Jun 5 2001
Impact:   Denial of service via local system, Execution of arbitrary code via local system, Root access via local system


Description:   A vulnerability has been reported in OpenBSD's virtual file system (vfs) that allows local users to crash the host and potentially obtain root-level privileges on the host.

The vulnerabilities are reported in the kern_descrip.c and sys_pipe.c modules. However, the author of the report implies that there may be vulnerabilites in other kernel modules.

The kern_descrip.c module reportedly contains a race condition between the dup2() and close() functions where more than one thread might be sharing a common file descriptor table. The sys_pipe.c module reportedly contains a race condition between the pipe() and close() functions.

Impact:   A local user can cause the host to crash and can potentially execute arbitrary code on the host to gain root-level privileges.
Solution:   No solution was available at the time of this entry.
Vendor URL:  www.openbsd.org/ (Links to External Site)
Cause:   State error
Underlying OS:  UNIX (OpenBSD)

Message History:   None.


 Source Message Contents

Subject:  Locally exploitable races in OpenBSD VFS


[my apologies if it ends up submitted twice]
Let's start with the trivial: good old aliasing bugs.

Example 1:
dup2() vs. close(). Relevant file: kern/kern_descrip.c

sys_dup2(p, v, retval)
        struct proc *p;
        void *v;
        register_t *retval;
{
[snip]
        if ((u_int)old >= fdp->fd_nfiles || fdp->fd_ofiles[old] == NULL ||
            (u_int)new >= p->p_rlimit[RLIMIT_NOFILE].rlim_cur ||
            (u_int)new >= maxfiles)
                return (EBADF);
OK, we've checked (among other things) that old is indeed opened.
[snip]
        if (new >= fdp->fd_nfiles) {
We either expand a descriptor table
[snip]
        } else {
Or reuse existing descriptor, closing file if it's opened.
                (void) fdrelease(p, new);
Which is the blocking operation, BTW.
        }
        return (finishdup(fdp, old, new, retval));
}
[snip]
finishdup(fdp, old, new, retval)
        register struct filedesc *fdp;
        register int old, new;
        register_t *retval;
{
        register struct file *fp;

        fp = fdp->fd_ofiles[old];
Got the struct sile of the file we are trying to dup...
        if (fp->f_count == LONG_MAX-2)
... and dereference it. We had checked that it's non-NULL, right?

Wrong. Another thread might be sharing our descriptor table (man rfork).
IOW, fdp points to shared data structure. So we had done the equivalent of

	if (global_var) {
		blocking_call();
		if (global_var->f_count)
			...
	}

Sloppy? Yes, and way beyond that. We have a nice shiny race between
dup2(0,1); and close(0). And it's a wide one. Turning that into
full-blown exploit is left as an exercise for readers.

Example 2:
pipe() vs. close() (kern/sys_pipe.c)

sys_opipe(p, v, retval)
[snip]
        error = falloc(p, &rf, &fd);
        if (error)
                goto free2;
[snip]
        retval[0] = fd;

        error = falloc(p, &wf, &fd);
        if (error)
                goto free3;
[snip]
        return (0);
free3:
        ffree(rf);
        fdremove(fdp, retval[0]);
free2:
[snip]

	Think what happens if the second allocation fails.  It is a
blocking call. During that time another thread had a nice possibility
to call close(retval[0]); since that value is very easy to predict -
it's the first available file descriptor. close() would
	* remove pointer from fdp[retval[0]]
	* call ffree() on it.
Now, we come back and do _another_ ffree() on the poor beast. Welcome to
kernel panic...

	Code is equivalent to

	global_var = p = alloc_foo();
	blocking_call();
	release_foo(p);
	global_var = NULL;

It's not just sloppy. It's obviously broken - obviously for anyone with
half of clue.

I can easily provide more examples of the same crap and so can anyone
who would bother to RTFS the descriptor handling in kern/*. Apparently
that had never happened during the last 5 years or so.

I'm not talking about the bugs that would require anything nontrivial to
find and understand. Just follow the yello^Wpiles of sloppy C and nearly
every one will turn out to be exploitable. And no, it's not limited to
descriptor handling - same goes for sys_pipe.c, etc.

Theo had been informed about that crap. Couple of weeks ago. Finding and
fixing these bugs is a simple matter of grep. So far it hadn't been done.
I've proposed to help with that, but apparently it got no interest. <shrug> 
Very well, there are other piles of garbage in need of fixing and seeing
crappy code in obscure Linux drivers is less disturbing than that in core
kernel.

Frankly. my respect to Theo went way down. This code had never been read
through, let alone audited. And that's the core kernel. Moreover, the
same bugs had been fixed in FreeBSD half a year ago. In other words, just
keeping an eye on other *BSD trees would be enough to catch them. Same
for lurking on freebsd-hackers. Same for watching the Linux tree, where
an audit of relevant areas had been done nearly two years ago. Done and
discussed on linux-kernel. Sad...

#include <stdrants/people_dont_bother_to_RTFS.h>

-- 
"You're one of those condescending Unix computer users!"
"Here's a nickel, kid.  Get yourself a better computer" - Dilbert.


 
 


Go to the Top of This SecurityTracker Archive Page





Home   |    View Topics   |    Search   |    Contact Us

This web site uses cookies for web analytics. Learn More

Copyright 2021, SecurityGlobal.net LLC