Wednesday, June 6, 2012

Adobe Illustrator Tx operator Remote Buffer Overflow - CVE-2012-0780

Product: Adobe Illustrator CS5 Version: 15.0.2
Binary affected: Illustrator.exe [98bce5a36f3d6a0b34507d5d9921b257]
CVSS v2 Base Score:10.0 (HIGH)
Impact Subscore: 10.0
Exploitability Subscore: 10.0
CVE: 2012-0780
BID:  53422


A stack based overflow on the graphic operator 'Tx'.
Adobe Illustrator is a vector graphics editor developed and marketed by Adobe Systems. The issue explained here affects Illustrator CS5 15.0.2 (CS5.5/CS5/CS4) for both Mac and Windows; other versions may also be affected. This corresponds to CVE-2012-0780,  BID-53422 and to apsb12-10

Historically Illustrator seems to base its primary file format in a well structured pseudo postscript file as outlined here. This format contains a series of graphic operators that describe the vector graphic, including lines, paths, raster images and text among others. Illustrator fails to check the boundaries of a buffer when parsing a string parameter of the text printing operator 'Tx'.  
For example consider the following postscript statement:

(The string) Tx

If more than 256 bytes are passed to the operator Tx there is a stack based overflow. "The string" can be encoded in hex or octal notation and is able to contain null characters.

The debugging example of the crashing .ai file is provided here. this hits the end of the stack writing the character "A".


In windows this occurs at the function 0x004A7200 (Note that Illustrator.exe is based at 0x400000 ). The local buffer is /GS-cookie protected and the stack usually* has ~0x3000 bytes from the beginning of the buffer until the very beginning of the stack. 

In very few words the overflowing function does this (under overflowing condition):
  1. reserves 256+ bytes in the stack for a buffer and other local variables
  2. write arbitrary amount of arbitrary data read from the file to the stack buffer
  3. translate every character using a provided map (e.g. \x90->\x00)
  4. due to some stack variable overwrite we control the destination address and size  where the function is ought to copy the translated string
  5. at pc=004A72C6 it copies our buffer to an arbitrary location (read from the overwritten stack) until the first null character

We 'need' to trigger an exception or otherwise control the execution flow before the GS cookie is checked. For this there are (at least) two possibilities: 
     a. At state (2) exhaust the stack memory and sigsegv at the beginning of  it or 
     b. at state (4) write to an invalid location. 

Let's analize the second option to generate an exception so less stack gets corrupted with the overflowed buffer. When reading the buffer, the stack has this look:

Note that in Win7 there weren't found any fixed no safeseh-protected memory maps already loaded so the only option left is to point the seh handler to a non module related memory map. 
  ```Also an interesting technique to bypass DEP most of the times could be to overwrite the security_cookie wich is always at 0xFB3380 with the arbitrary write primitive, *guess* the EBP and go for the direct ret address overwrite.```
We use the arbitrary write primitive pointed out earlier to put some controlled data at a known address and also to trigger an exception. To do so we write to the end of the a fixed writable map that doesn't belong to any module, say the memory at 0x10000 (This is also possible using a sprayed memory map (see below)). Basically we write a small bit of ASCII assembler to the end of the environment variables map and keep going until it the memory map end is reached, raising an exception. Then as we have overwriting the seh handler and we can not jump to any safeseh protected module we jump to the recently written memory. At which point it jumps back to the stack and executes the calcuatorrrrrr.
```No DEP on Windows7 and XPSP3 with default installation```

One .ai to rule them all

Just for the fun of it, let's investigate the possibility of making a one file to exploit all versions and operating systems combinations available. Illustrator runs in Windows and OSX always as a 32 bit only process.

Inspecting the crash in the OSX/Leopard version we have found that there is no canary or any compiler aided check for stack overflow, it is a simple return address overwrite. Also the offset from where the return address is taken in OSX is different from the offset from where the SEH handler pointer is taken in Windows. This makes it possible for both exploits to coexist; we can jump to different addresses in different platforms using the same file.

Also we spray both  the OSX and W32 shellcode several times on the memory. Both versions of the shellcode will be at the same time on the memory no matter in which platform is being exploited. For more info about how to spray an Illustrator process memory check our previous blog post.

Now it is only a matter of pointing the correct stack offset to the corresponding shellcode.

In Windows the overwritten seh handler points to the windows version of the shellcode and in OSX as there isn't any stack canary, the overwriten return address point to a different heap memory where the OSX shellcode is placed.

As the offset of the osx-ret and the windows-seh-handler are different and compatible both versions could coexist in a single exploit file.

The vulnerable function follows...

.text:004A7200 ; =============== S U B R O U T I N E =======================================
.text:004A7200 ; Attributes: bp-based frame
.text:004A7200 sub_4A7200      proc near               
.text:004A7200 var_11C         = dword ptr -11Ch
.text:004A7200 var_118         = dword ptr -118h
.text:004A7200 var_114         = byte ptr -114h
.text:004A7200 var_14          = dword ptr -14h
.text:004A7200 var_10          = dword ptr -10h
.text:004A7200 var_C           = dword ptr -0Ch
.text:004A7200 var_4           = dword ptr -4
.text:004A7200 arg_0           = dword ptr  8
.text:004A7200                 push    ebp
.text:004A7201                 mov     ebp, esp
.text:004A7203                 push    0FFFFFFFFh
.text:004A7205                 push    offset loc_C3B8C0
.text:004A720A                 mov     eax, large fs:0
.text:004A7210                 push    eax
.text:004A7211                 sub     esp, 110h                    ;Make room for a 256 bytes buffer, etc 
.text:004A7217                 mov     eax, dword_FB3380    
.text:004A721C                 xor     eax, ebp
.text:004A721E                 mov     [ebp+var_14], eax    ;Cookie! Immediately after the buffer
.text:004A7221                 push    ebx
.text:004A7222                 push    esi
.text:004A7223                 push    edi
.text:004A7224                 push    eax
.text:004A7225                 lea     eax, [ebp+var_C]
.text:004A7228                 mov     large fs:0, eax
.text:004A722E                 mov     [ebp+var_10], esp
.text:004A7231                 mov     ebx, [ebp+arg_0]
.text:004A7234                 mov     edi, ecx
.text:004A7236                 mov     ecx, ebx
.text:004A7238                 mov     [ebp+var_118], ebx
.text:004A723E                 call    std::basic_string::length(...) ;Original size offending size
                                                                                               ;(It doesn;t stop at null chars)
.text:004A7244                 mov     esi, eax
.text:004A7246                 push    esi
.text:004A7247                 mov     ecx, ebx
.text:004A7249                 call    std::basic_string::c_str(...)
.text:004A724F                 push    eax
.text:004A7250                 lea     eax, [ebp+var_114]
.text:004A7256                 push    eax
.text:004A7257                 call    memcpy                     ;STACK OVERFLOW! (If more than 256 bytes)
.text:004A725C                 lea     eax, [ebp+esi+var_114]
.text:004A7263                 add     esp, 0Ch
.text:004A7266                 mov     [ebp+var_11C], eax
.text:004A726C                 mov     byte ptr [eax], 0
.text:004A726F                 mov     [ebp+var_4], 0
.text:004A7276                 lea     esi, [ebp+var_114]
.text:004A727C                 lea     esp, [esp+0]
.text:004A7280 loc_4A7280:                             
.text:004A7280                 cmp     esi, eax
.text:004A7282                 jnb     short loc_4A72B6
.text:004A7284                 mov     edx, [edi]
.text:004A7286                 mov     eax, [edx+4]
.text:004A7289                 push    esi
.text:004A728A                 mov     ecx, edi
.text:004A728C                 call    eax                       ;Iterates over the stack copied buffer
                                                                                 ;applying a 'locale'? character translation
                                                                                 ;(Invalid chars noted in exploit)
.text:004A728E                 test    eax, eax
.text:004A7290                 jg      short loc_4A7297
.text:004A7292                 mov     eax, 1
.text:004A7297 loc_4A7297:                             
.text:004A7297                 add     esi, eax
.text:004A7299                 mov     eax, [ebp+var_11C]
.text:004A729F                 jmp     short loc_4A7280
.text:004A72AE ; ---------------------------------------------------------------------------
.text:004A72AE loc_4A72AE:                             
.text:004A72AE                 mov     ebx, [ebp+var_118]
.text:004A72B4                 jmp     short loc_4A72BD
.text:004A72B6 ; ---------------------------------------------------------------------------
.text:004A72B6 loc_4A72B6:                             
.text:004A72B6                 mov     [ebp+var_4], 0FFFFFFFFh
.text:004A72BD loc_4A72BD:                             
.text:004A72BD                 lea     ecx, [ebp+var_114]
.text:004A72C3                 push    ecx
.text:004A72C4                 mov     ecx, ebx
.text:004A72C6                 call    std::basic_string::operator=(...)   ;Here, due to local values 
                                                                                                             ;corruption it is possible to
                                                                                                             ;write a translated version of
                                                                                                             ;our buffer to anywhere 
.text:004A72CC                 mov     ecx, [ebp+var_C]
.text:004A72CF                 mov     large fs:0, ecx
.text:004A72D6                 pop     ecx
.text:004A72D7                 pop     edi
.text:004A72D8                 pop     esi
.text:004A72D9                 pop     ebx
.text:004A72DA                 mov     ecx, [ebp+var_14]
.text:004A72DD                 xor     ecx, ebp
.text:004A72DF                 call    sub_C27512                           ;Check the cookie
.text:004A72E4                 mov     esp, ebp
.text:004A72E6                 pop     ebp
.text:004A72E7                 retn    4
.text:004A72E7 sub_4A7200      endp

No comments:

Post a Comment