高级安全专家Hossein Lotfi

2016年12月14日更新: 在分析Microsoft修复程序期间,我们确认仍未修复相关错误。因此,又发布了一个Secunia咨询SA74000 [5]来解决这个问题。

On December 13, 2016, Microsoft released updates that fix two vulnerabilities reported由Secunia Research提供。 Both can be exploited through a specially crafted font file. One vulnerability results in a Denial of Service (DoS) or a privilege escalation and the other allows a compromise of a vulnerable system even.

本文的目的是提供有关第二个漏洞的详细信息,该漏洞是基于堆的缓冲区溢出漏洞,该漏洞由Microsoft Unicode脚本处理器(属于Microsoft Windows操作系统)中的整数溢出错误引起。此漏洞已分配了常见漏洞和披露(CVE)标识符CVE-2016-7274,并在Microsoft安全公告MS16-147 [1]和Secunia Advisory SA70000 [2]中进行了概述。由于该漏洞最终通过远程提供的True Type字体(TTF)文件导致受影响的系统受到威胁,因此Secunia Advisory SA70000的等级为“Highly Critical”由Secunia Research提供。

通常,可以通过特制网页在基于Web的方案中利用这种漏洞,而指向此类网页的链接可能会通过电子邮件发送给毫无戒心的受害者。此外,攻击者还可以创建嵌入了特制字体文件的文档,然后将其发送给受害者。打开此类特制的网页或文档后,利用可能会在受影响的系统上成功进行。

介绍:

Uniscribe是Microsoft Windows服务集,用于呈现Unicode编码的文本,尤其是复杂的文本布局[3]。在Windows 8和8.1中,实现从USP10.dll(DLL,动态链接库)移向GDI32.dll。在Windows 10中,该功能在GDI32full.dll中实现。

请注意,以下分析是在Windows 10 Enterprise上使用GDI32full.dll版本10.0.14393.206进行的。

再生产:

Windows 10企业版的默认安装总共包含119个TTF文件。仅需稍加修改即可使用其中两个来轻松触发此问题:Microsoft PhagsPa(phagspa.ttf)和Microsoft PhagsPa Bold(phagspab.ttf)。

为了进行复制,请在十六进制编辑器中打开文件“ C:\ Windows \ Fonts \ phagspa.ttf”,并将偏移量0x2051的值从0x00000006更改为0x33333334。

技术细节:

在处理字体文件中的脚本期间,代码流到达“LoadFont()”GDI32full.dll中的函数。此后不久,此函数调用“GetFontDesc()”函数加载字体内字符代码的映射。

.text:750F42CA ; __int32 __stdcall LoadFont(HDC, struct FACE_CACHE *)
.text:750F42CA var_260         = dword ptr -260h
.text:750F42CA var_25C         = dword ptr -25Ch
.text:750F42CA var_258         = dword ptr -258h
.text:750F42CA var_254         = dword ptr -254h
.text:750F42CA var_250         = dword ptr -250h
.text:750F42CA lpMem           = dword ptr -24Ch
.text:750F42CA Dst             = byte ptr -248h
.text:750F42CA var_24          = HDC__ ptr -24h
.text:750F42CA var_4           = dword ptr -4
.text:750F42CA
.text:750F42CA ; FUNCTION CHUNK AT .text:7512EDD3 SIZE 000001F7 BYTES
.text:750F42CA
.text:750F42CA          mov     edi, edi
.text:750F42CC          push    ebp
.text:750F42CD          mov     ebp, esp
.text:750F42CF          sub     esp, 264h
.text:750F42D5          mov     eax, ___security_cookie
.text:750F42DA          xor     eax, ebp
.text:750F42DC          mov     [ebp+var_4], eax
.text:750F42DF          push    ebx
.text:750F42E0          push    esi             ; unsigned __int32 *
.text:750F42E1          push    edi             ; union FONTUVSENTRY *
.text:750F42E2          xor     ebx, ebx
.text:750F42E4          mov     [ebp+var_250], ecx
.text:750F42EA          and     [ebp+var_254], ebx
.text:750F42F0          lea     eax, [ebp+Dst]
.text:750F42F6          push    220h            ; Size
.text:750F42FB          push    ebx             ; Val
.text:750F42FC          push    eax             ; Dst
.text:750F42FD          mov     esi, edx
.text:750F42FF          mov     [ebp+lpMem], ebx
.text:750F4305          call    _memset
.text:750F430A          add     esp, 0Ch
.text:750F430D          lea     edi, [ebp+var_24]
.text:750F4310          push    8
.text:750F4312          pop     eax
.text:750F4313          mov     ecx, eax
.text:750F4315          xor     eax, eax
.text:750F4317          rep stosd
.text:750F4319          mov     eax, 0F807h
.text:750F431E          and     [esi+0A0h], ax
.text:750F4325          mov     al, [esi+95h]
.text:750F432B          and     al, 0Ch
.text:750F432D          cmp     al, 8
.text:750F432F          jz      loc_7512EDD3
.text:750F4335
.text:750F4335 loc_750F4335:        ; CODE XREF: LoadFont(HDC__ *,FACE_CACHE *)+3AB0F_j
.text:750F4335          mov     ecx, [ebp+var_250]
.text:750F433B          lea     eax, [ebp+lpMem]
.text:750F4341          lea     edi, [esi+98h]
.text:750F4347          push    eax             ; HDC
.text:750F4348          mov     edx, edi
.text:750F434A          call    GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)

的“GetFontDesc()”函数首先检查“OS/2”表,然后从cmap表加载数据。

.text:750F6618 ; __int32 __stdcall GetFontDesc(HDC, int *, struct FONTCMAPDESC **)
.text:750F6618 hdc             = dword ptr -30h
.text:750F6618 var_2C          = dword ptr -2Ch
.text:750F6618 var_28          = dword ptr -28h
.text:750F6618 var_24          = dword ptr -24h
.text:750F6618 pvBuffer        = dword ptr -20h
.text:750F6618 var_1C          = dword ptr -1Ch
.text:750F6618 var_18          = dword ptr -18h
.text:750F6618 var_14          = dword ptr -14h
.text:750F6618 var_10          = dword ptr -10h
.text:750F6618 var_C           = dword ptr -0Ch
.text:750F6618 var_8           = dword ptr -8
.text:750F6618 var_4           = dword ptr -4
.text:750F6618 arg_0           = dword ptr  8
.text:750F6618
.text:750F6618 ; FUNCTION CHUNK AT .text:7512F8E4 SIZE 0000007C BYTES
.text:750F6618
.text:750F6618          mov     edi, edi
.text:750F661A          push    ebp
.text:750F661B          mov     ebp, esp
.text:750F661D          sub     esp, 30h
.text:750F6620          and     [ebp+var_C], 0
.text:750F6624          lea     eax, [ebp+pvBuffer]
.text:750F6627          push    ebx
.text:750F6628          push    esi             ; int
.text:750F6629          push    edi             ; unsigned __int32 *
.text:750F662A          mov     edi, [ebp+arg_0]
.text:750F662D          mov     ebx, ecx
.text:750F662F          push    4               ; cjBuffer
.text:750F6631          push    eax             ; pvBuffer
.text:750F6632          push    3Eh             ; dwOffset
.text:750F6634          and     dword ptr [edi], 0
.text:750F6637          mov     esi, edx
.text:750F6639          push    '2/SO'          ; dwTable
.text:750F663E          push    ebx             ; hdc
.text:750F663F          mov     [ebp+var_18], esi
.text:750F6642          mov     [ebp+hdc], ebx
.text:750F6645           call    ds:[email protected] ; GetFontData(x,x,x,x,x)
.text:750F664B          cmp     eax, 4
.text:750F664E          jnz     loc_750F68B2
.text:750F6654          mov     al, byte ptr [ebp+pvBuffer+2]
.text:750F6657          cmp     al, 0F0h
.text:750F6659          jnb     short loc_750F665F
.text:750F665B          test    al, al
.text:750F665D          jnz     short loc_750F666A
.text:750F665F
.text:750F665F loc_750F665F:     ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+41_j
.text:750F665F          mov     eax, [ebp+pvBuffer]
.text:750F6662          test    al, al
.text:750F6664          jnz     loc_7512F8E4
.text:750F666A
.text:750F666A loc_750F666A:    ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+45_j
.text:750F666A          or      dword ptr [esi], 0FFFFFFFFh
.text:750F666D
.text:750F666D loc_750F666D: ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+392D1_j
.text:750F666D          xor     eax, eax
.text:750F666F          push    eax             ; cjBuffer
.text:750F6670          push    eax             ; pvBuffer
.text:750F6671          push    eax             ; dwOffset
.text:750F6672          push    'pamc'          ; dwTable
.text:750F6677          push    ebx             ; hdc
.text:750F6678          call    ds:[email protected] ; GetFontData(x,x,x,x,x)
.text:750F667E          mov     ebx, eax
.text:750F6680          cmp     ebx, 0FFFFFFFFh
.text:750F6683          jz      loc_7512F955
.text:750F6689          cmp     ebx, 4
.text:750F668C          jl      loc_7512F955
.text:750F6692          push    edi             ; int
.text:750F6693          lea     ecx, [ebx+34h]
.text:750F6696          push    ecx             ; dwBytes
.text:750F6697          call    [email protected] ; UspAllocCache(x,x)
.text:750F669C          test    eax, eax
.text:750F669E          js      loc_7512F955
.text:750F66A4          mov     ecx, [edi]
.text:750F66A6          push    ebx             ; cjBuffer
.text:750F66A7          lea     eax, [ecx+34h]
.text:750F66AA          mov     [ecx+4], eax
.text:750F66AD          mov     eax, [edi]
.text:750F66AF          mov     [eax+8], ebx
.text:750F66B2          mov     eax, [edi]
.text:750F66B4          push    dword ptr [eax+4] ; pvBuffer
.text:750F66B7          push    0              ; dwOffset
.text:750F66B9          push    'pamc'          ; dwTable
.text:750F66BE          push    [ebp+hdc]       ; hdc
.text:750F66C1          call    ds:[email protected] ; GetFontData(x,x,x,x,x)

根据加载的信息,进行检查以确保有足够的数据可用,并且至少有一个EncodingRecord表。

.text:750F66CF          mov     eax, [edi]
.text:750F66D1          mov     eax, [eax+4]
.text:750F66D4          rol     word ptr [eax+2], 8             ;  numTables
.text:750F66D9          mov     ecx, [edi]
.text:750F66DB          mov     edx, [ecx+4]
.text:750F66DE          movzx   eax, word ptr [edx+2]
.text:750F66E2          mov     [ebp+numTables], eax
.text:750F66E5          lea     eax, ds:4[eax*8               ;  needed data
.text:750F66EC          cmp     ebx, eax                    ; check if enough data is available.
.text:750F66EE          jl      loc_7512F8EE
.text:750F66F4          lea     eax, [edx+4]
.text:750F66F7          xor     edx, edx
.text:750F66F9          and     [ecx+2Ch], edx
.text:750F66FC          and     [ebp+var_8], edx
.text:750F66FF          and     [ebp+var_10], edx
.text:750F6702          mov     [ebp+var_14], eax
.text:750F6705          mov     eax, [edi]
.text:750F6707          mov     [ebp+var_4], edx
.text:750F670A          and     [eax+30h], edx
.text:750F670D          mov     eax, [ebp+numTables]            ; check if numTables is zero
.text:750F6710          test    eax, eax
.text:750F6712          jle     cleanup_and_return

之后,进入一个循环以处理可用的EncodingRecords。如果平台ID为0,编码ID为5,子表格式为14 [4],则该记录将被视为“ Unicode变异序列”(UVS)记录,并且将UVS子表的偏移量和大小保存在处理对象中,以用于以后处理。

loc_750F671E:           ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+1B0_j
.text:750F671E          xor     edx, edx
.text:750F6720          inc     edx
.text:750F6721          xor     eax, eax
.text:750F6723
.text:750F6723 loc_750F6723:   ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+114_j
.text:750F6723          rol     word ptr [ecx+eax*2], 8
.text:750F6728          inc     eax
.text:750F6729          cmp     eax, 2
.text:750F672C          jl      short loc_750F6723
.text:750F672E          lea     esi, [ecx+4]
.text:750F6731          mov     ecx, esi
.text:750F6733          call    [email protected]@[email protected]          ; FlipDWords(ulong *,int)
.text:750F6738          movzx   ecx, word ptr [esi-2]           ; encodingID
.text:750F673C          mov     esi, [esi]
.text:750F673E          mov     [ebp+var_28], esi               ; offset
.text:750F6741          test    esi, esi
.text:750F6743          jz      continue_loop
.text:750F6749          lea     eax, [ebx-4]
.text:750F674C          cmp     eax, esi
.text:750F674E          jbe     continue_loop
.text:750F6754          mov     edx, [edi]
.text:750F6756          mov     eax, [edx+4]
.text:750F6759          add     eax, esi
.text:750F675B          mov     esi, [ebp+var_14]
.text:750F675E          mov     [ebp+encoding_subtable_offset], eax ; encoding_subtable_offset
.text:750F6761          mov     ax, [eax]
.text:750F6764          rol     ax, 8
.text:750F6768          movzx   eax, ax
.text:750F676B          mov     [ebp+var_2C], eax
.text:750F676E          movzx   eax, word ptr [esi]             ; platformID
.text:750F6771          test    ax, ax
.text:750F6774          jz      loc_750F689B
.text:750F677A          push    3
.text:750F677C          pop     edx
.text:750F677D          cmp     ax, dx
.text:750F6780          jnz     continue_loop
.text:750F6786          xor     eax, eax
.text:750F6788          inc     eax
.text:750F6789          test    cx, cx
.text:750F678C          jz      short loc_750F679A
.text:750F678E          cmp     cx, ax
.text:750F6791          jnz     loc_750F68BA
.text:750F6797          push    2
.text:750F6799          pop     eax
.text:750F679A
.text:750F679A loc_750F679A:   ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+174_j
.text:750F679A          cmp     [ebp+var_10], eax
.text:750F679D          jge     continue_loop
.text:750F67A3          mov     [ebp+var_10], eax
.text:750F67A6
.text:750F67A6 loc_750F67A6:   ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+2B2_j
.text:750F67A6          mov     eax, [ebp+var_2C]
.text:750F67A9          mov     esi, [ebp+encoding_subtable_offset]
.text:750F67AC          mov     edx, [ebp+var_28]
.text:750F67AF          movzx   eax, ax
.text:750F67B2          mov     [ebp+var_8], esi
.text:750F67B5          mov     [ebp+var_4], edx
.text:750F67B8          mov     [ebp+var_C], eax
.text:750F67BB
.text:750F67BB loc_750F67BB:  ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+295_j
.text:750F67BB          mov     ecx, [ebp+var_14]
.text:750F67BE          add     ecx, 8
.text:750F67C1          sub     [ebp+num_Tables], 1
.text:750F67C5          mov     [ebp+var_14], ecx
.text:750F67C8          jnz     loc_750F671E

.text:750F689B loc_750F689B:  ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+15C_j
.text:750F689B          cmp     ecx, 5     ;encoding ID = 5?
.text:750F689E          jz      loc_7512F8F1

.text:7512F8F1 loc_7512F8F1:    ; CODE XREF: GetFontDesc(HDC__ *,int *,FONTCMAPDESC * *)+286_j
.text:7512F8F1          mov     eax, [ebp+var_2C]
.text:7512F8F4          cmp     ax, 0Eh          ; is subtable format 14?
.text:7512F8F8          jnz     continue_loop
.text:7512F8FE          cmp     dword ptr [edx+2Ch], 0          ; just one record allowed.
.text:7512F902          jnz     continue_loop
.text:7512F908          mov     ecx, ebx
.text:7512F90A          sub     ecx, [ebp+var_28]
.text:7512F90D          push    0Ah
.text:7512F90F          pop     eax
.text:7512F910          cmp     ecx, eax
.text:7512F912          jl      continue_loop
.text:7512F918          mov     esi, [ebp+encoding_subtable_offset]
.text:7512F91B          mov     [edx+2Ch], esi                  ; UVS_subtable_offset
.text:7512F91E          mov     eax, [edi]
.text:7512F920          mov     [eax+30h], ecx                  ; UVS_subtable_size
.text:7512F923          jmp     continue_loop

从该函数返回后,代码最终调用“ LoadUvsTable()”函数来处理可能遇到的UVS表。

.text:750F4446          xor     edx, edx                ; passing zero. Just return space needed.
.text:750F4448          push    edi                    ; struct FONTCMAPDESC *
.text:750F4449          mov     ecx, ebx
.text:750F444B          mov     [ebp+var_258], edi
.text:750F4451          call    LoadUvsTable (FONTCMAPDESC *,FONTUVSENTRY *,ulong *)

首先,该函数通过验证是否设置了UVS_subtable_offset来检查是否找到了UVS记录。

.text:750F5DB2 ; __int32 __stdcall LoadUvsTable(struct FONTCMAPDESC *, union FONTUVSENTRY *, unsigned __int32 *)
.text:750F5DB2 var_2C= dword ptr -2Ch
.text:750F5DB2 var_28= dword ptr -28h
.text:750F5DB2 var_24= dword ptr -24h
.text:750F5DB2 var_20= dword ptr -20h
.text:750F5DB2 var_1C= dword ptr -1Ch
.text:750F5DB2 var_18= dword ptr -18h
.text:750F5DB2 var_14= dword ptr -14h
.text:750F5DB2 var_10= dword ptr -10h
.text:750F5DB2 var_C= dword ptr -0Ch
.text:750F5DB2 var_8= dword ptr -8
.text:750F5DB2 var_4= dword ptr -4
.text:750F5DB2 arg_0= dword ptr  8
.text:750F5DB2
.text:750F5DB2 ; FUNCTION CHUNK AT .text:7512F4CE SIZE 000001D4 BYTES
.text:750F5DB2
.text:750F5DB2          mov     edi, edi
.text:750F5DB4          push    ebp
.text:750F5DB5          mov     ebp, esp
.text:750F5DB7          sub     esp, 2Ch
.text:750F5DBA          mov     eax, [ebp+arg_0]
.text:750F5DBD          push    ebx
.text:750F5DBE          mov     [ebp+var_C], edx
.text:750F5DC1          xor     edx, edx
.text:750F5DC3          mov     [ebp+var_10], edx
.text:750F5DC6          mov     ebx, [eax]
.text:750F5DC8          mov     [ebp+var_2C], ebx
.text:750F5DCB          push    esi
.text:750F5DCC          push    edi
.text:750F5DCD          test    ecx, ecx
.text:750F5DCF          jz      short loc_750F5DDC
.text:750F5DD1          mov     ebx, [ecx+2Ch]                  ; is UVS_subtable_offset set?
.text:750F5DD4          test    ebx, ebx
.text:750F5DD6          jnz     loc_7512F4CE                    ; is UVS_subtable_size set?

经过一些完整性检查后,将提取变体选择器记录的数量。

.text:7512F4CE          cmp     dword ptr [ecx+30h], 0Ah ; at least one variation Selector Record data available?
.text:7512F4D2          jb      loc_750F5DDC
.text:7512F4D8          movzx   esi, byte ptr [ebx+2]
.text:7512F4DC          movzx   eax, byte ptr [ebx+3]
.text:7512F4E0          shl     esi, 8
.text:7512F4E3          or      esi, eax
.text:7512F4E5          movzx   eax, byte ptr [ebx+4]
.text:7512F4E9          shl     esi, 8
.text:7512F4EC          or      esi, eax
.text:7512F4EE          movzx   eax, byte ptr [ebx+5]
.text:7512F4F2          shl     esi, 8
.text:7512F4F5          or      esi, eax                        ; extracted_size_from_subtable
.text:7512F4F7          cmp     esi, [ecx+30h]
.text:7512F4FA          ja      loc_750F5DDE
.text:7512F500          movzx   edi, byte ptr [ebx+6]
.text:7512F504          movzx   eax, byte ptr [ebx+7]
.text:7512F508          shl     edi, 8
.text:7512F50B          or      edi, eax
.text:7512F50D          movzx   eax, byte ptr [ebx+8]
.text:7512F511          shl     edi, 8
.text:7512F514          or      edi, eax
.text:7512F516          movzx   eax, byte ptr [ebx+9]
.text:7512F51A          shl     edi, 8
.text:7512F51D          or      edi, eax                 ; Number of variation selector records
.text:7512F51F          imul    eax, edi, 0Bh
.text:7512F522          add     eax, 0Ah
.text:7512F525          cmp     esi, eax                 ; enough data available in subtable?
.text:7512F527          jb      loc_750F5DDE
.text:7512F52D          and     [ebp+var_18], edx
.text:7512F530          lea     eax, [ebx+0Ah]
.text:7512F533          and     [ebp+var_24], edx
.text:7512F536          mov     [ebp+var_28], eax
.text:7512F539          test    edi, edi                  ; No variation selector records?
.text:7512F53B          jz      return

然后进入一个循环,以查看需要多少空间来处理变化选择器记录。这是通过计算所有记录中范围的总数来完成的。对于每个提取的记录范围编号,将进行检查以查看是否有足够的数据可用。请注意,此处存在整数溢出错误,可用于绕过此检查。由于在调用此函数时未传递任何缓冲区,因此仅返回计算出的缓冲区大小。

.text:7512F547          movzx   eax, byte ptr [eax]
.text:7512F54A          movzx   ecx, byte ptr [edx-8]
.text:7512F54E          shl     eax, 8
.text:7512F551          or      ecx, eax
.text:7512F553          movzx   eax, byte ptr [edx-7]
.text:7512F557          shl     ecx, 8
.text:7512F55A          or      ecx, eax
.text:7512F55C          mov     [ebp+var_20], ecx
.text:7512F55F          cmp     [ebp+var_18], ecx
.text:7512F562          jnb     loc_750F5DDE
.text:7512F568          movzx   eax, byte ptr [edx-1]
.text:7512F56C          mov     [ebp+var_18], ecx
.text:7512F56F          movzx   ecx, byte ptr [edx-2]
.text:7512F573          shl     ecx, 8
.text:7512F576          or      ecx, eax
.text:7512F578          movzx   eax, byte ptr [edx]
.text:7512F57B          shl     ecx, 8
.text:7512F57E          or      ecx, eax
.text:7512F580          movzx   eax, byte ptr [edx+1]
.text:7512F584          shl     ecx, 8
.text:7512F587          or      ecx, eax
.text:7512F589          jz      loc_7512F66B
.text:7512F58F          lea     eax, [ecx+4]
.text:7512F592          cmp     eax, ecx
.text:7512F594          jb      loc_750F5DDE
.text:7512F59A          cmp     eax, esi
.text:7512F59C          ja      loc_750F5DDE
.text:7512F5A2          lea     edx, [ecx+ebx]
.text:7512F5A5          movzx   eax, byte ptr [edx]
.text:7512F5A8          shl     eax, 8
.text:7512F5AB          mov     [ebp+var_4], eax
.text:7512F5AE          movzx   eax, byte ptr [edx+1]
.text:7512F5B2          or      [ebp+var_4], eax
.text:7512F5B5          movzx   eax, byte ptr [edx+2]
.text:7512F5B9          shl     [ebp+var_4], 8
.text:7512F5BD          or      [ebp+var_4], eax
.text:7512F5C0          movzx   eax, byte ptr [edx+3]
.text:7512F5C4          shl     [ebp+var_4], 8
.text:7512F5C8          mov     [ebp+var_1C], edx
.text:7512F5CB          mov     edx, [ebp+var_4]
.text:7512F5CE          or      edx, eax
.text:7512F5D0          mov     [ebp+var_4], edx                ; numUnicodeValueRanges
.text:7512F5D3          imul    edx, 5                          ; *** Integer Overflow ***
.text:7512F5D6          push    esi
.text:7512F5D7          add     edx, 4
.text:7512F5DA          call    CheckBuffer
.text:7512F5DF          test    eax, eax
.text:7512F5E1          jz      loc_750F5DDE
.text:7512F5E7          mov     edx, [ebp+var_1C]
.text:7512F5EA          add     edx, 4
.text:7512F5ED          cmp     [ebp+var_C], 0                  ; is a buffer passed?
.text:7512F5F1          jz      short loc_7512F65F
.text:7512F5F3          and     [ebp+var_1C], 0
.text:7512F5F7          cmp     [ebp+var_4], 0
.text:7512F5FB          jbe     short loc_7512F668
.text:7512F5FD          mov     ecx, [ebp+var_20]
.text:7512F600
.text:7512F600 ; CODE XREF: LoadUvsTable(FONTCMAPDESC *,FONTUVSENTRY *,ulong *)+398A9_j
.text:7512F600          movzx   eax, byte ptr [edx]
.text:7512F603          shl     eax, 8
.text:7512F606          mov     [ebp+var_8], eax
.text:7512F609          movzx   eax, byte ptr [edx+1]
.text:7512F60D          or      [ebp+var_8], eax
.text:7512F610          shl     [ebp+var_8], 8
.text:7512F614          movzx   eax, byte ptr [edx+2]
.text:7512F618          or      [ebp+var_8], eax
.text:7512F61B          jbe     loc_750F5DDE
.text:7512F621          mov     eax, [ebp+var_10]
.text:7512F624          inc     eax
.text:7512F625          mov     [ebp+var_10], eax
.text:7512F628          cmp     [ebp+var_2C], eax
.text:7512F62B          jb      short loc_7512F698
.text:7512F62D          mov     eax, [ebp+var_C]
.text:7512F630          mov     [eax+4], ecx
.text:7512F633          mov     ecx, [ebp+var_C]
.text:7512F636          mov     eax, [ebp+var_8]
.text:7512F639          add     [ebp+var_C], 0Ah
.text:7512F63D          mov     [ecx], eax
.text:7512F63F          mov     ax, [edx+3]
.text:7512F643          add     edx, 5
.text:7512F646          rol     ax, 8
.text:7512F64A          mov     [ecx+8], ax
.text:7512F64E          mov     eax, [ebp+var_1C]
.text:7512F651          mov     ecx, [ebp+var_20]
.text:7512F654          inc     eax
.text:7512F655          mov     [ebp+var_1C], eax
.text:7512F658          cmp     eax, [ebp+var_4]
.text:7512F65B          jb      short loc_7512F600
.text:7512F65D          jmp     short loc_7512F668
.text:7512F65F ; ---------------------------------------------------------------------------
.text:7512F65F
.text:7512F65F; CODE XREF: LoadUvsTable(FONTCMAPDESC *,FONTUVSENTRY *,ulong *)+3983F_j
.text:7512F65F          mov     eax, [ebp+var_10]
.text:7512F662          add     eax, [ebp+var_4]          ; *** Integer Overflow Possibility***
.text:7512F665          mov     [ebp+var_10], eax
.text:7512F668
.text:7512F668; CODE XREF: LoadUvsTable(FONTCMAPDESC *,FONTUVSENTRY *,ulong *)+39849_j
.text:7512F668; LoadUvsTable(FONTCMAPDESC *,FONTUVSENTRY *,ulong *)+398AB_j
.text:7512F668          mov     edx, [ebp+var_14]
.text:7512F66B
.text:7512F66B; CODE XREF: LoadUvsTable(FONTCMAPDESC *,FONTUVSENTRY *,ulong *)+397D7_j
.text:7512F66B          mov     ecx, [ebp+var_24]
.text:7512F66E          add     edx, 0Bh
.text:7512F671          mov     eax, [ebp+var_28]
.text:7512F674          inc     ecx
.text:7512F675          add     eax, 0Bh
.text:7512F678          mov     [ebp+var_24], ecx
.text:7512F67B          mov     [ebp+var_28], eax
.text:7512F67E          mov     [ebp+var_14], edx
.text:7512F681          cmp     ecx, edi
.text:7512F683          jb      loc_7512F547

返回时,将计算出的缓冲区大小乘以10,即发生整数溢出错误。这将导致分配的缓冲区过小。

.text:7512EEB9 loc_7512EEB9:             ; CODE XREF: LoadFont(HDC__ *,FACE_CACHE *)+19A_j
.text:7512EEB9          mov     ecx, [edi]
.text:7512EEBB          test    ecx, ecx                        ; calculated buffer size
.text:7512EEBD          jz      loc_750F446A
.text:7512EEC3          lea     eax, [esi+410h]
.text:7512EEC9          push    eax                             ; int
.text:7512EECA          mov     [ebp+var_25C], eax
.text:7512EED0          imul    eax, ecx, 0Ah                   ; *** Integer Overflow ***
.text:7512EED3          push    eax                          ; dwBytes
.text:7512EED4          call    [email protected]               ; UspAllocCache(x,x)

对“ LoadUvsTable()”函数的另一个调用是在传递了尺寸不足的缓冲区以执行UVS表的实际加载的情况下完成的。

.text:7512EEE3          mov     edx, [esi+410h]
.text:7512EEE9          mov     ecx, ebx        ; allocated undersized buffer
.text:7512EEEB          push    edi             ; struct FONTCMAPDESC *
.text:7512EEEC          call   LoadUvsTable(FONTCMAPDESC *,FONTUVSENTRY *,ulong *)

循环用于将数据复制到提供的大小不足的缓冲区,从而导致内存损坏。通过将startUnicodeValue设置为零,可以在需要时退出循环。

.text:7512F600          movzx   eax, byte ptr [edx]
.text:7512F603          shl     eax, 8
.text:7512F606          mov     [ebp+var_8], eax
.text:7512F609          movzx   eax, byte ptr [edx+1]
.text:7512F60D          or      [ebp+var_8], eax
.text:7512F610          shl     [ebp+var_8], 8
.text:7512F614          movzx   eax, byte ptr [edx+2]
.text:7512F618          or      [ebp+var_8], eax
.text:7512F61B          jbe     loc_750F5DDE             ; early exit of loop whenever needed! ;)
.text:7512F621          mov     eax, [ebp+var_10]
.text:7512F624          inc     eax
.text:7512F625          mov     [ebp+var_10], eax
.text:7512F628          cmp     [ebp+var_2C], eax
.text:7512F62B          jb      short loc_7512F698
.text:7512F62D          mov     eax, [ebp+var_C]
.text:7512F630          mov     [eax+4], ecx                    ; *** Corruption ***
.text:7512F633          mov     ecx, [ebp+var_C]
.text:7512F636          mov     eax, [ebp+var_8]
.text:7512F639          add     [ebp+var_C], 0Ah
.text:7512F63D          mov     [ecx], eax                      ; *** Corruption ***
.text:7512F63F          mov     ax, [edx+3]
.text:7512F643          add     edx, 5
.text:7512F646          rol     ax, 8
.text:7512F64A          mov     [ecx+8], ax                     ; *** Corruption ***
.text:7512F64E          mov     eax, [ebp+var_1C]
.text:7512F651          mov     ecx, [ebp+var_20]
.text:7512F654          inc     eax
.text:7512F655          mov     [ebp+var_1C], eax
.text:7512F658          cmp     eax, [ebp+var_4]                ; big value
.text:7512F65B          jb      short loc_7512F600

内存损坏及其控制最终使攻击者有可能破坏运行受影响的Microsoft Windows操作系统版本的系统。

参考文献:

[1] //technet.microsoft.com/library/security/MS16-147

[2] //secunia.com/advisories/70000

[3] //en.wikipedia.org/wiki/Uniscribe

[4] //www.microsoft.com/typography/otspec/cmap.htm

[5] //secunia.com/advisories/74000