NVIDIA Driver 375.70 - Buffer Overflow in Command Buffer Submission

Blake 15.02.2017 Verified
Denial of Service Exploits Windows

Exploit Code

Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1012

DxgkDdiSubmitCommandVirtual is the function implemented by the kernel mode driver
responsible for submitting a command buffer to the GPU. One of the arguments passed
contains vendor specific data from the user mode driver. The kernel allocates a single
buffer for this purpose for all submit calls for the same context.

NVIDIA implements this data as:

  struct NvPrivateHeader {
    DWORD magic;
    WORD unknown_4;
    WORD unknown_6;
    DWORD unknown_8;
    DWORD size;
  };

  struct NvPrivateData {
    NvPrivateHeader header;
    DWORD unknown_0;
    DWORD unknown_1;
    DWORD some_size;
    DWORD unknown_2;
    PVOID a_gpu_address_maybe;
    BYTE unknown[1220];
  };

In one of the functions that process this data, there appears to be code to
shift around the contents of this user private data.

  // |len| is controlled by the user. can come from the |some_size| field if the
  |a_gpu_address_maybe| field is 0.

  if ( len ) {
    if ( 8 * len >= pCommand_->DmaBufferPrivateDataSize - 0x4E8 )
      do_debug_thingo();                        // doesn't stop the memcpy

    priv_data = (NvSubmitPrivateData *)pCommand_->pDmaBufferPrivateData;

    src = (char *)priv_data + priv_data->header.size;  // unchecked length
    priv_data = (NvSubmitPrivateData *)((char *)priv_data + 1256);
    *(_QWORD *)&v4->unknown_0[256] = priv_data;

    // potential bad memcpy
    memcpy(priv_data, src, 8 * len);
  }


There are two main problems here: the |len| value is checked, but that appears
to only call a debug logging function and not actually stop the memcpy that
occurs afterwards.

Also, the |size| field from the header is not properly
checked to be smaller than the actual size of the data (this is also checked in
the calling function but once again only calls do_debug_thingo()).

This lets an attacker specify an arbitrary length for the copy, as well as
specify an arbitrary 32-bit offset to copy from, leading to pool memory corruption.

Crashing context with PoC (Win 10 x64, driver version 375.95):

PAGE_FAULT_IN_NONPAGED_AREA (50)
...
rax=0000000000000008 rbx=0000000000000000 rcx=ffffb2087fe8f4f0
rdx=0000000041413c59 rsi=0000000000000000 rdi=0000000000000000
rip=fffff8035fc15b00 rsp=ffffd88179edd1a8 rbp=0000000000000080
 r8=00000000020a0a08  r9=0000000000105050 r10=0000000000000000
r11=ffffb2087fe8f4f0 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz na po nc
nvlddmkm+0x5e5b00:
fffff803`5fc15b00 f30f6f040a      movdqu  xmm0,xmmword ptr [rdx+rcx] ds:ffffb208`c12a3149=????????????????????????????????
Resetting default scope


Proof of Concept:
https://github.com/offensive-security/exploit-database-bin-sploits/raw/master/sploits/41365.zip