Lucene search

K
binamuseFeliam ([email protected])BINAMUSE:28E11A73F405856E315AEF651E2CAE96
HistoryJun 06, 2012 - 8:24 p.m.

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

2012-06-0620:24:00
blog.binamuse.com
642

EPSS

0.019

Percentile

88.7%

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

Description

> 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”.

Exploitation

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

.text:004A7200 ; Attributes: bp-based frame

.text:004A7200

.text:004A7200 sub_4A7200 proc near

.text:004A7200

.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

.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

.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

.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

.text:004A72AE loc_4A72AE:

.text:004A72AE mov ebx, [ebp+var_118]

.text:004A72B4 jmp short loc_4A72BD

.text:004A72B6 ; ---------------------------------------------------------------------------

.text:004A72B6

.text:004A72B6 loc_4A72B6:

.text:004A72B6 mov [ebp+var_4], 0FFFFFFFFh

.text:004A72BD

.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

.text:004A72E7

EPSS

0.019

Percentile

88.7%