Tuesday, February 14, 2017

Attacking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows NVIDIA Driver

Posted by Oliver Chang

Modern graphic drivers are complicated and provide a large promising attack surface for EoPs and sandbox escapes from processes that have access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GPU (e.g. cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Chrome GPU process). In this blog post we’ll take a look at attacking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NVIDIA kernel mode Windows drivers, and a few of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs that I found. I did this research as part of a 20% project with Project Zero, during which a total of 16 vulnerabilities were discovered.

Kernel WDDM interfaces

The kernel mode component of a graphics driver is referred to as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 display miniport driver. Microsoft’s documentation has a nice diagram that summarises cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relationship between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 various components:

IC504961.png

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DriverEntry() for display miniport drivers, a DRIVER_INITIALIZATION_DATA structure is populated with callbacks to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vendor implementations of functions that actually interact with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardware, which is passed to dxgkrnl.sys (DirectX subsystem) via DxgkInitialize(). These callbacks can eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r be called by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DirectX kernel subsystem, or in some cases get called directly from user mode code.

DxgkDdiEscape

A well known entry point for potential vulnerabilities here is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DxgkDdiEscape interface. This can be called straight from user mode, and accepts arbitrary data that is parsed and handled in a vendor specific way (essentially an IOCTL). For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of this post, we’ll use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 term “escape” to denote a particular command that’s supported by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DxgkDdiEscape function.

NVIDIA has a whopping 400~ escapes here at time of writing, so this was where I spent most of my time (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 necessity of many of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se being in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel is questionable):


// (names of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se structs are made up by me)
// Represents a group of escape codes
struct NvEscapeRecord {
 DWORD action_num;
 DWORD expected_magic;
 void *handler_func;
 NvEscapeRecordInfo *info;
 _QWORD num_codes;
};

// Information about a specific escape code.
struct NvEscapeCodeInfo {
 DWORD code;
 DWORD unknown;
 _QWORD expected_size;
 WORD unknown_1;
};

NVIDIA implements cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir private data (pPrivateDriverData in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DXGKARG_ESCAPE struct) for each escape as a header followed by data. The header has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following format:

struct NvEscapeHeader {
 DWORD magic;
 WORD unknown_4;
 WORD unknown_6;
 DWORD size;
 DWORD magic2;
 DWORD code;
 DWORD unknown[7];
};

These escapes are identified by a 32-bit code (first member of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NvEscapeCodeInfo struct above), and are grouped by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir most significant byte (from 1 - 9).

There is some validation being done before each escape code is handled. In particular, each NvEscapeCodeInfo contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 expected size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 escape data following cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 header. This is validated against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NvEscapeHeader, which itself is validated against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PrivateDriverDataSize field given to DxgkDdiEscape. However, it’s possible for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 expected size to be 0 (usually when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 escape data is expected to be variable sized) which means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 escape handler is responsible for doing its own validation. This has led to some bugs (1, 2).

Most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerabilities found (13 in total) in escape handlers were very basic mistakes, such as writing to user provided pointers blindly, disclosing uninitialised kernel memory to user mode, and incorrect bounds checking. There were also numerous issues that I noticed (e.g. OOB reads) that I didn’t report because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y didn’t seem exploitable.

DxgkDdiSubmitBufferVirtual

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r interesting entry point is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DxgkDdiSubmitBufferVirtual function, which is newly introduced in Windows 10 and WDDM 2.0 to support GPU virtual memory (deprecating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old DxgkDdiSubmitBuffer/DxgkDdiRender functions). This function is fairly complicated, and also accepts vendor specific data from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user mode driver for each command submitted. One bug was found here.

Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rs

There are a few ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r WDDM functions that accept vendor-specific data, but nothing of interest were found in those after a quick review.

Exposed devices

NVIDIA also exposes some additional devices that can be opened by any user:

  • \\.\NvAdminDevice which appears to be used for NVAPI. A lot of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ioctl handlers seem to call into DxgkDdiEscape.
  • \\.\UVMLite{Controller,Process*}, likely related to NVIDIA’s “unified memory”. 1 bug was found here.
  • \\.\NvStreamKms, installed by default as part of GeForce Experience, but you can opt out during installation. It’s not exactly clear why this particular driver is necessary. 1 bug was found here also.

More interesting bugs

Most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs I found were by manual reversing and analysis, along with some custom IDA scripts. I also ended up writing a fuzzer, which was surprisingly successful given how simple it was.

While most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs were racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r boring (simple cases of missing validation), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re were a few that were a bit more interesting.

NvStreamKms

This driver registers a process creation notification callback using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PsSetCreateProcessNotifyRoutineEx function. This callback checks if new processes created on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system match image names that were previously set by sending IOCTLs.

This creation notification routine contained a bug:

(Simplified decompiled output)

wchar_t Dst[BUF_SIZE];

...

if ( cur->image_names_count > 0 ) {
 // info_ is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PPS_CREATE_NOTIFY_INFO that is passed to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 routine.
 image_filename = info_->ImageFileName;
 buf = image_filename->Buffer;
 if ( buf ) {
   filename_length = 0i64;
   num_chars = image_filename->Length / 2;
   // Look for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 filename by scanning for backslash.
   if ( num_chars ) {
     while ( buf[num_chars - filename_length - 1] != '\\' ) {
       ++filename_length;
       if ( filename_length >= num_chars )
         goto DO_COPY;
     }
     buf += num_chars - filename_length;
   }
DO_COPY:
   wcscpy_s(Dst, filename_length, buf);
   Dst[filename_length] = 0;
   wcslwr(Dst);

This routines extracts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image name from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ImageFileName member of PS_CREATE_NOTIFY_INFO by searching backwards for backslash (‘\’). This is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n copied to a stack buffer (Dst) using wcscpy_s, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length passed is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calculated name, and not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 destination buffer.

Even though Dst is a fixed size buffer, this isn’t a straightforward overflow. Its size is bigger than 255 wchars, and for most Windows filesystems path components cannot be greater than 255 characters. Scanning for backslash is also valid for most cases because ImageFileName is a canonicalised path.

It is however, possible to pass a UNC path that keeps forward slash (‘/’) as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path separator after being canonicalised (credits to James Forshaw for pointing me to this). This means we can get a filename of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 form “aaa/bbb/ccc/...” and cause an overflow.

For example: CreateProcessW(L"\\\\?\\UNC\\127.0.0.1@8000\\DavWWWRoot\\aaaa/bbbb/cccc/blah.exe", …)

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r interesting note is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wcslwr following cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bad copy doesn’t actually limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only requirement is valid UTF-16). Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calculated filename_length doesn’t include cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 null terminator, wcscpy_s will think that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 destination is too small and will clear cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 destination string by writing a null byte at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beginning (after copying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents up to filename_length bytes first so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow still happens). This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wcslwr is useless because this wcscpy_s call and part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code never worked to begin with.

Exploiting this is trivial, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver is not compiled with stack cookies (hacking like it’s 1999). A local privilege escalation exploit is attached in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original issue that sets up a fake WebDAV server to exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerability (ROP, pivot stack to user buffer, ROP again to allocate rwx mem containing shellcode and jump to it).

Incorrect validation in UVMLiteController

NVIDIA’s driver also exposes a device at \\.\UVMLiteController that can be opened by any user (including from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandboxed Chrome GPU process). The IOCTL handlers for this device write results directly to Irp->UserBuffer, which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output pointer passed to DeviceIoControl (Microsoft’s documentation  says not to do this).The IO control codes specify METHOD_BUFFERED, which means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows kernel checks that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address range provided is writeable by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user before passing it off to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver.

However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se handlers lacked bounds checking for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output buffer, which means that a user mode context could pass a length of 0 with any arbitrary address (which passes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ProbeForWrite check) to result in a limited write-what-where (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “what” here is limited to some specific values: including 32-bit 0xffff, 32-bit 0x1f, 32-bit 0, and 8-bit 0).

A simple privilege escalation exploit is attached in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original issue.

Remote attack vector?

Given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 quantity of bugs that were discovered, I investigated whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r if any of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m can be reached from a completely remote context without having to compromise a sandboxed process first (e.g. through WebGL in a browser, or through video acceleration).

Luckily, this didn’t appear to be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case. This wasn’t too surprising, given that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerable APIs here are very low level and only reached after going through many layers (for Chrome, libANGLE -> Direct3D runtime and user mode driver -> kernel mode driver), and generally called with valid arguments constructed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user mode driver.

NVIDIA’s response

The nature of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs found showed that NVIDIA has a lot of work to do. Their drivers contained a lot of code which probably shouldn’t be in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, and most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs discovered were very basic mistakes. One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir drivers (NvStreamKms.sys) also lacks very basic mitigations (stack cookies) even today.

However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir response was mostly quick and positive. Most bugs were fixed well under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 deadline, and it seems that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y’ve been finding some bugs on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own internally. They also indicated that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y’ve been working on re-architecturing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir kernel drivers for security, but weren’t ready to share any concrete details.

Timeline

2016-07-26
First bug reported to NVIDIA.
2016-09-21
6 of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs reported were fixed silently in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 372.90 release. Discussed patch gap issues with NVIDIA.
2016-10-23
Patch released that includes fix for rest (all 14) of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs that were reported at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time (375.93).
2016-10-28
Public bulletin released, and P0 bugs derestricted.
2016-11-04
Realised that https://bugs.chromium.org/p/project-zero/issues/detail?id=911 wasn’t fixed properly. Notified NVIDIA.
2016-12-14
Fix for issue 911 released along with bulletin.
2017-02-14
Final two bugs fixed.

Patch gap

NVIDIA’s first patch, which included fixes to 6 of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs I reported, did not include a public bulletin (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 release notes mention “security updates”). They had planned to release public details a month after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 patch is released. We noticed this, and let cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m know that we didn’t consider this to be good practice as an attacker can reverse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 patch to find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerabilities before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public is made aware of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 details given this large window.

While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 6 bugs fixed did not have details released for more than 30 days, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remaining 8 at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time had a patch released 5 days before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first bulletin was released. It looks like NVIDIA has been trying to reduce this gap, but based on recent bulletins it appears to be inconsistent.

Conclusion

Given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 large attack surface exposed by graphics drivers in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 generally lower quality of third party code, it appears to be a very rich target for finding sandbox escapes and EoP vulnerabilities. GPU vendors should try to limit this by moving as much attack surface as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel.