#Exploit Title: MSI NTIOLib.sys, WinIO.sys local privilege escalation
#Date: 2016-09-26
#Exploit Author: ReWolf
#Vendor Homepage: http://www.msi.com
#Version: too many
#Tested on: Windows 10 x64 (TH2, RS1)

Full description: http://blog.rewolf.pl/blog/?p=1630
Exploit github repo: https://github.com/rwfpl/rewolf-msi-exploit

EDB PoC Mirror:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40426.zip

NTIOLib.sys is installed with a few different MSI utilities that are part of the software package for MSI motherboards and graphic cards. WinIO.sys is completely different driver and is installed with Dragon Gaming Center application, which is part of the software package for MSI notebooks. Since both drivers expose physical memory access to the unprivileged users, I decided to put it into one report (I’ll describe the technical differences later). Actually when I was verifying list of affected software, I’ve found third driver that is doing exactly the same thing, just have a bit different interface and name (RTCore32.sys / RTCore64.sys).

Affected software:

    NTIOLib.sys / NTIOLib_X64.sys
        MSI FastBoot
        MSI Command Center
        MSI Live Update
        MSI Gaming APP
        MSI Super Charger
        MSI Dragon Center
    WinIO.sys / WinIO64.sys
        MSI Dragon Gaming Center
        MSI Dragon Center
    RTCore32.sys / RTCore64.sys
        MSI Afterburner

NTIOLib functionality exposed through IOCTLs:

    read/write physical memory (using MmMapIoSpace)
    read write MSR registers (using rdmsr/wrmsr opcodes)
    read PMC register (using rdpmc opcode)
    in/out port operations
    HalGetBusDataByOffset / HalSetBusDataByOffset

WinIO functionality exposed through IOCTLs:

    read/write physical memory (ZwMapViewOfSection of “\\Device\\PhysicalMemory”)
    in/out port operations

RTCore functionality exposed through IOCTLs:

    read/write physical memory (ZwMapViewOfSection of “\\Device\\PhysicalMemory”)
    read write MSR registers (using rdmsr/wrmsr opcodes)
    in/out port operations
    HalGetBusDataByOffset / HalSetBusDataByOffset

It appears that RTCore driver is kind of hybrid between NTIOLib and WinIO. It’s also worth noting that WinIO driver is just compiled (and signed by MSI) version of the code that can be found here: http://www.internals.com/utilities_main.htm.

UPDATE: RTCore driver is part of RivaTuner software, so all OEM branded RivaTuner clones are vulnerable (https://twitter.com/equilibriumuk/status/780367990160326656).

Some of the mentioned applications load vulnerable driver on demand, but some of them loads the driver with service startup and keeps it loaded for the whole time, thus exploitation is rather trivial. I haven’t thoroughly inspected all MSI applications, since it’s not really possible (different version of the software for different hardware, multiple installers etc), so it’s very probable that my list doesn’t cover all cases. Generally if someone owns any MSI hardware, it’s good to check if any of above drivers (or with similar name) is loaded, and if yes, just remove the application that installed it.

Disclosure timeline:
30.05.2016 	sent e-mail notification to the addresses: security@msi.com, secure@msi.com, bugs@msi.com (none of those is valid, but it was worth trying)
31.05.2016 – 03.06.2016 	tried reporting through official support channel, without any luck, final reply:

    Please don’t worry about it and the software files are secure.Anyway,we will send the information to relative department.Thanks!

03.06.2016 	tried contact through a friend from security team of some super-secret big corporation – also without luck
26.09.2016 	full disclosure

Technical details & PoC

After ASMMAP disclosure, I’ve read that the exploitation of this kind of vulnerability is rather easy:

    This can be done by scanning for EPROCESS structures within memory and identifying one, then jumping through the linked list to find your target process and a known SYSTEM process (e.g. lsass), then duplicating the Token field across to elevate your process. This part isn’t really that novel or interesting, so I won’t go into it here.

Since I don’t have much experience in this area, I decided to try above method and see if the exploitation is really straightforward. I’ve started randomly poking with physical pages, just to see how it behaves. My first observation was, that the WinIO driver is a lot more stable than NTIOLib, it probably stems from the method that is used to expose physical memory to the user application (MmMapIoSpace vs ZwMapViewOfSection). NTIOLib tends to BSODs sometimes, especially if the accessed addresses are random (aligned to the 0x1000). My second observation was, that NTIOLib becomes quite stable if the memory is accessed sequentially (page by page). This is actually good, because EPROCESS search is sequential activity.

EPROCESS structures are allocated with Proc pool tag, this is the first indicator that EPROCESS search algorithm will look for. Each memory chunk starts with POOL_HEADER structure, followed by a few OBJECT_HEADER_xxx_INFO structures and finally by the OBJECT_HEADER. OBJECT_HEADER.Body is the actual EPROCESS. More details can be found in Uninformed Journal or in WRK (ObpAllocateObject, \wrk\base\ntos\ob\obcreate.c). On Windows 10 x64 (TH2, RS1) all those structures sums up to 0x80 bytes. To successfully execute local privilege escalation, I need to locate EPROCESS structure of 2 processes. One will be some system process and the second should be the process that privileges are supposed to be escalated. For system process I chose wininit.exe, and the escalated process will be the current process. Having names and PIDs of chosen processes, exploit can proceed to final EPROCESS verification (checks of UniqueProcessId and ImageFileName fields).

With above information it is possible to test initial exploit – it is very slow, so slow that I haven’t wait till it finish. The slowdown comes from accessing addresses that are reserved for hardware IO devices. Those reserved memory ranges will vary from one machine to another, so it’s required to find them out and skip during EPROCESS search. The easiest method to get those ranges is calling NtQuerySystemInformation with SuperfetchInformationClass (http://www.alex-ionescu.com/?p=51), however this call requires elevation, so it has no use in this case. Second place where this information can be obtained is WMI (CIMV2, Win32_DeviceMemoryAddress). This method is not as accurate as SuperfetchInformationClass, but I decided to use it in my PoC. Information returned on VMware test system were 100% accurate, and the slowdown disappeared, however I was still experiencing slowdown on my host machine. I come up with really simple and ugly solution: I’ve added hardcoded <0xF0000000-0xFFFFFFFF> region to the ranges returned from WMI. At this point PoC successfully runs on both VMware test machine (Win10 x64 TH2) and my host machine (Win10 x64 RS1):

  Whoami: secret\user
  Found wininit.exe PID: 000002D8
  Looking for wininit.exe EPROCESS...
  EPROCESS: wininit.exe, token: FFFF8A06105A006B, PID: 2D8
  Stealing token...
  Stolen token: FFFF8A06105A006B
  Looking for MsiExploit.exe EPROCESS...
  EPROCESS: MsiExploit.exe, token: FFFF8A0642E3B957, PID: CAA8
  Reusing token...
  Whoami: nt authority\system

Over-engineered version of PoC can be found on github (Visual Studio 2015 recommended):

https://github.com/rwfpl/rewolf-msi-exploit

It has hard-coded EPROCESS field offsets, so it only works on Win10 x64 TH2/RS1. PoC should work with any version of NTIOLib and WinIO drivers. I haven’t fully analyzed RTCore interface due to the fact, that I found it just today, so obviously it is not included in PoC.