Microsoft Windows - 'win32k.sys' TTF Processing RCVT TrueType Instruction Handler Out-of-Bounds Read (MS16-120)








We have encountered a number of Windows kernel crashes in the win32k!itrp_GetCVTEntryFast function (called by the handler of the RCVT TrueType instruction) while processing corrupted TTF font files, such as:

Invalid system memory was referenced.  This cannot be protected by try-except,
it must be protected by a Probe.  Typically the address is just plain bad or it
is pointing at freed memory.
Arg1: fb000078, memory referenced.
Arg2: 00000000, value 0 = read operation, 1 = write operation.
Arg3: 8ee70ccb, If non-zero, the instruction address which referenced the bad memory
Arg4: 00000000, (reserved)

Debugging Details:

READ_ADDRESS:  fb000078 Paged session pool

8ee70ccb 8b048a          mov     eax,dword ptr [edx+ecx*4]


IMAGE_NAME:  win32k.sys



FAULTING_MODULE: 8ee20000 win32k



PROCESS_NAME:  csrss.exe


ANALYSIS_VERSION: 6.3.9600.17237 (debuggers(dbg).140716-0327) x86fre

TRAP_FRAME:  897b3568 -- (.trap 0xffffffff897b3568)
ErrCode = 00000000
eax=fafffcdc ebx=00000000 ecx=000000ff edx=fafffc7c esi=fafffe6e edi=00000000
eip=8ee70ccb esp=897b35dc ebp=897b3620 iopl=0         nv up ei pl nz na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010206
8ee70ccb 8b048a          mov     eax,dword ptr [edx+ecx*4] ds:0023:fb000078=????????
Resetting default scope

LAST_CONTROL_TRANSFER:  from 828edd87 to 82889978

897b30bc 828edd87 00000003 7170889f 00000065 nt!RtlpBreakWithStatusInstruction
897b310c 828ee885 00000003 00000000 00000000 nt!KiBugCheckDebugBreak+0x1c
897b34d0 8289c94d 00000050 fb000078 00000000 nt!KeBugCheck2+0x68b
897b3550 8284efa8 00000000 fb000078 00000000 nt!MmAccessFault+0x104
897b3550 8ee70ccb 00000000 fb000078 00000000 nt!KiTrap0E+0xdc
897b35d8 8ee83782 fafffe7b 8ee7bf89 00000001 win32k!itrp_GetCVTEntryFast+0x8
897b35e0 8ee7bf89 00000001 8ee81af3 00000000 win32k!itrp_RCVT+0x63
897b35e8 8ee81af3 00000000 fafffcdc faffff10 win32k!itrp_InnerExecute+0x38
897b3620 8ee7bf89 fafffcdc 8ee7f3b1 fafffd70 win32k!itrp_CALL+0x23b
897b3628 8ee7f3b1 fafffd70 fafffd38 faffff90 win32k!itrp_InnerExecute+0x38
897b36a8 8ee7cee8 fafffec8 faffff10 fafffcdc win32k!itrp_Execute+0x2b2
897b36dc 8ee85d0d fafffcdc 00000000 fa44a298 win32k!itrp_ExecutePrePgm+0x5d
897b36f8 8ee7f67c fa44a51c fafffc7c fa44a2c4 win32k!fsg_RunPreProgram+0x78
897b3758 8ee89385 00000001 897b3774 8ee892dc win32k!fs__Contour+0x1c1
897b3764 8ee892dc fa44a010 fa44a07c 897b3790 win32k!fs_ContourGridFit+0x12
897b3774 8ee89c38 fa44a010 fa44a07c 00000003 win32k!fs_NewContourGridFit+0x10
897b3790 8ee89c79 fc11ae78 00000003 897b37cc win32k!bGetGlyphOutline+0xd7
897b37b8 8ee89e72 fc11ae78 00000003 00000001 win32k!bGetGlyphMetrics+0x20
897b38fc 8ee7ef89 fc11ae78 00000003 897b39ec win32k!lGetGlyphBitmap+0x2b
897b3924 8ee7edd6 00000000 00000001 00000003 win32k!ttfdQueryFontData+0x15e
897b3974 8ee7dff2 fc396010 fc1d0cf0 00000001 win32k!ttfdSemQueryFontData+0x45
897b39bc 8ee7e169 fc396010 fc1d0cf0 00000001 win32k!PDEVOBJ::QueryFontData+0x3e
897b3a30 8ee7bc81 00000002 897b3bdc 00000000 win32k!RFONTOBJ::bInitCache+0xd4
897b3aec 8eef8655 897b3bc8 897b3b94 00000003 win32k!RFONTOBJ::bRealizeFont+0x5df
897b3b98 8eef8890 fc74ad80 00000000 00000002 win32k!RFONTOBJ::bInit+0x2f4
897b3bb0 8ee8f111 897b3bc8 00000000 00000002 win32k!RFONTOBJ::vInit+0x16
897b3bd4 8ee8f262 fc1d0cf0 897b3bf4 0678b8bd win32k!GreGetRealizationInfo+0x2a
897b3c24 8284bdc6 37010587 0459f2cc 0459f2e4 win32k!NtGdiGetRealizationInfo+0x41
897b3c24 77346bf4 37010587 0459f2cc 0459f2e4 nt!KiSystemServicePostCall
0459f2e4 00000000 00000000 00000000 00000000 ntdll!KiFastSystemCallRet

The bugcheck is caused by an attempt to access an out-of-bounds CVT table index (255 in this case, see the ECX register), likely due to a weird behavior of the win32k!itrp_RCVT function, which allows the index to be larger than the size of the array as long as it is smaller than 256. The bug appears to only enable an out-of-bounds read primitive, since at a first glance, the corresponding WCVT instruction handler does not seem to be affected by the same problem. Still, even in its current form, the vulnerability could be used to disclose the contents of adjacent pool allocations to user-mode, potentially leaking sensitive kernel memory or facilitating a KASLR bypass.

The issue reproduces on Windows 7 and 8.1. It is easiest to reproduce with Special Pools enabled for win32k.sys, but it is also possible to observe a crash on a default Windows installation. Just hovering over the proof of concept files or opening them in the default Windows Font Viewer tool should be sufficient to trigger the condition.

Attached is an archive with two proof of concept font files.

Proof of Concept: