Exploitation of race condition vulnerabilities

Race conditions have caused quite a few issues and privilege escalation attacks on the Android platform; many of them allowing malicious attackers to gain root privileges.

Essentially, race conditions are caused by the lack of enforced mutual exclusion when a process on a multithreaded (a platform where more than one process is allowed to run concurrently) system that uses preemptive process scheduling. Preemptive scheduling allows a task scheduler to interrupt a thread or running process preemptively, meaning without first waiting for the task to be ready for interruption. This enables race conditions because often developers don't enable applications to operate in a way that accommodates arbitrary and unpredictable interrupts from the process scheduler; as a result, processes that rely on access to potentially shared resources like files, environment variables, or data structures in shared memory are always "racing" to get first and exclusive access to these resources. Attackers abuse this situation by gaining access to these resources first and corrupting them in a way that enables either damage to the processes operation or allows them to maliciously influence the process's behavior. A simple example would be a program that checks if a user authenticating themselves is in a given file listing the valid usernames; should this process not accommodate the preemptive scheduler, it may only access the file after a malicious user has corrupted it by adding his/her username to the list, allowing them to be authenticated.

In this walkthrough, I will detail some basic race condition vulnerabilities and discuss other potential causes; I will also detail exploitation of a few of the most basic race condition vulnerabilities. The walkthrough ends with references and useful sources of information on past Android-based race condition vulnerabilities; most of them reported in the year of this writing.

Exploitation of race condition vulnerabilities depends on a few factors, namely an attacker must at least be able to:

  • Gain access to the resources a vulnerable process is racing for access to: Just having a process that doesn't enforce mutual exclusion for its external resources but leaves the attacker with no method of access to these same resources wouldn't harbor much potential for exploitation. If this wasn't true, every single nonmutual exclusive access a process makes would be exploitable. This includes every time a process dereferences a pointer in memory without checking out a semaphore or spin lock, which could happen billions of times!
  • Influence these resources maliciously: It wouldn't help much if a process doesn't exclusively access its resources in the context in which an attack cannot augment or maliciously modify the resources. For instance, if a process accesses shared memory or a file that an attacker only has read access to—unless of course this causes the vulnerable process to crash, given the semantic priority of the process; for example, an anti-virus program, IDS, or firewall.
  • Time of use / time of check window size (TOU/TOC): This is essentially the time difference, or more effectively the likelihood of scheduler interrupts, between the time an application checks for access to a resource and actually accesses the resource. The exploitability of a race condition depends heavily on this time difference because exploits will essentially race for access in this time frame in order to maliciously affect the resource.

Taking these conditions into account, let's look at some constructed examples of race condition vulnerabilities and how to exploit them on Android.

Getting ready

Before we start exploiting race conditions, we need to prepare an example. Here's how you do that:

  1. We're going to prepare to an embedded ARM Android platform—the Jelly Bean emulator in this example—that causes race condition vulnerability. The following code details the behavior of a vulnerable process:
    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    #define MAX_COMMANDSIZE 100
    int main(int argc,char *argv[],char **envp){
      char opt_buf[MAX_COMMANDSIZE];
      char *args[2];
      args[0] = opt_buf;
      args[1] = NULL;
      int opt_int;
      const char *command_filename = "/data/race-condition/commands.txt";
      FILE *command_file;
      printf("option: ");
      opt_int = atoi(gets(opt_buf));
      printf("[*] option %d selected...
    ",opt_int);
      if (access(command_filename,R_OK|F_OK) == 0){
        printf("[*] access okay...
    ");
        command_file = fopen(command_filename,"r");
        for (;opt_int>0;opt_int--){
          fscanf(command_file,"%s",opt_buf);
        }
        printf("[*] executing [%s]...
    ",opt_buf);
        fclose(command_file);
      }
      else{
        printf("[x] access not granted...
    ");
      }
      int ret = execve(args[0],&args,(char **)NULL);
      if (ret != NULL){
        perror("[x] execve");
      }
      return 0;
    }

    Compile this by following the same process as detailed in the Cross-compiling native executables recipe and deploy it to your Android device. Try deploying it to a partition or folder that's been mounted as executable and readable by any user on the Android system (to see how to do this, please refer to the Copying files off/into an AVD recipe in Chapter 1, Android Development Tools). Throughout this recipe, we use the partition mounted as /system, which was remounted with read and write permissions, as in other recipes. Please note this may cause the NDK to throw out a couple of warnings, but as long as everything compiles to an executable, you're good to go!

  2. You'll also need to put the commands.txt file in the directory mentioned in the code, namely /data/race-condition/command.txt. This requires making a race condition folder in the /data path. A good example of how to do this can be found in the Inspecting network traffic recipe in Chapter 4, Exploiting Applications, since we needed to create a similar setup for TCPdump.
  3. You will need to set the setuid permission for this executable on the Android device; you can do this by executing the following command after deploying it to the device:
    chmod 4711 /system/bin/race-condition
    

    This command also makes sure any user on the system has execute permissions. Please be aware that you will need root permissions to perform this command. We are simulating the effect of a setuid binary and how it can cause arbitrary code execution.

We have everything set up for exploitation; we can move onto detailing this exploitation now.

How to do it...

To exploit the vulnerable binary, you will need to do the following:

  1. Run the ADB shell into the Android device; if you're using an emulator or a rooted device, you should be able to use su to assume another application's access rights.

    Try accessing some root-owned folders and files that don't have execute, read, or write permission set for your user. Here I've chosen user 10170 as an example, and you should see the Permission denied messages being thrown around when you try to access the /cache/ directory:

    How to do it...
  2. Let's exploit the race-condition binary. We do this by augmenting the commands.txt file with another command, namely /system/bin/sh, which will open a shell for us. You can do this by executing the following command:
    echo "/system/bin/sh" >> /data/race-condition/commands.txt
    

    The /system/bin/sh command should now be the last entry in the commands.txt file, this means, if we hope to select it from the menu we need to choose option 5.

    How to do it...
  3. Execute race-condition on the Android device and supply 5 as an option. The vulnerable binary would then execute the sh command and give you root permissions.
  4. Test your root access by trying to change the directory to /cache. If you're running a Jelly Bean or later version of Android, you should not see any Permission denial messages, which means you've just escalated your privileges to root!
    How to do it...

The preceding example is designed to detail the most basic concepts in race conditions, namely when an application accesses a file that any other process can augment and uses it to perform actions as the root user. There are more intricate and subtle situations that cause race conditions, one that's been commonly exploited are those involving symbolic links. These vulnerabilities stem from an application's inability to discern a file from a symbolic link, which allows attacks to augment files via a crafted symbolic link or when a file reads a symbolic or hard link but is incapable of determining the authenticity of the link target, which means the link can be redirected maliciously. For more modern examples of race condition vulnerabilities, check out the links in the See also section.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.48.78