New issue
Advanced search Search tips
Starred by 1 user
Status: Fixed
Owner:
Closed: Sep 2016
Cc:



Sign in to add a comment
VMWare Workstation vprintproxy.exe double-free in the handling of EMF (EMR_SMALLTEXTOUT record)
Project Member Reported by mjurczyk@google.com, Jun 15 2016 Back to list
As already discussed in a number of reports in this tracker (#285, #286, #287, #288, #289, #292), VMware Workstation (current version 12.1.1 build-3770994) ships with a feature called "Virtual Printers", which enables the virtualized operating systems to access printers installed on the Host. Inside the VM, the communication takes place through a COM1 device, and the incoming data is handled by a dedicated "vprintproxy.exe" process on the Host, as launched by the "vmware-vmx.exe" service. Administrative privileges are not required to access COM1 in the guest, at least on Windows.

The vprintproxy.exe is a significant attack surface for potential VM escapes. Due to its nature, the application implements support for a variety of complex protocols and file formats, such as the printing protocol, EMFSPOOL format, and further embedded EMFs, fonts, images etc. This report addresses a bug in the handling of EMF files in the TPView.DLL library extensively used by vprintproxy.exe.

The version of the TPView.DLL file referenced in this report is 9.4.1045.1 (md5sum b6211e8b5c2883fa16231b0a6bf014f3).

At address 0x10038AF0, there is a function called CEMF::EnhMetaFileProc, which is used as a custom callback to the EnumEnhMetaFile API. It is responsible for the special handling of several chosen EMF records, such as EMR_SMALLTEXTOUT, EMR_EXTCREATEFONTINDIRECTW, EMR_EXTTEXTOUTA or EMR_EXTTEXTOUTW. The double-free bug resides in a nested function processing EMR_SMALLTEXTOUT records, located at address 0x100391B0. The epilogue of the function, as generated by Hex-Rays, is as follows:

--- cut ---
  if ( v12 & 0x100 && !(v12 & 4) || *(a3 + 44) )
  {
    *a4 = v8;
    v8 = 0;
    v14 = 1;
  }
  else
  {
    v17 = *(v8 + 4);
    v16 = v8 + 52;
    v13 = *(v8 + 2);
    if ( v12 & 0x200 )
    {
      ExtTextOutA(hdc, v13, *(v8 + 3), a6 & v12, 0, v16, v17, 0);
      free(v8);                                                          <========== Free #1
      v14 = 0;
    }
    else
    {
      ExtTextOutW(hdc, v13, *(v8 + 3), a6 & v12, 0, v16, v17, 0);
      free(v8);                                                          <========== Free #1
      v14 = 0;
    }
  }
  free(v8);                                                              <========== Free #2
  return v14;
}
--- cut ---

On the above listing, it is clearly visible that if a conditional branch is taken to either of the ExtTextOut API calls, the buffer under "v8" is freed twice -- once directly after the calls, and then at the end of the function. The behavior is also easily confirmed in the assembly view:

--- cut ---
.text:100392BA                 push    eax             ; options
.text:100392BB                 mov     eax, [edi+0Ch]
.text:100392BE                 push    eax             ; y
.text:100392BF                 push    ecx             ; x
.text:100392C0                 push    edx             ; hdc
.text:100392C1                 call    ds:ExtTextOutA
.text:100392C7                 push    edi             ; void *
.text:100392C8                 call    _free                             <========== Free #1
.text:100392CD                 add     esp, 4
.text:100392D0                 xor     esi, esi
.text:100392D2                 jmp     short loc_10039349
.text:100392D4 ; ---------------------------------------------------------------------------
.text:100392D4
.text:100392D4 loc_100392D4:                           ; CODE XREF: Handle_EMR_SMALLTEXTOUT+105j
.text:100392D4                 and     eax, [ebp+arg_C]
.text:100392D7                 push    eax             ; options
.text:100392D8                 mov     eax, [edi+0Ch]
.text:100392DB                 push    eax             ; y
.text:100392DC                 push    ecx             ; x
.text:100392DD                 push    edx             ; hdc
.text:100392DE                 call    ds:ExtTextOutW
.text:100392E4                 push    edi             ; void *
.text:100392E5                 call    _free                             <========== Free #1
.text:100392EA                 add     esp, 4
.text:100392ED                 xor     esi, esi
.text:100392EF                 jmp     short loc_10039349
...
.text:10039349 loc_10039349:                           ; CODE XREF: Handle_EMR_SMALLTEXTOUT+122j
.text:10039349                                         ; Handle_EMR_SMALLTEXTOUT+13Fj ...
.text:10039349                 push    edi             ; void *
.text:1003934A                 call    _free                             <========== Free #2
--- cut ---

Unfortunately, in order to reach the vulnerable condition, the "v12 & 0x100 && !(v12 & 4) || *(a3 + 44)" condition must evaluate to false. During some brief experimentation with Kostya's exploit from  bug #287 , I was unable to set the value at *(a3 + 44) to zero, thus failing to direct code execution to the vulnerable code area (performing the first free()). However, since this is such an obvious bug, and may be triggerable under the right configuration or with the right interaction with the Printer Proxy, I am still reporting it without a working proof of concept to demonstrate a crash. 

This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.

 
Project Member Comment 1 by mjurczyk@google.com, Sep 14 2016
Labels: CVE-2016-7082 Fixed-2016-Sep-13
Status: Fixed
Fixed in http://www.vmware.com/security/advisories/VMSA-2016-0014.html.
Project Member Comment 2 by mjurczyk@google.com, Sep 19 2016
Labels: -Restrict-View-Commit
Sign in to add a comment