深入解析GDI+ EMF解析漏洞的非可执行性

本文详细分析了Microsoft GDI+组件在处理特制EMF文件时出现的off-by-one漏洞,通过汇编代码和内存转储展示了/GS安全机制如何有效防止代码执行,确认该漏洞不具备远程代码执行能力。

新发现的EMF gdiplus.dll崩溃无法用于代码执行 | MSRC博客

昨日我们注意到一篇博客文章和安全焦点文章,提及Microsoft GDI+在解析特制EMF文件时可能存在新漏洞。您可能已听说此漏洞被称为"GpFont.SetData()"。我们希望就这个EMF解析错误的一些推测进行说明。

首先,我们的初步调查显示该漏洞无法用于代码执行。我们仍在研究触发此代码的所有潜在方式,但在目前所有常见情况下,我们的/GS缓解措施都是有效的深度防御手段。该EMF解析错误最终会向/GS安全cookie的低两位字节写入0x0000(单个Unicode 0字符)。只有这两个字节被覆盖,应用程序会因/GS失败而终止。

以下是问题发生的位置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
0:000> k
ChildEBP RetAddr
0007e37c 4ec9e783 gdiplus!GpFont::SetData+0x6a
0007e3a0 4ec9e70f gdiplus!MetafilePlayer::AddObject+0x8f
0007e3b4 4ec9d1b5 gdiplus!ObjectEPR::Play+0x1a
0007e3d0 4ec9ce34 gdiplus!GdipPlayMetafileRecordCallback+0x35
0007e3fc 4ec9cd4e gdiplus!MetafilePlayer::EnumerateEmfPlusRecords+0x66
0007e414 77f2072f gdiplus!EnumEmfWithDownLevel+0x52
0007e490 4ec9e625 gdiplus!MetafilePlayer::EnumerateEmfRecords+0xd7
0007edd4 4eca0c90 gdiplus!GpGraphics::EnumEmfPlusDual+0x27d
0007ee70 4ec9e67f gdiplus!GpMetafile::EnumerateForPlayback+0x686
0007ef90 4ed03350 gdiplus!GpMetafile::Play+0x26

gdiplus!GpFont::SetData代码显示存在明显的off-by-one错误。此问题在Vista发布前已修复。以下是Windows XP代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#define FamilyNameMax 32

WCHAR familyName[FamilyNameMax];

length = fontData->Length; // 来自EMF文件

if (length > FamilyNameMax)
{
    length = FamilyNameMax;
}

// 读取familyName/data
UnicodeStringCopyCount (familyName, (WCHAR *)dataBuffer, length);
familyName[length]=0;

从汇编代码看:

1
2
3
4
5
6
7
8
9
gdiplus!GpFont::SetData:
4ecff9e9 8bff            mov     edi,edi
4ecff9eb 55              push    ebp
4ecff9ec 8bec            mov     ebp,esp
4ecff9ee 83ec44          sub     esp,44h
4ecff9f1 a10000dd4e      mov     eax,dword ptr [gdiplus!__security_cookie (4edd0000)] ds:0023:4edd0000=00006ea3
4ecff9f6 53              push    ebx
4ecff9f7 8945fc          mov     dword ptr [ebp-4],eax // 安全cookie保存在ebp-4
4ecff9fa 8b4508          mov     eax,dword ptr [ebp+8]

从函数开头可以看到安全cookie为00006ea3。继续往下看:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
4ecffa29 8b4804          mov     ecx,dword ptr [eax+4]
4ecffa2c 894f10          mov     dword ptr [edi+10h],ecx
4ecffa2f 8b4808          mov     ecx,dword ptr [eax+8]
4ecffa32 894f18          mov     dword ptr [edi+18h],ecx
4ecffa35 8b480c          mov     ecx,dword ptr [eax+0Ch]
4ecffa38 894f14          mov     dword ptr [edi+14h],ecx
4ecffa3b 8b7014          mov     esi,dword ptr [eax+14h]
4ecffa3e 8d4c3618        lea     ecx,[esi+esi+18h]
4ecffa42 83c018          add     eax,18h
4ecffa45 394d0c          cmp     dword ptr [ebp+0Ch],ecx
4ecffa48 0f8283000000    jb      gdiplus!GpFont::SetData+0xe8 (4ecffad1)
4ecffa4e 83fe20          cmp     esi,20h
4ecffa51 7603            jbe     gdiplus!GpFont::SetData+0x6d (4ecffa56)
4ecffa53 6a20            push    20h
4ecffa55 5e              pop     esi
4ecffa56 56              push    esi
4ecffa57 50              push    eax
4ecffa58 8d45bc          lea     eax,[ebp-44h] // 0x20 WCHAR缓冲区
4ecffa5b 50              push    eax
4ecffa5c e8d7750600      call    gdiplus!GpRuntime::UnicodeStringCopyCount (4ed67038)
4ecffa61 a15009dd4e      mov     eax,dword ptr [gdiplus!Globals::FontCollection (4edd0950)]
4ecffa66 66895c75bc      mov     word ptr [ebp+esi*2-44h],bx
4ecffa6b 8b7008          mov     esi,dword ptr [eax+8]
4ecffa6e 395e08          cmp     dword ptr [esi+8],ebx

在字符串复制前中断,可以看到:

1
2
3
4
5
eax=00913658 ebx=00000000 ecx=00000020 edx=05aa010c esi=00000020 edi=0091c798
eip=4ecffa66 esp=0007e32c ebp=0007e37c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
gdiplus!GpFont::SetData+0x7d:
4ecffa66 66895c75bc      mov     word ptr [ebp+esi*2-44h],bx ss:0023:0007e378=6ea3

从上面的汇编代码可知安全cookie为00006ea3。mov指令执行前的内存状态:

1
2
3
4
5
6
7
8
9
0:000> dd ebp-0x10
0007e36c  44332211 001d0b0b 44332211 00006ea3
0007e37c  0007e3a0 4ec9e783 05aa00b4 00000024
0007e38c  0091e008 0091e008 00000030 0091e001
0007e39c  0091e008 0007e3b4 4ec9e70f 00000000
0007e3ac  05aa00b4 00000024 0007e3d0 4ec9d1b5
0007e3bc  0091e008 00004008 00000600 00000024
0007e3cc  05aa00a8 0007e3fc 4ec9ce34 00004008
0007e3dc  00000600 00000024 05aa00b4 0091e008

可以看到栈上的cookie保护着返回地址。复制操作后cookie被覆盖:

1
2
3
4
5
6
7
8
9
0:000> dd ebp-0x10
0007e36c  44332211 001d0b0b 44332211 00000000
0007e37c  0007e3a0 4ec9e783 05aa00b4 00000024
0007e38c  0091e008 0091e008 00000030 0091e001
0007e39c  0091e008 0007e3b4 4ec9e70f 00000000
0007e3ac  05aa00b4 00000024 0007e3d0 4ec9d1b5
0007e3bc  0091e008 00004008 00000600 00000024
0007e3cc  05aa00a8 0007e3fc 4ec9ce34 00004008
0007e3dc  00000600 00000024 05aa00b4 0091e008

我们一如既往地鼓励负责任地披露潜在漏洞。联系我们最佳方式是secure@microsoft.com。感谢。

  • Jonathan Ness, MSRC工程团队 文章按"原样"提供,不提供任何担保,也不授予任何权利。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计