Research

Author Archive

Analysis of CVE-2009-0658 (Adobe Reader 0day)

Tuesday, March 10th, 2009

Bow here again. It has been a while since we posted a binary analysis on our blog, so I figured we would post one for a vuln that has been getting a lot of hoopla the past few weeks :)

Whenever there is a critical vulnerability in a product that is used frequently, we perform an internal binary analysis in order to get a complete picture of the vulnerability and write reliable countermeasures for it. In some cases, we post the analysis to our threat intelligence portal and in others they simply stay internal to SecureWorks. Enough of that, on to the fun part…

The vuln we will be talking about is CVE-2009-0658, which is a code execution vulnerability in Adobe Acrobat. There are a few PoCs hosted over at milw0rm for this vulnerability:

http://www.milw0rm.com/exploits/8099
http://www.milw0rm.com/exploits/8090

The interesting thing about these two PoCs is that they are both supposedly for the same vulnerability, however they crash in two different locations (which will be explained later). I started off debugging the vulnerability using the second exploit (8090). If you attach a debugger to Adobe Reader, you’ll see that the crash occurs here:


.text:009ADAD7 loc_9ADAD7:
.text:009ADAD7 mov ecx, [ebx+edi*4] ; breakpoint #1
.text:009ADADA mov eax, [ecx+1Ch]
.text:009ADADD mov edx, [esi+10h]
.text:009ADAE0 lea eax, [eax+eax*4]
.text:009ADAE3 lea eax, [edx+eax*4-14h]
.text:009ADAE7 mov edx, [eax+4]
.text:009ADAEA test edx, edx
.text:009ADAEC jz short loc_9A

.text:009ADAEE mov ebp, [eax+10h]
.text:009ADAF1 mov [edx+ebp*4], ecx ; Crash #2 occurs here
.text:009ADAF4 add dword ptr [eax+10


An access violation is triggered with a failed write to 0x41414141, which is obviously an attacker controlled value.

In order to go any further in the code, we must have an understanding of the JBIG2 file format and the stream within the PDF that we are dealing with. First, the documentation is available here:

http://www.jpeg.org/public/fcd14492.pdf

The irony being that you have to open it in a PDF reader ;) Now to look at the file. If you search the file for JBIG2 you'll find it in 4 places:


0000ad0: 792f 4669 6c74 6572 2f4a 4249 4732 4465 y/Filter/JBIG2De
00029e0: 7465 722f 4a42 4947 3244 6563 6f64 652f ter/JBIG2Decode/
0009750: 792f 4669 6c74 6572 2f4a 4249 4732 4465 y/Filter/JBIG2De
0010aa0: 4669 6c74 6572 2f4a 4249 4732 4465 636f Filter/JBIG2Deco


These are 4 separate JBIG2 streams within the PDF and any of them could result in the trigger of the vulnerability, however debugging reveals that the first stream triggers the vuln. The first 64 bits or so of the initial stream is here:


0000af0: 3e3e 7374 7265 616d 0d0a 0000 0001 4000 >>stream......@.
0000b00: 0033 3333 1300 000a 0000 000c e400 0001 .333............
0000b10: 2c00 0001 2c50 0000 0000 0001 0001 0100 ,...,P..........
0000b20: 0009 0400 0003 fffd ff02 fefe fe00 0000 ................


So now we need to determine what out of this stream causes the crash. I started this by trying to trace back the values used in the pointer calculation that ends up being 0x41414141 in the crash. Before continuing, it is worth noting that the calculation at 0x009ADAF1 results in a value of 0x41414141, but both registers contain the same value, 0x0D0D0D0D (which is 0x41414141/5), which is again obviously user controlled. Both of the registers used in the calculation, EBP and EDX, obtain their values shortly before the crash, so set a breakpoint just before the crash at 0x009ADAD7. The next few instructions are a fairly annoying sequence of arithmetic operations, so we'll walk through them step by step. Here is another annotated version of the disassembly:


.text:009ADAD7 loc_9ADAD7:
.text:009ADAD7 mov ecx, [ebx+edi*4] ; breakpoint #1, EDI = 0, EBX = pointer [1]
.text:009ADADA mov eax, [ecx+1Ch] ; EAX = 0x00333333 [2]
.text:009ADADD mov edx, [esi+10h]
.text:009ADAE0 lea eax, [eax+eax*4] ; EAX = 0x00FFFFFF [3]
.text:009ADAE3 lea eax, [edx+eax*4-14h] ; Final object calculation prior to crash [4]
.text:009ADAE7 mov edx, [eax+4] ;
.text:009ADAEA test edx, edx
.text:009ADAEC jz short loc_9A


It is important to note before moving on that both values in EBP and EDX during the crash are obtained by accessing a pointer to some object that is pointed to by EAX (which is calculated at [4]). We will need to focus first on what the value inside of EAX is and how we can control it.

The first calculation ([1]) is part of an array processing loop where EDI is the counter and EBX is the base pointer to the array. The array holds a series of objects, the loop is fairly small and just seems to be iterating through every available object and moving around some pointers. The second ([2]) instruction moves an integer into EAX, the value of which is 0×00333333 during exploitation, which should look familiar. If you look at the bytes from the file, 0×00333333 is very clearly at the start of the stream, we can verify this statically or by modifying that value in the file and debugging again. For the sake of time, I will just state that it is indeed from the file and is completely user controlled. If we move down, we see that value is used yet again in a calculation, which results in a value of 0×00FFFFFF ([3]) being placed into EAX. Shortly after this calculation, an array access is performed using our controlled value and the result is put into EAX, which is used later on as a pointer to some object [4]. This is where the vulnerability lies, we are able to read a value outside the array which is then used as a pointer for a memory write. It is worth noting that while debugging the exploit, the memory locations surrounding the array access are filled with 0×0D0D0D0D and it seems to be sprayed across a large portion of memory.

So to summarize all of this, we have an array of objects which are accessed via a user-controlled value and whose member variables are used to calculate an address to write to. This results in us being able to write anywhere in Adobe Reader’s address space by using some trickery. From here we need to see what the values used in the calculation represent in the file. We’ll start by looking at the stream documentations…

We know that we are dealing with the first few bytes in the JBIG2 stream and it is likely to be a stream header of some sorts. The JBIG2 documentation says that encoded JBIG2 streams are broken down into segments which are made up of two parts, the segment header and the segment data (see page 71). The format of the segment data is different depending upon the segment type, however the format for the segment header is the same across all different segment types. Knowing this, we can start looking at the segment header format. Here are the first few bytes of the stream again:


[00 00 00 01] [40] [00] [00 33 33 33] 13 00 00 0a ...
[Segment#] [FL] [RT] [SPAssociat] …


According to the documentation, the first DWORD in the stream is always the segment number (pg 72, 7.2.2). The next byte is a flags field ([FL]) which indicates the segment type (the lower 6 bits) and two possible characteristics of the stream, page association field size (bit 7) and deferred non-retain (bit 8). In the proof of concept, the 7th bit (page association field size) is the only bit in the flags that is set. The byte that follows the flag field is a varying length object called the referred-to segment count [RT], in this case, it is only a byte long and the value is zero, indicating that this segment does not refer to any other segments. If this value were non-zero, then the object could be larger and would be followed by another variable length object which contains the numbers of the segments that the current segment refers to. Since the referred-to segment count is zero in the PoC, we don’t need to worry about this too much. Finally, the next DWORD is the segment page association ([SPAssociat]), which will be a byte if the page association field bit is not set or a DWORD if it is. In this case, the bit is set and so this field is a DWORD. The segment page association is the value that is responsible for the access violation.

I like to always verify that whatever I’m reversing actually lives up to the specification, so we’ll take a look at some of the code that processes this header structure. Unfortunately, the Adobe code that processes JBIG2 streams is significantly more complicated than other code I’ve seen dealing with JBIG2, so the only thing I am going to deal with here is whether or not the page association bit needs to be set for exploitation. We know from the documentation this is supposed to be true and we could just fiddle with it in a hex editor, but where is the fun in that ;) So first things first, we need to find where our file is in memory and look for references to it (specifically the 5th byte).

The first routine we will be looking at is sub_9A8D50 (boo no symbols), where var_2C is a pointer to our stream buffer that contains the stream itself. The pointer in var_2C is obtained from the return value of another function, sub_30D220 and is the result of a call to a typical malloc type function (although there is some other code wrapped around it). This occurs during this virtual function call:


.text:009A8FE8 loc_9A8FE8:
.text:009A8FE8 mov ecx, [esi+0D0h]
.text:009A8FEE mov edx, [ecx]
.text:009A8FF0 push eax
.text:009A8FF1 mov eax, [edx+14h]
.text:009A8FF4 push 0
.text:009A8FF6 push ecx
.text:009A8FF7 call eax ; 0x0030D220
.text:009A8FF9 add esp, 0Ch
.text:009A8FFC test eax, eax
.text:009A8FFE mov [esp+44h+var_30], eax
.text:009A9002 jz short loc_9A8F

.text:009A9004 xor edi, edi
.text:009A9006 cmp [esi+78h], edi
.text:009A9009 mov [esp+44h+var_2C], eax ; stream buf pointer moved into var_2C
.text:009A900D mov [esp+44h+var_28], eax


The allocation looks like this:


.text:0030D26B mov ecx, [esp+0Ch+arg_4]
.text:0030D26F cmp eax, ecx
.text:0030D271 mov ebx, eax
.text:0030D273 jb short loc_30

.text:0030D277
.text:0030D277 loc_30D277:
.text:0030D277 push ecx ; allocation size - stream length
.text:0030D278 mov ecx, esi
.text:0030D27A call malloc_wrapper_sub2
.text:0030D27F test edi, edi
.text:0030D281 mov esi, eax


There are two important things to note here, first that the stream length is used for the allocation and second that the stream data is not copied over in sub_30D220, but another routine we’ll look at a little later. Once sub_30D220 returns, we see that the return value is stored into var_28, var_2C, and var_30, so we have three variables holding onto the stream buf pointer. We then run into another virtual function call (yay):


.text:009A9019 mov edx, [esi+78h] ; one iteration
.text:009A901C mov ecx, [ebp+10h]
.text:009A901F mov eax, [esp+44h+var_28]
.text:009A9023 mov ecx, [ecx+8]
.text:009A9026 push ebp
.text:009A9027 sub edx, edi
.text:009A9029 push edx
.text:009A902A push 1
.text:009A902C push eax ; stream buf ptr
.text:009A902D call ecx ; sub_317A80
.text:009A902F add [esp+54h+var_28], eax
.text:009A9033 add edi, eax
.text:009A9035 mov eax, [esp+54h+var_30]
.text:009A9039 add esp, 10h



This virtual function call ends up resolving to sub_317B30, which is a routine with a loop and a number of function calls inside of it. During the second iteration of the loop, the following is executed:


.text:00317BFE loc_317BFE: ;
.text:00317BFE mov eax, [esi+0Ch] ; pointer to the stream buffer (with the data in it)
.text:00317C01 lea ecx, [edi+ebp]
.text:00317C04 cmp ecx, eax
.text:00317C06 jbe short loc_317C22

.text:00317C22
.text:00317C22 loc_317C22:
.text:00317C22 push ebp ; size_t (stream size)
.text:00317C23 push eax ; source pointer (obtained from ESI+0Ch)
.text:00317C24 push edi ; destination pointer (pointer allocated earlier)
.text:00317C25 call memcpy
.text:00317C2A add [esi+0Ch], ebp
.text:00317C2D add esp, 0Ch
.text:00317C30 sub [esi+8], ebp
.text:00317C33 jmp short loc_317


So now we know where the buffer we are dealing with is allocated and filled with the file contents, we can set breakpoints on the flags and see where they are read. The first trigger is here:


.text:009AD509 mov bl, [edx+4] ; Flags byte
.text:009AD50C and bl, 3Fh ; Obtain segment type
.text:009AD50F mov byte ptr [esp+30h+var_8], bl
.text:009AD513 mov edx, [esp+30h+var_8]


Which is simply attempting to obtain the segment type by ANDing the lower 5 bytes. Moving on, the next trigger of the breakpoint should occur here inside sub_9BB160:


.text:009BB176 movzx edx, byte ptr [ecx] ; segment flags, hw bp trigger #2
.text:009BB179 mov [eax+0Ch], dl ; segment flags copied
.text:009BB17C add ecx, 1
.text:009BB17F mov [eax], ecx
.text:009BB181 mov al, dl
.text:009BB183 mov cl, al
.text:009BB185 and cl, 3Fh
.text:009BB188 test al, 40h ; Page association size
.text:009BB18A setnbe dl ; is the 7th bit set? (set dl = 1)
.text:009BB18D mov [esi+4], cl
.text:009BB190 test al, 80h
.text:009BB192 movzx cx, dl ; store value of dl into cx
.text:009BB196 setnbe dl
.text:009BB199 movzx ax, dl
.text:009BB19D mov [esi+8], ax
.text:009BB1A1 mov eax, [esi+2Ch]
.text:009BB1A4 mov [esi+6], cx ; move value of cx (result from the page association test) into memory


You should see a couple of things here. First, the next hardware breakpoint will trigger where indicated above. You then see that the segment flags are copied to another location. Moving down a little ways, the lower byte of the EAX register is TESTed against 0×40. The setnbe instruction will set the destination operand (lower byte of EDX in this case) to 1 if both CF and ZF are equal to zero, or set it to 0 otherwise. Since test performs an AND and the 7th bit of the flags is set, the AND will result in ZF not being set, so DL will be set to 1. Moving down we see that the result of the previous instruction is tossed around and eventually stored back into an object in memory.

Moving a little further down the routine, you’ll see this:


.text:009BB388
.text:009BB388 loc_9BB388:
.text:009BB388 cmp word ptr [esi+6], 0 ; Was page association size field set?
.text:009BB38D jnz short loc_9BB3A3

.text:009BB38F mov eax, [esi+2Ch] ; if the page association size field is 0, this block is executed (only read one byte)
.text:009BB392 mov ecx, [eax]
.text:009BB394 mov dl, [ecx]
.text:009BB396 add ecx, 1
.text:009BB399 mov [eax+0Ch], dl
.text:009BB39C mov [eax], ecx
.text:009BB39E movzx eax, dl
.text:009BB3A1 jmp short loc_9BB3AD

.text:009BB3A3
.text:009BB3A3 loc_9BB3A3:
.text:009BB3A3 mov ecx, [esi+2Ch] ; if page association is set to 1
.text:009BB3A6 push 4
.text:009BB3A8 call sub_9BA6B0 ; read DWORD

.text:009BB3AD loc_9BB3AD:
.text:009BB3AD mov ecx, [esi+2Ch]
.text:009BB3B0 push 4
.text:009BB3B2 mov [esi+1Ch], eax ; store segment page association field in memory



This is testing the result of our previous test against 0×40 and making a function call if it was set. A quick analysis of sub_9BA6B0 indicates that it is looping through and reading the value of the segment page association field byte by byte and returning the full DWORD. The return value is then stored into memory at ESI+1Ch. This is the location where the vulnerable routine will read the page association field value from that results in the access violation. Now, moving back into sub_9AD380 we see that the value read and stored at ESI+1Ch is accessed and tested to see if it has a value:


.text:009AD887 loc_9AD887:
.text:009AD887 mov ecx, [edx]
.text:009AD889 mov eax, [ecx+1Ch] ; segment page association
.text:009AD88C test eax, eax
.text:009AD88E jz loc_9ADB40

.text:009AD894 mov ecx, [esi+10h]
.text:009AD897 lea eax, [eax+eax*4]
.text:009AD89A add dword ptr [ecx+eax*4-14h], 1 ; Crash #1 (8099)
.text:009AD89F lea eax, [ecx+eax*4-14h]
.text:009AD8A3 mov eax, [edx]
.text:009AD8A5 mov ecx, [eax+10h]
.text:009AD8A8 test ecx, ecx
.text:009AD8AA mov [esp+30h+var_4],



It is of interest to note that this is just above where the crash occurs with one of the other PoCs. If you are debugging, it is important to understand why one PoC triggers here, while the other one does not (8099). In 8099 the add instruction which triggers the crash will not be able to resolve to a valid memory address, resulting in the access violation, however in 8090, the fact we have a lot of memory allocated filled with 0D’s increases the likelihood that the address calculation will result in a valid memory address (which is a result of a Javascript heap spray). Moving down, we see that value is accessed once again prior to the final crash:


.text:009ADAD7 loc_9ADAD7:
.text:009ADAD7 mov ecx, [ebx+edi*4]
.text:009ADADA mov eax, [ecx+1Ch] ; segment page association
.text:009ADADD mov edx, [esi+10h]
.text:009ADAE0 lea eax, [eax+eax*4]
.text:009ADAE3 lea eax, [edx+eax*4-14h]
.text:009ADAE7 mov edx, [eax+4]
.text:009ADAEA test edx, edx
.text:009ADAEC jz short loc_9


So, looking back now, we can see where the page association field size is checked and where memory is written accordingly. In addition, we can also see where the value used in the array access is written into memory.

Several sources have indicated that Javascript is not necessary for this vulnerability to be exploited. There is no need for Javascript in order to trigger the vulnerability, however I’ve yet to see any exploits that are reasonably reliable with or without using Javascript. The sample that we debugged from “the wild” used Javascript for the heap spray and had a fairly small rate of success. There are potential ways of increasing the reliability of the exploit without using Javascript, however we have not captured anything of that nature yet in the wild.

This issue was patched by Adobe on March 10th, 2009. A link to the Adobe advisory is available here:

http://www.adobe.com/support/security/bulletins/apsb09-03.html

Share This Blog | SlashDot | del.ico.us | Technorati | Reddit | Digg it

SAMI Is My Hero: MS08-033 Disassembled

Tuesday, July 29th, 2008

My name is Bow Sineath and I have recently joined the SecureWorks Counter Threat Unit™ (CTU) as a security researcher. During my previous employment, I managed an IDS/IPS signature set and was responsible for acting on vulnerability intelligence that was, more often than not, very limited in public details. My experience in reverse engineering, source code analysis and countermeasure development is assisting SecureWorks in developing countermeasures that accurately protect our clients.

For my initial blog post as a CTU researcher, I am going to detail a vulnerability from Microsoft’s June 2008 patch cycle and correct an error I have seen in a number of the publicly available countermeasures. The vulnerability exists in the way that Microsoft DirectX 7 and 8 handle the parsing of Synchronized Accessible Media Interchange (SAMI) files. Due to the fact that Microsoft 7 and 8 are only distributed with Windows 2000, all other modern variants of Windows are not vulnerable to this specific issue, which limits the scope of this vulnerability significantly. The Microsoft advisory identifier for this vulnerability is MS08-033.

The SAMI file format is a captioning technology developed by Microsoft and detailed here (http://msdn.microsoft.com/en-us/library/ms971327.aspx). The format has a series of tags, similar to HTML, that describe and print out captions. The files can be identified by both the file contents which are plain-text and, in most cases, the .smi file extension. When SAMI is used, the SAMI file describing the captions exists as a separate entity from the media, which means there will be two files, the media itself and the SAMI file providing the captions. The file format itself is relatively similar to HTML in that it uses tags and markup similar to HTML and CSS. Further details on the file format are available from Microsoft.

The vulnerability we will be analyzing exists in the way that DirectX 7 and 8 prior to MS08-033 handle class declarations in SAMI files. The SAMI file format requires that two headers exist, the SAMIParam block and the Style block, both of which provide metadata for the SAMI file and the captions. Within the Style block, classes can be declared and defined that give each caption the ability to perform language-specifications. A class can have a name, a language and formatting information associated with it. When classes are declared, they are done so with a period (.) followed by a sequence of alphanumeric characters, which will be the name used to associate a caption with a class. Following the class name, an open bracket designates the start of the class definition. Within the definition lies the meat of the class, including a class name to be presented to the user and a language selection, which by definition should follow the ISO639-ISO3166 naming convention. In addition, the author can specify formatting options within the class definition. The final portion of the definition is a close bracket, designating the end of the definition. An example declaration and definition could look like this:

.F00  { Name:"Foo Class"; Lang: en-US; color: white; }

Once the two header blocks have been defined, the captions can be defined. A basic SAMI caption consists of two tags, the first being SYNC, which defines the time in milliseconds to display the caption and the second being P, which allows the author to specify a class and ID for formatting. A typical caption definition could look like this:

    <SYNC  Start=10>
    <P  Class=F00> Hello there

In addition to class definitions, a caption can be formatted within the style block by using either a Paragraph block or a Source block. Both of these formatting types are documented in the Microsoft SAMI specification and work somewhat similar to classes.

The vulnerability lies in the way that quartz.dll applies style information to captions and exists in the CSAMIRead::FillBuffer() function. Within the CSAMIRead::FillBuffer() function, there are multiple calls to functions such as lstrcpyn and wsprintf, which are functions that can easily be misused. The use of these functions, in combination with a failure to properly track the size of data copied into a buffer, results in a trivially exploitable stack overflow.

At the start of CSAMIRead::FillBuffer(), two stubs are called which essentially obtain values from a structure and return them. Both of the function calls are virtual, so the target of the calls is not immediately clear.

There are several ways the target of virtual function calls can be determined. In this case the quickest and easiest way to determine the target of the calls is by setting a breakpoint on CSAMIRead::FillBuffer() and running it in a debugger. This can be done by attaching a debugger to Windows media player and setting the debugger to break on library load, and setting breakpoints within quartz.dll after the library has been loaded. The two functions are CMediaSample::GetSize() and CMediaSample::GetPointer() and are both virtual member functions of the IMediaSample COM object. The block of code which makes these calls is below.

  .text:35584819                 mov     edi, [ebp+arg_0]
  .text:3558481C                 mov     esi, ecx
  .text:3558481E                 push    edi
  .text:3558481F                 mov     eax, [edi]
  .text:35584821                 call    dword ptr [eax+10h] ; CMediaSample::GetSize()
  .text:35584824                 mov     eax, [edi]
  .text:35584826                 lea     ecx, [ebp+var_8]
  .text:35584829                 push    ecx
  .text:3558482A                 push    edi
  .text:3558482B                 call    dword ptr [eax+0Ch] ;  CMediaSample::GetPointer()
  .text:3558482E                 test    eax, eax
  .text:35584830                 jge     short loc_35584842 ; if(GetPointer() != 0)  return error

It is important to note in this block of code that the return value of the call to CMediaSample::GetSize() is discarded, which makes calling this function useless since the only purpose it serves is to return the size of the buffer. Moving a few blocks down into the function, checks are performed to see if certain fields were initialized in the SAMI file. This block of code is below.

  .text:3558486A                 mov     ecx, [esi+494h]  ; Pointer to SOURCE= object
  .text:35584870                 test    ecx, ecx
  .text:35584872                 jz      short loc_3558487D
  .text:35584874                 mov     edi, ecx
  .text:35584876                 mov     ecx, offset Default ; ""
  .text:3558487B                 jmp     short loc_35584884
  .text:3558487D ;  ---------------------------------------------------------------------------
  .text:3558487D
  .text:3558487D  loc_3558487D:  ;  CODE XREF: CSAMIRead::FillBuffer(IMediaSample *,ulong,ulong *)+61j
  .text:3558487D                 mov     ecx, offset Default  ; ""
  .text:35584882                 mov     edi, ecx
  .text:35584884
  .text:35584884  loc_35584884:  ;  CODE XREF: CSAMIRead::FillBuffer(IMediaSample *,ulong,ulong *)+6Aj
  .text:35584884                 mov     eax, [eax+4]    ;
  .text:35584884                 ;  Pointer to class definition
  .text:35584887                 test    eax, eax
  .text:35584889                 mov     edx, eax
  .text:3558488B                 jnz     short loc_3558488F
  .text:3558488D                 mov     edx, ecx
  .text:3558488F
  .text:3558488F  loc_3558488F:  ;  CODE XREF: CSAMIRead::FillBuffer(IMediaSample *,ulong,ulong *)+7Aj
  .text:3558488F                 mov     eax, [esi+498h] ;
  .text:3558488F                 ; Pointer to Paragraph style
  .text:35584895                 test    eax, eax
  .text:35584897                 jz      short loc_3558489B
  .text:35584899                 mov     ecx, eax

The three pointers shown here are going to be pushed as arguments to a wsprintf() call, which means they must be given some value before being passed to wsprintf(). The block of code above checks the pointers to the locations of the respective objects in memory to see if they are NULL or not, if the pointer is NULL then a pointer to an empty string is copied into the register used for an argument to wsprintf(), otherwise the pointer to the object is passed into wsprintf(). This seems a bit complicated, but seeing the wsprintf() call will make it clear.

  .text:3558489B  loc_3558489B:  ; CODE XREF:  CSAMIRead::FillBuffer(IMediaSample *,ulong,ulong *)+86j
  .text:3558489B                 push    edi
  .text:3558489C                 push    edx
  .text:3558489D                 push    ecx
  .text:3558489E                 push    offset aPStyleHsHsHs ; "<P  STYLE=\"%hs %hs %hs\">"
  .text:355848A3                 push    [ebp+var_8]     ; LPSTR
  .text:355848A6                 call    ebx ; __imp__wsprintfA
  .text:355848A8                 mov     edi, eax
  .text:355848AA                 mov      eax, [esi+49Ch]
  .text:355848B0                 add     esp, 14h
  .text:355848B3                 xor     ecx, ecx
  .text:355848B5                 mov     eax, [eax+20h]
  .text:355848B8                 mov     [ebp+var_C], ecx
  .text:355848BB                 test    eax, eax
  .text:355848BD                 mov     [ebp+var_4], eax
  .text:355848C0                 jz      short loc_355848E7

According to the stdcall calling convention, arguments are pushed in reverse order onto the stack, so in the call above edi points to the source object of the caption, edx points to the class definition, and ecx points to the Paragraph style block. In effect, this block of code is replacing the variable names specified in the SAMI source, class and style objects inside of each caption. Due to the fact that the wsprintf() function does not provide any means of bounds checking, any of the three strings passed into this wsprintf() call can be used to trigger the vulnerability. The code that follows contains a series of lstrcpyn() and wsprintf() calls, all of which can be used to trigger the vulnerability as well.

Most countermeasures for this vulnerability do not provide complete protection against this vulnerability and only account for certain portions of class definitions, completely ignoring the Source and Paragraph blocks and missing the fact that there do not need to be valid identifiers within class definitions for the vulnerability to be triggered. This vulnerability is one of many that underscores the importance of reverse engineering patches and creating internal proof of concepts for vulnerabilities when creating countermeasures.

Share This Blog | SlashDot | del.ico.us | Technorati | Reddit | Digg it
SecureWorks Blogs
Other SecureWorks Blog Categories:
  • Events (1)
  • General (27)
  • Links (7)
  • Phishing (3)
  • Research (90)
  • Spam (1)
  • Trojans (5)
  • Blogs by Month:
  • March 2010
  • February 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • September 2008
  • August 2008
  • July 2008
  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • November 2007
  • October 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • March 2007
  • January 2007
  • December 2006
  • November 2006
  • October 2006
  • September 2006
  • August 2006
  • June 2006
  • May 2006
  • Next Steps

    Start With SecureWorks Request More Information Now
    Call SecureWorks Call Us Today
    877-905-6661

    Info Request




    Newsletter Signup

    * First Name:
    * Last Name:
    * Email Address:


    SecureWorks Authors
    SecureWorks Blog Topics