Monday, January 7, 2013

Uncover Adobe Reader Sandbox Exceptions

Since version 10 Adobe Reader has included a flavor of the Chrome sandbox. This technology is much better explained here, and in the 4 Adobe specific posts: part1, part2, part3 and part4. But in very few words it works dividing responsibilities in at least 2 processes; the broker and a target. The target process is a low integrity process that basically can't access any resources by itself. To access almost any operating system entity it must relay on the broker process. 
The target simply ask the broker via IPC to do certain system calls for him. The broker then checks if the request comply with a preset list of rules and eventually gives the result back to the target. The set of rules are configured at the beginning. In this post we'll inspect this list for the different Adobe Reader versions. We'll build a python script that programatically generates a process monitor filter file for all the different Reader versions.  Then We'll show how to further inspect sensitive interactions between the target and other higher integrity processes using the generated filters.

Sandbox policy exceptions1

The policy interface allows the broker to specify exceptions. An exception is a way to take a specific Windows API call issued in the target and proxy it over to the broker. The broker can inspect the parameters and re-issue the call as is, re-issue the call with different parameters, or simply deny the call. To specify exceptions there is a single call: AddRule. The following kind of rules for different Windows subsystems are supported at this time:
  • Files
  • Named pipes
  • Process creation
  • Registry
  • Synchronization objects
The exact form of the rules for each subsystem varies, but in general rules are triggered based on a string pattern. For example, a possible file rule is:

AddRule(SUBSYS_FILES, FILES_ALLOW_READONLY, L"c:\\temp\\app_log\\d*.dmp")

This rule specifies that access will be granted if a target wants to open a file, for read-only access as long as the file matches the pattern expression; for example c:\temp\app_log\domino.dmp is a file that satisfies the pattern. There are three policy types, and each one is created differently:
  • Hard-coded policies
  • Dynamic policies
  • Admin-configurable policies
Policies are used whenever an access request is made to open the following kernel objects: File, Registry, Named Pipe, Section, Mutant, and Event. It is also used for launching external helper processes such as the Adobe Reader Updater or the comments synchronizer.

Logging AddRule calls

In order to audit the actual list of exceptions we need to log every call to the AddRule function. First we need to search for the AddRule function in the Reader binary. It may be a little tricky to pinpoint its exact location. Its position varies with the different versions but the signature is maintained  We also know that in 10.x.x the AddRule function references the string "Check failed: policy_". Using this an other magic tricks we can detect the exact location of it. We did it for the following versions:
breakpoint_offsets = { "10.1.3": 0x21260,
                       "10.1.4": 0x21630,
                       "11.0.0": 0x20370 } 
The first two arguments of AddRule are integers selecting the subsystem and the permissions wanted. The third argument is the "path" to which the rule apply to. The path may contain wildcards: *, ?.

We aim for a programmatic solution, something that we can run stand alone and get the list of rules. Among a lot of options we have chosen to use the great WinAppDbg. Download it from here.

The winappdbg script

Let's code a small WinAppDbg script to log every AddRule call. First we need to run the main Adobe Reader Process. In this example we use Reader 11.0.0 in a 32 bit Windows 7, the executable path varies in for other combinations.

  1.     #Instantiate the debugger
  2.     debug = Debug(bKillOnExit=True, bHostileCode=True)
  3.     path = r"C:\Program Files\Adobe\Reader 11.0\Reader\AcroRd32.exe"
  4.     version = '11.0.0'
  5.     print "Adobe Reader X %s"%version
  6.     #Run the reader!
  7.     debug.execl(path)

Then, when in the debug loop, we add a breakpoint at the AddRule function when AdbeRd32 is loaded.

  1.     # Loop while is alive
  2.     while debug:
  3.         # Get the next debug event.
  4.         event = debug.wait()
  5.         # Dispatch the event and continue execution.
  6.         try:
  7.             debug.dispatch(event)
  8.             # add breakpoint when acrord32 gets loaded
  9.             if event.get_event_code() == 3:
  10.                 process = event.get_process()
  11.                 base_address = event.get_image_base()
  12.                 print "AcroRd32 Main module found at %08x"%base_address
  13.                 # Hint: Use the string "Check failed: policy_." to hunt
  14.                 # the function that adds a new policy
  15.                 breakpoint_address = base_address + 0x20370
  16.                 #setting breakpoint
  17.                 print "Setting breakpoint at %08x"%breakpoint_address
  18.                 debug.break_at(process.get_pid(), breakpoint_address, print_policy)
  19.         except Exception,e:
  20.             print "Exception in user code:",e
  21.         finally:
  22.             debug.cont(event)
  23.     # Stop the debugger.
  24.     debug.stop()

We basically instructed the debugger to call our print_policy python function every time the Reader calls  AddRule. In the breakpoint handler we read the AddRule parameters from the stack and print them to the stdout.

  1. def print_policy(event):
  2.     #get process, thread and stack pointer
  3.     process = event.get_process()
  4.     thread = event.get_thread()
  5.     stack = thread.get_sp()
  6.     #read the 3 arguments from the debugged memory
  7.     subsystem = process.read_pointer(stack+0x4)
  8.     semantic = process.read_pointer(stack+0x8)
  9.     value_p = process.read_pointer(stack+0xC)
  10.     value =, 2)
  11.     while value[-2:] != '\x00\x00':
  12.         value +=,2)
  13.     value = value.decode('utf-16')
  14.     print "Rule: %d, %d, %s"%(subsystem,semantic,value)

The complete script can be found here. It will run the installed Adobe Reader 11 logging all the rules to the screen (you'll need to close the process by hand when you are done).

This is how the dumped rules look like:
Wellcome. Using Winappdbg version Version 1.5 (beta 4)
Adobe Reader X 11.0.0
AcroRd32 Main module found at 009f0000
Setting breakpoint at 00a10370
Rule: 0, 16, *.exe
Rule: 0, 16, *.bat
Rule: 0, 16, *.cmd
Rule: 0, 16, *.com
Rule: 0, 16, *.dll
Rule: 0, 16, *.cpl
Rule: 0, 16, *.ocx
Rule: 0, 16, *.pif
Rule: 0, 16, *.scr
Rule: 0, 16, *.scf
Rule: 0, 1, C:\Program Files\*
Rule: 0, 1, C:\Program Files
Rule: 0, 1, C:\Windows\*
Rule: 0, 1, C:\Windows
Rule: 0, 1, C:\Program Files\Adobe\Reader 11.0\*
Rule: 0, 1, C:\Program Files\Adobe\Reader 11.0\
Rule: 0, 1, C:\Program Files\Adobe\Reader 11.0\Reader\*
Rule: 0, 1, C:\Program Files\Adobe\Reader 11.0\Reader
Rule: 0, 1, C:\Users\w7\AppData\Roaming\Adobe\Acrobat\Privileged\11.0\*
Rule: 0, 1, C:\Users\w7\AppData\Roaming\Adobe\Acrobat\Privileged\11.0
Rule: 0, 1, C:\Users\w7\AppData\Roaming\Microsoft\Crypto\RSA\*
Rule: 0, 0, C:\Users\w7\AppData\LocalLow\Adobe\Acrobat\11.0\*
Download the complete list from here

Understanding the codes

The first column of the dumped rules selects one of the 6 subsystems

enum SubSystem {
    SUBSYS_NAMED_PIPES,       // Creation of named pipes.
    SUBSYS_PROCESS,           // Creation of child processes.
    SUBSYS_REGISTRY,          // Creation and opening of registry keys.
    SUBSYS_SYNC,              // Creation of named sync objects.
    SUBSYS_HANDLES            // Duplication of handles to other processes.

The second column selects the semantics of the rule. Adobe seems to have changed or added some of the semantics ids but the most important ones should be listed here.

// Allowable semantics when a rule is matched.
enum Semantics {
    FILES_ALLOW_ANY,       // Allows open or create for any kind of access that
                           // the file system supports.
    FILES_ALLOW_READONLY,  // Allows open or create with read access only.
    FILES_ALLOW_QUERY,     // Allows access to query the attributes of a file.
    FILES_ALLOW_DIR_ANY,   // Allows open or create with directory semantics
                           // only.
    HANDLES_DUP_ANY,       // Allows duplicating handles opened with any
                           // access permissions.
    NAMEDPIPES_ALLOW_ANY,  // Allows creation of a named pipe.
    PROCESS_MIN_EXEC,      // Allows to create a process with minimal rights
                           // over the resulting process and thread handles.
                           // No other parameters besides the command line are
                           // passed to the child process.
    PROCESS_ALL_EXEC,      // Allows the creation of a process and return fill
                           // access on the returned handles.
                           // This flag can be used only when the main token of
                           // the sandboxed application is at least INTERACTIVE.
    EVENTS_ALLOW_ANY,      // Allows the creation of an event with full access.
    EVENTS_ALLOW_READONLY, // Allows opening an even with synchronize access.
    REG_ALLOW_READONLY,    // Allows readonly access to a registry key.
    REG_ALLOW_ANY          // Allows read and write access to a registry key.

Loading the rules on the process monitor

We want to analyze the interactions between the sandboxed low integrity process and everything else in the Sysinternals Process Monitor.
In particular, we are interested in the interactions that pose the greatest risk. These are for example when a higher integrity process reads data generated by the sandboxed process. We ignore events of low integrity process. 
Somehow we need to add the rules from The reader sandbox to the process Monitor. We start with rules like this one

Rule: 0, 0, C:\Users\w7\AppData\LocalLow\Adobe\Acrobat\11.0\*

This means that the sandboxed process can open or create any kind of access that the file system supports over all the files under the folder : C:\Users\w7\AppData\LocalLow\Adobe\Acrobat\11.0\.
So we will be interested in the event of any process reading files from that folder as it could contain data crafted by an attacker trapped in the sandbox. For the last rule we add a filter line to the procmon to show the events with the path starting with "C:\Users\w7\AppData\LocalLow\Adobe\Acrobat\11.0\".

For our convenience we can use the pyPMF library to programmatically construct a  pmf filter file for the procmon. We modify the basic script to directly generate a ready to use procmon pmf filter. Yo can download the complete version from here. It supports Adobe reader 10.1.3,10.1.4 and 11.0.0 in both 32 and 64 bit Windows platforms.The following video shows how to run it for an XI version.

  1. run python
  2. load policy.pmf into procmon
  3. review generated rules
  4. run Adobe Reader and inspect the displayed events


  1. Interesting article.. So to locate AddRule, I opened AcroRd32.exe binary with IDA Pro. and looked for "Check failed: policy_", But IDA could not find any entry :( Is there any other way we can locate AddRule() Function ??

  2. Maybe you should try it on Adobe Reader 10.1.4 first. And then try to match some of the AddRule function features (found in the old version) in a newer version. Though, it may have changed a lot. You may also check the Chromium code.
    Let us know how it goes! Thanks!