xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
添加文章 Xcon English Version

Microsoft Windows图形渲染引擎WMF格式解码内存越界漏洞


创建时间:2006-01-07 更新时间:2006-01-12
文章属性:原创
文章提交:cocoruder (frankruder_at_hotmail.com)

漏洞名称:Microsoft Windows图形渲染引擎WMF格式解码内存越界漏洞
发现者:cocoruder(frankruder_at_hotmail.com) http://ruder.cdut.net
类型:设计错误
最后更新时间:07/01/2006
受威胁的系统:
    Microsoft Windows XP SP2
    Microsoft Windows XP SP1
    Microsoft Windows Server 2003 SP1
    Microsoft Windows Server 2003
    Microsoft Windows ME
    Microsoft Windows 98se
    Microsoft Windows 98
    Microsoft Windows 2000SP4
未受威胁的系统:
    目前未知
厂商:
    www.microsoft.com

漏洞描述:
    Microsoft Windows的WMF图形渲染引擎处理特殊格式的wmf文件时缺乏参数检查,存在多个内存越界漏洞,如果用户访问了恶意的WMF格式文件将导致拒绝服务攻击。
    注意此文章中涉及的问题与ms06-001以及ms05-053中描述的无关。事实上,这是我在测试ms06-001时偶然发现的。

技术细节:
当前发现至少有2个地方可以触发内存越界错误。

1.ExtCreateRegion调用导致内存越界。
函数原型:
HRGN ExtCreateRegion(
  CONST XFORM *lpXform,     // transformation data
  DWORD nCount,             // size of region data
  CONST RGNDATA *lpRgnData  // region data buffer
);

PlayMetaFileRecord函数在调用此函数前会组织RGNDATA *lpRgnData参数,涉及下面几个结构
WMFRECORD结构:
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;

ExtCreateRegion调用对应的Parameters结构:
typedef    struct     _Parameters
{
    char    unknow1[0x0a];
    WORD    All_PointtStruct_Num;    //后面PointtStruct块的总数
    char    unknow2[0x0a];
    char    PointtStruct[];        //首块
}Parameters;

一个PointtStruct块的结构:
typedef    struct     _PointtStruct
{
    WORD    PointNum;        //后面的RECT结构成员的数目(1个成员2字节)
    WORD    Point[PointNum];    //4个Point构成1个RECT
    char    unkonow[6];
}PointtStruct;

因此每个PointtStruct块的总字节数=PointNum*2+8
ExtCreateRegion调用码为0xff,下面的代码完成计算并分配堆内存操作。

.text:7F00FE07 loc_7F00FE07:                           ; CODE XREF: PlayMetaFileRecord+1256j
.text:7F00FE07                 sub     eax, 3
.text:7F00FE0A                 jnz     loc_7F022B9A    ; 0xff
.text:7F00FE10                 movzx   ecx, word ptr [ebx+10h]        ;取得PointtStruct块总数
.text:7F00FE14                 mov     [ebp-88h], ecx            ;保存
.text:7F00FE1A                 test    ecx, ecx
.text:7F00FE1C                 jnz     short loc_7F00FE2E        ;跳
.text:7F00FE1E                 xor     eax, eax
.text:7F00FE20                 push    eax             ; int
.text:7F00FE21                 push    eax             ; int
.text:7F00FE22                 push    eax             ; int
.text:7F00FE23                 push    eax             ; int
.text:7F00FE24                 call    CreateRectRgn
.text:7F00FE29                 jmp     loc_7F010494
.text:7F00FE2E ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
.text:7F00FE2E
.text:7F00FE2E loc_7F00FE2E:                           ; CODE XREF: PlayMetaFileRecord+C15j
.text:7F00FE2E                 xor     edi, edi
.text:7F00FE30                 mov     [ebp-8Ch], edi
.text:7F00FE36                 lea     eax, [ebx+1Ch]            ;得到首PointtStruct块地址
.text:7F00FE39                 mov     [ebp-90h], eax            ;保存
.text:7F00FE3F                 and     [ebp-94h], edi            
.text:7F00FE45                 and     [ebp-98h], edi
.text:7F00FE4B                 test    ecx, ecx
.text:7F00FE4D                 jbe     short loc_7F00FE8C        
.text:7F00FE4F
.text:7F00FE4F loc_7F00FE4F:                           ; CODE XREF: PlayMetaFileRecord+C83j
.text:7F00FE4F                 movzx   ecx, word ptr [eax]        ;取得PointNum,可触发内存错误**
.text:7F00FE52                 mov     edx, ecx                
.text:7F00FE54                 shr     edx, 1                ;PointNum/2
.text:7F00FE56                 add     edx, edi                ;累加PointNum/2
.text:7F00FE58                 cmp     edx, edi
.text:7F00FE5A                 jb      loc_7F0106D5
.text:7F00FE60                 mov     edi, edx
.text:7F00FE62                 mov     [ebp-8Ch], edi
.text:7F00FE68                 cmp     ecx, 7FFFFFFBh
.text:7F00FE6E                 jnb     loc_7F0106D5
.text:7F00FE74                 lea     eax, [eax+ecx*2+8]
.text:7F00FE78                 inc     dword ptr [ebp-98h]        ;计数器+1
.text:7F00FE7E                 mov     ecx, [ebp-98h]
.text:7F00FE84                 cmp     ecx, [ebp-88h]            ;与PointtStruct块总数相比较
.text:7F00FE8A                 jb      short loc_7F00FE4F        ;遍历所有PointtStruct块结构
.text:7F00FE8C
.text:7F00FE8C loc_7F00FE8C:                           ; CODE XREF: PlayMetaFileRecord+C46j
.text:7F00FE8C                                         ; PlayMetaFileRecord+14D8j
.text:7F00FE8C                 cmp     dword ptr [ebp-94h], 0
.text:7F00FE93                 jnz     loc_7F022BA2
.text:7F00FE99                 cmp     edi, 0FFFFFFDh
.text:7F00FE9F                 jnb     loc_7F022BA2
.text:7F00FEA5                 mov     eax, [ebp-8Ch]
.text:7F00FEAB                 add     eax, 2
.text:7F00FEAE                 shl     eax, 4
.text:7F00FEB1                 mov     [ebp-9Ch], eax
.text:7F00FEB7                 push    eax             ; uBytes
.text:7F00FEB8                 push    0               ; uFlags
.text:7F00FEBA                 call    ds:LocalAlloc            ;正常的话在这里分配堆内存
.text:7F00FEC0                 mov     edi, eax

因此当设置足够大的All_PointtStruct_Num,PointNum,但后面未有足够多的PointtStruct结构,在遍历所有PointNum时即会产生内存越界,导致拒绝服务攻击。

一个例子:
...05020000FF0044444444444444444444FFFF33333333333333333333FFFF41424344...

2.ExtEscape POSTSCRIPT_INJECTION调用导致内存越界。
原型:
int ExtEscape(
  hdc,                  // handle to DC (HDC)
  POSTSCRIPT_INJECTION, // nEscape param of ExtEscape
  cbInput,              // size of input buffer
  lpszInData,           // pointer to input (PSINJECTDATA *)
  0,                    // cbOutput param of ExtEscape
  NULL                  // lpszOutData param of ExtEscape
);

cbInput我们可以控制,如果设置足够大的cbInput,但后面未有足够多的szInData的话,将可能产生内存越界,导致拒绝服务攻击。
ExtEscape里处理POSTSCRIPT_INJECTION(0x1016)调用的代码如下:

.text:7F027312 loc_7F027312:                           ; CODE XREF: ExtEscape+11Ej
.text:7F027312                                         ; ExtEscape+12Aj
.text:7F027312                 test    byte ptr [ecx+4], 40h
.text:7F027316                 jnz     loc_7F017CEC
.text:7F02731C                 mov     ebx, [ebp+arg_8]        ;我们可以控制的cbSize
.text:7F02731F                 add     ebx, 1Ah            ;cbSize+0x1a
.text:7F027322                 and     ebx, 0FFFFFFFCh        ;cbSize+0x1a-4
.text:7F027325                 mov     eax, large fs:18h
.text:7F02732B                 mov     eax, [eax+30h]
.text:7F02732E                 push    ebx
.text:7F02732F                 push    0
.text:7F027331                 push    dword ptr [eax+18h]
.text:7F027334                 call    ds:RtlAllocateHeap    ;按我们设置的cbSize分配cbSize+0x16长度的内存
.text:7F02733A                 test    eax, eax
.text:7F02733C                 jz      short loc_7F027385
.text:7F02733E                 mov     ecx, [ebp+arg_4]
.text:7F027341                 mov     [eax+0Ch], ecx
.text:7F027344                 mov     ecx, [ebp+arg_8]
.text:7F027347                 mov     [eax+10h], ecx
.text:7F02734A                 mov     edx, ecx
.text:7F02734C                 shr     ecx, 2
.text:7F02734F                 sub     ebx, 8
.text:7F027352                 mov     [eax+8], ebx
.text:7F027355                 lea     edi, [eax+14h]
.text:7F027358                 rep movsd            ;拷贝,可触发内存错误**
.text:7F02735A                 mov     ecx, edx
.text:7F02735C                 and     ecx, 3
.text:7F02735F                 rep movsb
.text:7F027361                 mov     ecx, [ebp-4]
.text:7F027364                 mov     edi, [ebp+arg_14]
.text:7F027367                 lea     edx, [ecx+48h]
.text:7F02736A                 mov     esi, [edx+4]
.text:7F02736D                 mov     [eax+4], esi

一个例子:
...0502000026001610FFFF4444444444444444...

解决方案:
   在Microsoft未发布新的补丁前请注销Windows图形和传真查看器(Shimgvw.dll),具体可参考MS06-001的变通办法。
致谢:
   感谢我的朋友们。
声明:
   本文涉及技术知识仅供仅供安全研究与教学之用,使用者风险自负!