Thank you, Mario, but our printSeps() is in another castle!

Fri 26 November 2010 by julien

This post details the way Adobe patched the printSeps() vulnerability in Adobe Reader (CVE-2010-4091). You'll see that the way Adode fixed the vulnerability is quite surprising...

Very lately a vulnerability in the undocumented JavaScript method printSeps() of Adobe Reader was disclosed (CVE-2010-4091). A few days later Adobe released a patch to fix it. Motivated by curiosity I decided to investigate the matter and find out how the vulnerability had been patched. I was really surprised by the way it was done and so I decided to share my findings, and therefore I wrote this post.

First of all, one has to understand how Adobe Reader’s JavaScript engine works. Interesting presentations have already been performed on this subject especially this one:http://www.immunitysec.com/download..., that I suggest interested people should read.

Adobe Reader's JavaScripts's engine is implemented in various files in the application's “Reader\\plug_ins” directory. Those plug-ins have an “*.api” extension, but are nothing more than regular dlls. During the application's initialization, the JavaScript classes register each property and method with a corresponding native handler as we can see below:

push    offset printSepsWithParamsHandler ; offset of native handler
push    offset aPrintsepswithp ; "printSepsWithParams" : offset of method name
push    esi             ; int
call    RegisterMethod  ; function responsible for method registration
push    offset scrollHandler ; int
push    offset aScroll  ; "scroll"
push    esi             ; int
call    RegisterMethod
push    offset mailDocHandler ; int
push    offset aMaildoc ; "mailDoc"
push    esi             ; int
call    RegisterMethod

Each plug-in file has a dispatcher responsible for calling the JavaScript native handlers with the given arguments, once it has checked that the method exists.The EScript.api, where the printSeps() native handler is implemented, has the following JavaScript dispatcher :

mov     [ebp+var_30], edi
jnz     short loc_2382EB7C
push    [ebp+FunctionName] ; gives the method name as argument
lea     ebx, [ebp+var_30]
mov     eax, edi
call    RetrieveHandlerByName ; function returns the native handler address if it is found
pop     ecx

...

mov     esi, [ebp+var_30]
push    edi
push    [ebp+FunctionName] ; gives the method name as argument
push    2
call    IsMethodCallable ; function returns a boolean to indicate if the user has suffiscient rights to call the method
add     esp, 10h
test    al, al

...

push    esi
push    [ebp+var_24]
push    [ebp+FunctionName]
push    [ebp+var_28]
call    [ebp+HandlerFunc] ; calls the native handler whose address has just been resolved

The Dispatcher firsts finds the native handler for the JavaScript method, then checks if the method is callable given the user’s security context, and finally calls the native handler.

I wanted to debug the printSeps() native handler from the patched version, but I realized that the JavaScript dispatcher was never called on the printSeps() method. Something was obviously wrong with the JavaScript itself, and something really funny. In fact Adobe had simply decided to remove this JavaScript method from the object rather than to patch its native handler vulnerability. It is removed by a simple test in the Doc object initialization:

call    sub_23825DDD
test    ax, ax
jnz     short loc_23802D71
push    offset sub_2383904D ; int
push    offset aPrintseps ; "printSeps"
push    esi             ; int
call    RegisterMethod
loc_23802D71:

The printSeps method is added to the Doc object or not depending on the result of the function at offset 0x23825DDD. This function is equivalent to the following C code:

if (strcmpw( ProductName , LReader ) == 0)
   return 1;
else
   return 0;

In other words, if the product is Adobe Reader then the printSeps() method is simply removed from the JavaScripts engine. The funny thing is that the native handler of printSeps() is still present and vulnerable.I think that Adobe's current solution may be temporary; since it looks more like a “quick and dirty” fix. And I would not be surprised if the printSeps() vulnerability was to reappear in the near future ;).