Last month I discovered and reported an integer overflow vulnerability in the Windows Registry. Last Tuesday, October 25th, Microsoft released Security Bulletin MS16-124, which contains the patch for this vulnerability, and identifies it as CVE-2016-0070.

This vulnerability could lead to local privilege elevation, and is rated as “Important” by Microsoft. The vulnerability affects multiple Windows versions, and Microsoft has recommended installing this update immediately.

In this blog I will share the details of this vulnerability.

How to Reproduce

To reproduce the vulnerability, follow the steps below.

Sign into Windows 7 with any non-admin account. Run regedit.exe in Command Prompt to open Registry Editor. Choose "HKEY_USERS" from the left pane of Registry Editor. Click menu item "File" => "Load Hive". Choose the PoC file PoC.hiv and open it. In the popup dialog, input any characters in "Key Name" such as "ddd". You will see the Windows 7 Bugcheck blue screen error, as shown in Figure 1.

Figure 1 – Bugcheck Blue Screen

Analysis

This vulnerability is triggered when parsing the crafted file PoC.hiv. So let’s take a look at it first. The screenshot in Figure 2 shows the key file structure of PoC.hiv.

Figure 2 - File Structure of PoC.hiv

As highlighted in the screenshot above, the integer overflow is caused by the crafted value 0x80000001 of ValueCount in the struct NKCELL.

Based on my analysis, the vulnerability exists in the function “CmpCheckKey”. Following is the code snippet of this function.

PAGE:82C10E9B ; int __thiscall CmpCheckKey(int, char, int, int, ULONG_PTR BugCheckParameter3, PRTL_BITMAP BitMapHeader) PAGE:82C10E9B _CmpCheckKey@24 proc near

... PAGE:82C114C0 loc_82C114C0: ; CODE XREF: CmpCheckKey(x,x,x,x,x,x)+60Fj PAGE:82C114C0 mov edx, [ebp+var_14] ; The value of edx is got from the PoC at offset 0x1048 and its value is 0x80000001. PAGE:82C114C3 shl edx, 2 ; edx is multiplied by 4. This results in integer overflow and the value of edx becomes 4. PAGE:82C114C6 cmp edx, 4 PAGE:82C114C9 jb short loc_82C11512 ; The comparison result is false. PAGE:82C114CB mov ecx, [ebp+BugCheckParameter3] PAGE:82C114CE mov eax, esi PAGE:82C114D0 call _HvGetCellSize@8 ; HvGetCellSize(x,x) PAGE:82C114D5 cmp edx, eax ; Compare eax=0xc with edx=4. PAGE:82C114D7 ja short loc_82C11512 ; The comparison result is false. PAGE:82C114D9 lea eax, [ebp+var_C] PAGE:82C114DC push eax PAGE:82C114DD push [ebp+arg_4] PAGE:82C114E0 mov al, [edi+2] PAGE:82C114E3 push [ebp+var_14] ; The crafted ValueCount value 0x80000001 is pushed on stack. PAGE:82C114E6 shr al, 4 PAGE:82C114E9 and al, 1 PAGE:82C114EB push ecx PAGE:82C114EC mov ecx, [ebp+BitMapHeader] PAGE:82C114EF movzx eax, al PAGE:82C114F2 push eax PAGE:82C114F3 push esi PAGE:82C114F4 lea edx, [ebp+var_10] PAGE:82C114F7 call _CmpCheckValueList@32 ; Call this function which causes Windows kernel bugcheck. PAGE:82C114FC test eax, eax …

Following is the code snippet of the function “_CmpCheckValueList@32”.

PAGE:82C11F24 ; __stdcall CmpCheckValueList(x, x, x, x, x, x, x, x) PAGE:82C11F24 _CmpCheckValueList@32 proc near

... PAGE:82C17253 loc_82C17253: ; CODE XREF: CmpCheckValueList(x,x,x,x,x,x,x,x)+32Bj PAGE:82C17253 mov ecx, [ebp+var_44] PAGE:82C17256 cmp [ecx], eax PAGE:82C17258 jnb short loc_82C1725C PAGE:82C1725A mov [ecx], eax PAGE:82C1725C PAGE:82C1725C loc_82C1725C: ; CODE XREF: CmpCheckValueList(x,x,x,x,x,x,x,x)+334j PAGE:82C1725C inc [ebp+var_20] ; The value of [ebp+var_20] becomes 1 in the 1st iteration of the loop. PAGE:82C1725F add [ebp+var_38], 4 PAGE:82C17263 jmp loc_82C16F89 ; Jump to the 2nd iteration of the loop.

... PAGE:82C173FF loc_82C173FF: ; CODE XREF: CmpCheckValueList(x,x,x,x,x,x,x,x)+4D3j PAGE:82C173FF push 0 PAGE:82C17401 push [ebp+arg_10] PAGE:82C17404 mov eax, esi PAGE:82C17406 call _HvMarkCellDirty@12 ; HvMarkCellDirty(x,x,x) PAGE:82C1740B test al, al PAGE:82C1740D jz loc_82C175B8 PAGE:82C17413 push 0 PAGE:82C17415 push dword ptr [edi+28h] PAGE:82C17418 mov eax, esi PAGE:82C1741A call _HvMarkCellDirty@12 ; HvMarkCellDirty(x,x,x) PAGE:82C1741F test al, al PAGE:82C17421 jz loc_82C175B8 PAGE:82C17427 dec dword ptr [edi+24h] PAGE:82C1742A dec [ebp+arg_C] ; [ebp+arg_C] is 0x80000001 which is a parameter passed by caller. Here it decreases 1. PAGE:82C1742D mov eax, [ebp+arg_C] PAGE:82C17430 sub eax, [ebp+var_20] ; [ebp+var_20] is 1 which is got from the 1st iteration of the above loop. Subtract 1 from 0x80000000, the result is 0x7fffffff. PAGE:82C17433 shl eax, 2 ; eax is multiplied by 4, the result is eax = 0xfffffffc. PAGE:82C17436 push eax ; A crafted memcpy size 0xfffffffc is pushed on stack. PAGE:82C17437 mov eax, [ebp+var_38] PAGE:82C1743A lea ecx, [eax+4] PAGE:82C1743D push ecx ; void * PAGE:82C1743E push eax ; void * PAGE:82C1743F call _memmove ; Call this function which results in the heap overflow. PAGE:82C17444 mov esi, [esi+28h] ...

Following is the code snippet of the function “_memmove”.

... .text:82A7C6AB and edx, 3 .text:82A7C6AE cmp ecx, 8 ; switch 8 cases .text:82A7C6B1 jb short loc_82A7C6DC ; .text:82A7C6B3 rep movsd ; Heap overflow occurs here due to the large copy size. .text:82A7C6B5 jmp ds:off_82A7C7CC[edx*4] ...

In summary, this vulnerability is triggered by an integer overflow that results in a heap overflow when performing a memory copy operation. Successful exploitation of this vulnerability could lead to local privilege elevation.