Microsoft Visual Studio CVE-2018-1037- Heap Memory Disclosure

Authors:Google Security Research   Risk:High

CVE:CVE-2018-1037                 0day:Heap Memory Disclosure

0day-id:0DAY-1037                  Date:2018-04-18

Description

The Microsoft compiler mspdbcore.dll suffers from a heap memory disclosure into output .pdb files. This affects Microsoft Symbol Server.

Describes

Wikipedia describes the PDB file format as follows [1]:

— cut —
Program database (PDB) is a proprietary file format (developed by Microsoft) for storing debugging information about a program (or, commonly, program modules such as a DLL or EXE). PDB files commonly have a .pdb extension. A PDB file is typically created from source files during compilation.
— cut —

Internally, the Microsoft build environment (Visual Studio etc.) uses mspdbcore.dll to generate .pdb files corresponding to the output executables created during compilation. Parts of that library were made open-source by Microsoft in the microsoft-pdb GitHub project [2]. This makes it easier to read and understand portions of code relevant to the vulnerability discussed here.

We have discovered that under certain circumstances, the library may disclose 3kB (0xC00 bytes) of uninitialized heap memory at offset 0x400 of the output PDB file, in one continuous chunk. We have reproduced the behavior with Visual Studio 2015 and version 14.0.24210.0 of the mspdbcore.dll file.

The source file we’re mostly interested in is msf.cpp [3]. It includes the declaration of a MSF_HB class, which is a container for the overall PDB. It stores the structures corresponding to the file header in the following union:

--- cut ---
  1015      union {
  1016          MSF_HDR     hdr;
  1017          BIGMSF_HDR  bighdr;
  1018      };
--- cut ---

Both MSF_HDR and BIGMSF_HDR structures contain a .cbPg field, which indicates the size of the smallest unit of data in the PDB file, a “page” (the header itself is also a page):

--- cut ---
   933  union MSF_HDR { // page 0
   934      struct {
   ...
   936          CB  cbPg;       // page size
   ...
   942      };
   943      PG pg;
   944  };
   945  
   946  union BIGMSF_HDR {  // page 0 (and more if necessary)
   947      struct {
   ...
   949          CB  cbPg;               // page size
   ...
   955      };
   956      PG  pg;
   957  };
--- cut ---

Two most typical page sizes are 0x400 (1024) and 0x1000 (4096). When a PDB file is created for the first time, the MSF_HB::afterCreate method is called to initialize the internal structures. The prologue of the method body is as follows:

--- cut ---
  1662  BOOL MSF_HB::afterCreate(MSF_EC* pec, CB cbPage) {
  1663      // init hdr; when creating a new MSF, always create the BigMsf variant.
  1664      memset(&bighdr, 0, sizeof bighdr);
  1665      memcpy(&bighdr.szMagic, szBigHdrMagic, sizeof szBigHdrMagic);
  1666      bighdr.cbPg = cbPage;
--- cut ---

We can see that the “bighdr” structure (of size 0x1000) is zero’ed out at the very beginning. This means that regardless of whether the page size ends up being 0x400 or 0x1000 (as specified by the function caller), no uninitialized data prevails in the internal structures. In our test environment, cbPage is set to 0x1000 at that point of execution.

However, when a PDB file already exists on disk and is only updated, the MSF_HB::afterOpen method is called instead. It starts with the following code:

--- cut ---
  1519  BOOL MSF_HB::afterOpen( MSF_EC* pec ) {
  1520                                  // VSWhidbey:600553
  1521      fBigMsf = true;             // This is arbitrary, and will be overwritten in fValidHdr().
  1522                                  // We do this to avoid uninitialized reads of this variable in pnMac().
  1523      pnMac(1);                   // extantPn(pnHdr) must be TRUE for first readPn()!
  1524      msfparms = rgmsfparms[0];   // need min page size set here for initial read.
  1525  
  1526      if (!readPn(pnHdr, &hdr)) {
  1527          if (pec) {
  1528              *pec = MSF_EC_FILE_SYSTEM;
  1529          }
  1530          pIStream = NULL;
  1531          return FALSE;
  1532      }
--- cut ---

The rgmsfparms array is defined as follows:

--- cut ---
   150  const CB    cbPgMax     = 0x1000;
   151  #ifdef SMALLPAGES
   152  const CB    cbPgMin     = 0x200;
   153  #else
   154  const CB    cbPgMin     = 0x400;
   155  #endif
   ...
   196  const MSFParms  rgmsfparms[] = {
   197  #ifdef SMALLPAGES
   198      MSF_PARMS(1024, 10, pnMaxMax, 8, 8),    // gives 64meg
   199  #else
   200      MSF_PARMS(cbPgMin, 10, pnMaxMax, 8, 8), // gives 64meg (??)
   201  #endif
   202      MSF_PARMS(2048,    11, pnMaxMax, 4, 4), // gives 128meg
   203      MSF_PARMS(cbPgMax, 12, 0x7fff,   2, 1)  // gives 128meg
   204  };
--- cut ---

This means that MSF_HB::afterOpen only loads the minimum number of header bytes (0x400) into the “hdr” structure. If the actual page size of the file is 0x1000 (as it is in our case), then the trailing 0xC00 bytes remain uninitialized, and are written that way back to the PDB file on disk. Considering that the MSF_HB object is allocated from the process heap (the container process is mspdbsrv.exe) and not pre-initialized, the leaked bytes may contain various leftover chunks of data used by the process in the past. The disclosure can be easily confirmed by enabling the Page Heap mechanism in Application Verifier for the mspdbsrv.exe process and observing a repeated pattern of 3072 marker bytes at offset 1024 of the output file.

A disclosure of this kind wouldn’t be typically very severe considering that .pdb files aren’t frequently exchanged over the Internet. However, it turns out that some PDBs generated by Microsoft for their Windows components, and shared with developers via the Microsoft Symbol Server or pre-built symbol packages, also contain uninitialized memory from internal Microsoft build servers. Based on some brief experimentation, we have found that around 0.5% official Microsoft PDBs for Windows 10 are affected by this problem. It’s unclear what the total amount of leaked memory is, as we don’t know how many unique symbol files are served by Microsoft. Symbols for systems prior to Windows 10 do not seem to be affected, which suggests that the vulnerability has been introduced in a recent version of the mspdbcore library.

In order to estimate the scope of the disclosure, we have analyzed all 18 symbol packages available for various Windows 10 versions at the official Windows Symbol Packages website [4]. It is easy to determine if a particular .pdb file leaks uninitialized memory, by examining the .cbPg field at offset 0x20 of the file and checking if it is equal to 0x1000. Below is a summary of our findings:

+------------------------------------------------------------+-------------+-----------------+------------+----------------------------+
|                     Symbol Package Set                     | Files total | Files with leak | Percentage | Amount of disclosed memory |
+------------------------------------------------------------+-------------+-----------------+------------+----------------------------+
| Windows 10 a July 2015                                     |       30807 |             152 | 0.49%      | 456 kB                     |
| Windows 10 a November 2015                                 |       31712 |             152 | 0.48%      | 456 kB                     |
| Windows 10 a March 2016                                    |       16138 |              78 | 0.48%      | 234 kB                     |
| Windows 10 and Windows Server 2016 a August 2016           |       16238 |              76 | 0.47%      | 228 kB                     |
| Windows 10 a September 2016                                |       16174 |              76 | 0.47%      | 228 kB                     |
| Windows 10 and Windows Server 2016 a April 2017            |       16755 |              76 | 0.45%      | 228 kB                     |
| Windows 10 and Windows Server, version 1709 a October 2017 |       17062 |              78 | 0.46%      | 234 kB                     |
| Total                                                      |      144886 |             688 | 0.47%      | 2064 kB (2.02 MB)          |
+------------------------------------------------------------+-------------+-----------------+------------+----------------------------+

Across all Windows 10 symbols available from the website, there is a little over 2 MB of uninitialized memory disclosed due to the vulnerability. The leaks were found in the following 40 symbol files:

1  appxdeploymentclient.pdb
     2  authbroker.pdb
     3  biwinrt.pdb
     4  combase.pdb
     5  cryptowinrt.pdb
     6  dllhst3g.pdb
     7  mbaeapipublic.pdb
     8  mbsmsapi.pdb
     9  mbussdapi.pdb
    10  msvideodsp.pdb
    11  msxml6.pdb
    12  nfccx.pdb
    13  ole32.pdb
    14  playtomanager.pdb
    15  provcore.pdb
    16  rtmediaframe.pdb
    17  urlmon.pdb
    18  uxtheme.pdb
    19  vaultcli.pdb
    20  webcamui.pdb
    21  windows.applicationmodel.background.systemeventsbroker.pdb
    22  windows.applicationmodel.background.timebroker.pdb
    23  windows.applicationmodel.pdb
    24  windows.devices.enumeration.pdb
    25  windows.devices.portable.pdb
    26  windows.devices.sensors.pdb
    27  windows.globalization.fontgroups.pdb
    28  windows.graphics.pdb
    29  windows.media.streaming.pdb
    30  windows.networking.backgroundtransfer.pdb
    31  windows.networking.pdb
    32  windows.storage.applicationdata.pdb
    33  windows.storage.compression.pdb
    34  windows.ui.input.inking.pdb
    35  windows.ui.pdb
    36  windows.ui.xaml.pdb
    37  windows.web.pdb
    38  wintypes.pdb
    39  wpnapps.pdb
    40  wwaapi.pdb

In many cases, the uninitialized blobs consist of all zeros, or otherwise non-interesting binary data. However, some of them contain textual strings such as environment variables on Microsoft build servers, which in turn disclose local paths, SMB paths, domain names, command line flags and other information about the compilation environment. We have provided binary dumps of all uninitialized data we have extracted from the symbols to the vendor.

References

[1] <a href=”https://en.wikipedia.org/wiki/Program_database” title=”” class=”” rel=”nofollow”>https://en.wikipedia.org/wiki/Program_database</a>
[2] <a href=”https://github.com/Microsoft/microsoft-pdb” title=”” class=”” rel=”nofollow”>https://github.com/Microsoft/microsoft-pdb</a>
[3] <a href=”https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp” title=”” class=”” rel=”nofollow”>https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp</a>
[4] <a href=”https://developer.microsoft.com/en-us/windows/hardware/download-symbols” title=”” class=”” rel=”nofollow”>https://developer.microsoft.com/en-us/windows/hardware/download-symbols</a>

Leave a Reply