In memory of a zero-day – MS13-051

Hello all!

We have taken a look at one of the zero-day vulnerabilities that Microsoft fixed this summer and we wanted to give you a brief overview of the vulnerability, including some additional details that have not been published by other parties yet.

In June 2013, Microsoft released MS13-051 [1] to fix a vulnerability in Microsoft Office 2003 Service Pack 3 and Microsoft Office for Mac 2011. The vulnerability was reported by researchers from Google. Based on Microsoft's TechNet blog, it has been used for targeted attacks mostly in Indonesia and Malaysia [2]. Secunia covered this vulnerability in SA53747 [3]. Although the average vulnerability window of a zero-day exploit is about 10 months [4], then it is suggested that this vulnerability has been exploited since 2009 [5], and even discovered initially back in 2008.

1) Install Microsoft Office 2003. Install Service Pack 3 for this version of office.
(Note: This analysis is done without applying any further patches with MSO.dll version 11.0.8172.0).

2) Create a new document.

3) Go to "%CommonProgramFiles%microsoft sharedGRPHFLT" (e.g. "C:Program FilesCommon Filesmicrosoft sharedGRPHFLT").

4) Drag "MS.PNG" file (a 2KB file) into the document and save the document as a "*.doc" file.

5) Open the saved document in a hex editor.

6) Change the value at offset 0x1B36 from 0x00000017 to 0xFFFFFFFF.

Microsoft Office will crash when processing this crafted document.

PNG File Format:
PNG (Portable Network Graphics) is an extensible file format for the lossless, portable, well-compressed storage of raster images. A PNG file consists of a PNG signature followed by a series of chunks. Each chunk consists of four parts [6]:

A 4-byte unsigned integer giving the number of bytes in the chunk's data field.

Chunk Type
A 4-byte chunk type code. Type codes are restricted to consist of uppercase and lowercase ASCII letters.

Chunk Data
The data bytes appropriate to the chunk type, if any.

A 4-byte CRC (Cyclic Redundancy Check).

Among other chunks, a PNG file may contain iTXt, tEXt, or zTXt chunks to provide textual information. Each of the text chunks contains as its first field, a keyword that indicates the type of information represented by the text string. The following keywords are predefined and should be used where appropriate [7]:


Title Short (one line) title or caption for image
Author Name of image's creator
Description Description of image (possibly long)
Copyright Copyright notice
Creation Time Time of original image creation
Software Software used to create the image
Disclaimer Legal disclaimer
Warning Warning of nature of content
Source Device used to create the image
Comment Miscellaneous comment

Technical Details:

An integer overflow error when parsing "tEXt" chunks of a PNG stream within a document file can be exploited to cause a stack-based buffer overflow. Successful exploitation allows execution of arbitrary code.

During the processing of a document containing a PNG stream, execution reaches sub_30E63709 where first a call to sub_30E63270 is made to verify the supplied PNG stream.

.text:30E63709 push ebp
.text:30E6370A lea ebp, [esp-74h]
.text:30E6370E sub esp, 9F0h
.text:30E63714 mov eax, ds:dword_3165E000
.text:30E63719 push ebx
.text:30E6371A push esi
.text:30E6371B push [ebp+74h+arg_0]
.text:30E6371E mov [ebp+74h+var_4], eax
.text:30E63721 mov esi, ecx
.text:30E63723 call sub_30E63DBC
.text:30E63728 push eax
.text:30E63729 lea ecx, [ebp+74h+var_1E4]
.text:30E6372F call sub_30D8EBB3
.text:30E63734 lea ecx, [ebp+74h+proessing_object]
.text:30E6373A call sub_30E63270 ; verify PNG stream.
.text:30E6373F test al, al ; valid PNG stream?
.text:30E63741 jz return_0

sub_30E63270 performs various checks by calling other functions including sub_30E633A4. Chunks with a very large length will pass the checks due to integer overflow errors.


.text:30E633BF loop_start:
.text:30E633BF mov eax, [esi+24h]
.text:30E633C2 add eax, edi
.text:30E633C4 mov ecx, eax
.text:30E633C6 mov [ebp+var_4], eax
.text:30E633C9 call get_dword ; chunk size
.text:30E633CE mov ecx, [ebp+var_4]
.text:30E633D1 add ecx, 4
.text:30E633D4 mov ebx, eax
.text:30E633D6 call get_dword ; Chunk Type
.text:30E633DB mov [ebp+var_4], eax
.text:30E633DE mov eax, [esi+28h]
.text:30E633E1 lea ecx, [ebx+edi+0Ch] ; *** integer overflow ***
.text:30E633E5 cmp ecx, eax
.text:30E633E7 ja loc_310BD821

.text:310BD82E lea ecx, [ebx+edi+8] ; *** integer overflow ***
.text:310BD832 cmp ecx, eax
.text:310BD834 jbe short loc_310BD83D

If sub_30E63270 detects certain errors during verification of the PNG stream, it returns zero and the image will not be processed. Otherwise, true is returned and execution reaches loc_30E638B3 to process any encountered "tEXt" chunk with the "Title" and "Description" keywords. It first parses "tEXt" chunks with the "Title" keyword via a call to sub_30E630FC.

Note: The same process will be done later for the "Description" keyword.

.text:30E638B3 loc_30E638B3:
.text:30E638B3 push 74455874h ; "tEXt"
.text:30E638B8 push 800h ; parsing_buffer_space
.text:30E638BD lea eax, [ebp+74h+buf] ; parsing_buffer
.text:30E638C3 push eax
.text:30E638C4 push offset aTitle_2 ; "Title"
.text:30E638C9 lea ecx, [ebp+74h+proessing_object]
.text:30E638CF mov [ebp+74h+buf], 0
.text:30E638D6 call sub_30E630FC ; process tEXt chunk with given keyword
.text:30E638DB test al, al
.text:30E638DD jz short loc_30E63944
.text:30E638DF lea edi, [ebp+74h+buf]
.text:30E638E5 lea ecx, [edi+1]
.text:30E638E8 loc_30E638E8:
.text:30E638E8 mov al, [edi]
.text:30E638EA inc edi
.text:30E638EB test al, al
.text:30E638ED jnz short loc_30E638E8
.text:30E638EF sub edi, ecx
.text:30E638F1 cmp edi, 200h
.text:30E638F7 jge short loc_30E6393C
.text:30E638F9 test edi, edi
.text:30E638FB jg insert_line_terminator ; insert 0D0A
.text:30E63901 loc_30E63901:
.text:30E63901 push 74455874h ; "tEXt"
.text:30E63906 mov eax, 800h
.text:30E6390B sub eax, edi ; calculating remained space in parsing_buffer
.text:30E6390D push eax ; parsing_buffer_space
.text:30E6390E lea ebx, [ebp+edi+74h+buf] ; parsing_buffer
.text:30E63915 push ebx
.text:30E63916 push offset aDescription ; "Description"
.text:30E6391B lea ecx, [ebp+74h+proessing_object]
.text:30E63921 call sub_30E630FC ; process tEXt chunk with given keyword
.text:30E63926 test al, al
.text:30E63928 jz short loc_30E63933

sub_30E630FC in turn calls sub_30E63126.

.text:30E630FC push ebp
.text:30E630FD mov ebp, esp
.text:30E630FF mov eax, 74455874h ; "tEXt" chunk
.text:30E63104 cmp [ebp+arg_C], eax
.text:30E63107 jnz loc_31028434
.text:30E6310D push dword ptr [ecx+18h]
.text:30E63110 push dword ptr [ecx+14h]
.text:30E63113 push eax
.text:30E63114 loc_30E63114:
.text:30E63114 push [ebp+arg_8] ; parsing_buffer_space
.text:30E63117 push [ebp+arg_4] ; parsing_buffer
.text:30E6311A push [ebp+arg_0] ; "Title" keyword
.text:30E6311D call sub_30E63126

The goal of sub_30E63126 is to extract text of all "tExt" chunks with the certain keyword (in this case "Title"). To achieve this goal, it enters a loop. Firstly, it starts by calculating the passed keyword length and checking if there is enough data available in the PNG stream.

.text:30E63126 push ebp
.text:30E63127 mov ebp, esp
.text:30E63129 sub esp, 14h
.text:30E6312C mov eax, [ebp+arg_0]
.text:30E6312F push ebx
.text:30E63130 xor ebx, ebx
.text:30E63132 cmp eax, ebx ; any keyword passed?
.text:30E63134 push esi
.text:30E63135 mov [ebp+pProcessing_object], ecx
.text:30E63138 jz loc_31028446
.text:30E6313E lea edx, [eax+1]
.text:30E63141 get_tEXt_keyword_length:
.text:30E63141 mov cl, [eax]
.text:30E63143 inc eax
.text:30E63144 test cl, cl
.text:30E63146 jnz short get_tEXt_keyword_length
.text:30E63148 sub eax, edx
.text:30E6314A inc eax
.text:30E6314B mov [ebp+tEXt_keyword_length], eax
.text:30E6314E loc_30E6314E:
.text:30E6314E cmp [ebp+buffer_size], ebx ; not enough data?
.text:30E63151 mov [ebp+result], 1
.text:30E63155 jbe short return_result

If enough data is available, it extracts the chunk size and checks if enough data is available in the stream for the extracted chunk length. Note that this check can be bypassed due to an integer overflow error.

.text:30E63157 mov esi, [ebp+current_offset]
.text:30E6315A cmp esi, ebx
.text:30E6315C ja loc_30F05B1F
.text:30F05B1F push edi
.text:30F05B20 mov eax, [ebp+pProcessing_object]
.text:30F05B23 mov edi, [eax+24h] ; load PNG stream
.text:30F05B26 lea ecx, [edi+esi] ; current offset
.text:30F05B29 mov [ebp+PNG_stream], edi
.text:30F05B2C call get_dword ; extracting chunk size
.text:30F05B31 mov ecx, [ebp+pProcessing_object]
.text:30F05B34 mov [ebp+cur_chunk_size], eax
.text:30F05B37 lea eax, [eax+esi+0Ch] ; *** integer overflow ***
.text:30F05B3B cmp eax, [ecx+28h] ; enough data available?
.text:30F05B3E ja short loc_30F05B95

Then, the chunk type is extracted and a check is done to see if it is a "tEXt" chunk.

.text:30F05B40 add esi, 4 ; advance the offset
.text:30F05B43 lea ecx, [edi+esi]
.text:30F05B46 call get_dword ; get chunk
.text:30F05B4B add esi, 4 ; advance the offset
.text:30F05B4E cmp eax, 'IEND' ; end of the PNG datastream?
.text:30F05B53 mov [ebp+current_offset], esi
.text:30F05B56 jz short loc_30F05B95
.text:30F05B58 cmp eax, [ebp+arg_C] ; tEXt chunk?
.text:30F05B5B jnz short continue_with_next

Then, the execution reaches loc_30F05B6D to check the keyword of the encountered "tEXt" chunk.

.text:30F05B6D mov eax, [ebp+PNG_stream]
.text:30F05B70 mov edi, [ebp+arg_0] ; "Title"
.text:30F05B73 add esi, eax ; loading current offset
.text:30F05B75 xor eax, eax
.text:30F05B77 repe cmpsb
.text:30F05B79 mov esi, [ebp+current_offset]
.text:30F05B7C jz loc_30FEFEF9

If the keyword matches, a check is done to ensure that there is enough space in a 2048-byte stack-based buffer to process that amount of data specified by chunk length. Note that this check can be bypassed due to the integer overflow error. Then the chunk data will be extracted to this buffer.

.text:30FEFF04 loc_30FEFF04:
.text:30FEFF04 mov ecx, [ebp+cur_chunk_size]
.text:30FEFF07 sub ecx, eax ; lean_chunk_size = cur_chunk_size – keyword_length
.text:30FEFF09 lea edx, [ecx+ebx] ; space_needed = lean_chunk_size + space_occupied *** integer overflow ***
.text:30FEFF0C lea edi, [edx+2] ; 2 more for appending 0D0A *** integer overflow ***
.text:30FEFF0F cmp edi, [ebp+buffer_size] ; parsing_buffer_space: 0x800 (2048) byte here
.text:30FEFF12 ja short continue_with_the_next_chunk ; not enough space to process this chunk
.text:30FEFF14 add esi, eax
.text:30FEFF16 mov eax, [ebp+pProcessing_object]
.text:30FEFF19 add esi, [eax+24h] ; loading the chunk data offset
.text:30FEFF1C mov eax, [ebp+arg_4]
.text:30FEFF1F lea edi, [eax+ebx] ; loading offset of parsing_buffer
.text:30FEFF22 mov ebx, ecx
.text:30FEFF24 shr ecx, 2
.text:30FEFF27 rep movsd ; *** stack-based buffer overflow here ***
.text:30FEFF29 mov ecx, ebx
.text:30FEFF2B and ecx, 3
.text:30FEFF2E mov ebx, edx
.text:30FEFF30 rep movsb
.text:30FEFF32 mov esi, [ebp+current_offset]
.text:30FEFF35 mov word ptr [ebx+eax], 0A0Dh ; appaned a NewLine
.text:30FEFF3B inc ebx
.text:30FEFF3C inc ebx
.text:30FEFF3D jmp continue_with_next_chunk

The loop continues checking for other "tEXt" chunks with the given keyword. By providing e.g. two "tEXT" chunks with e.g. "Title" or "Description" keyword where the size of the second one is an overly large value like 0xFFFFFFFF, it will be possible to pass the check of the available space in the buffer due to the integer overflow error and cause a stack-based buffer overflow.


Even though a working exploit already exists for this vulnerability, we would like to point out, that exploiting this vulnerability should be considered easy as the overflowed buffer resides on stack. Due to the copy size being a very large value, ultimately an access violation will be triggered that will be handled by an exception handler. As it is possible to overwrite the exception handlers with controlled data, receiving control and executing arbitrary code would be easily doable. The only remaining challenge would be to bypass ASLR on a more recent version of Microsoft Windows, which is not considered to be a hurdle due to the existence of non-ASLR modules within the process address space.

The vulnerability is patched in the fixes released with MS13-51 by checking for integer overflow errors when performing the calculations.

.text:30ECE722 mov esi, edx
.text:30ECE724 sub esi, [ebp+arg_10]
.text:30ECE727 lea eax, [ebp+var_18]
.text:30ECE72A push eax
.text:30ECE72B mov edx, esi
.text:30ECE72D mov ecx, ebx
.text:30ECE72F mov [ebp+var_28], esi
.text:30ECE732 call safely_add_edx_ecx
.text:30ECE737 test eax, eax ; integer overflow?
.text:30ECE739 jl return_0
.text:30ECE73B mov ecx, [ebp+var_18]
.text:30ECE73E lea eax, [ebp+var_18]
.text:30ECE741 push eax
.text:30ECE742 push 2
.text:30ECE744 pop edx
.text:30ECE745 call safely_add_edx_ecx
.text:30ECE74A test eax, eax ; integer overflow?
.text:30ECE74C jl return_0
.text:30ECE74E mov eax, [ebp+arg_8]
.text:30ECE751 cmp [ebp+var_18], eax
.text:30ECE754 ja short loc_30ECE787
.text:30ECE756 mov eax, [ebp+var_8]
.text:30ECE759 mov ecx, esi
.text:30ECE75B mov esi, [eax+24h]
.text:30ECE75E add esi, [ebp+arg_10]
.text:30ECE761 mov eax, [ebp+arg_4]
.text:30ECE764 add esi, [ebp+var_C]
.text:30ECE767 mov edx, ecx
.text:30ECE769 shr ecx, 2
.text:30ECE76C lea edi, [eax+ebx]
.text:30ECE76F rep movsd
.text:30ECE771 mov ecx, edx
.text:30ECE773 and ecx, 3
.text:30ECE776 add ebx, edx
.text:30ECE778 rep movsb
.text:30ECE77A mov word ptr [ebx+eax], 0A0Dh
.text:30ECE780 inc ebx
.text:30ECE781 inc ebx
.text:30ECE782 jmp loc_30E7FEFF

Stay secure!
Hossein Lotfi,
Security specialist,
Secunia research.