Talos Vulnerability Report

TALOS-2015-0129

Microsoft .NET Manifest Resource Information Disclosure Vulnerability

December 8, 2015
CVE Number

CVE-2015-6114

Summary

An exploitable information leak or denial of service vulnerability exists in the manifest resource parsing functionality of the .NET Framework. A specially crafted resource can cause an integer overflow resulting in an out of bounds read which may return arbitrary memory contents or cause the application to unexpectedly terminate. An attacker can supply a corrupted manifest through a .NET or Silverlight assembly, which can be automatically loaded in Internet Explorer or other browsers with the appropriate plugin installed. This vulnerability can be used to crash the program or to return memory contents to assist with bypassing memory hardening mitigations such as ASLR.

Tested Versions

Microsoft Silverlight 5.1.30514.0
Microsoft .NET Framework 4.5.50938

Product URLs

http://www.microsoft.com/

CVSSv3 Score

6.1 - CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H

Details

The .NET Manifest Resources format is undocumented but to learn about it we use one of source mentioned in references [1] [2]. The resource structure is documented in the following way:

 +00 : Signature | needs to equal 0xbeefcace
 +04 : Resource Manager header version
 +08 : Reader Type string length [*]
 +0C : Reader String |
 [*]   : Resource version
 [*]+4 : Number of resources
 [*]+8 : Number of types
 [ types ]
 [align 8]
 [Resources Name hashes]         = Number of resource * DWORD
 [Offsets to resource names]     = Number of resource * DWORD
 [..]  : Data Section offset
 (...)

We will focus our attention on Number of resources field. An attacker can modify this field to corrupt a pointer in the following way.

Vulnerable code appears in ResourceReader class: .net\coreclr\src\mscorlib\src\System\Resources\ResourceReader.cs This class has multipple constructors, in our scenario we are interested in constructors that assign a value to the _ums field. So it can be :

  Line 175        public ResourceReader(Stream stream)

or

  Line 197 internal ResourceReader(Stream stream, Dictionary<String, ResourceLocator> resCache)

Let’s we analyze the way .NET Manifest Resources structure is parsed. Both constructors call internal _ReadResources method:

  Line 870         private void _ReadResources()
          	{
  (...)
  933            _numResources = _store.ReadInt32();
  934            if (_numResources < 0) {
  935                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
  936            }
  (...)

Number of resources is represented by 32bit signed integer variable which value can’t be larger than MAX_INT. Later _numResources is used two times in code to calculate stream offset:

   991             else {
   992                 int seekPos = unchecked(4 * _numResources);
   993                 if (seekPos < 0) {
   994                     throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
   995                 }
   996                 unsafe {
   997                     _nameHashesPtr = (int*)_ums.PositionPointer;
   998                     // Skip over the array of nameHashes.
   999                     _ums.Seek(seekPos, SeekOrigin.Current);
   1000                    // get the position pointer once more to check that the whole table is within the stream
   1001                    byte* junk = _ums.PositionPointer;
   1002                }
   1003            }

You can see the vulnerable code at line 992 where integer overflow appears, but we won’t focus on it right now, just remember that _nameHashesPtr pointer was initialized here and it points to area where Resources Name hashes(DWORDs) should appears. We observe here also that the programmer planned to check whether pointer is assigned within stream boundaries but ultimately nothing is checked.

Second usage of _numResources:

        else {
            int seekPos = unchecked(4 * _numResources);
            if (seekPos < 0) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
            }
            unsafe {
                _namePositionsPtr = (int*)_ums.PositionPointer;
                // Skip over the array of namePositions.
                _ums.Seek(seekPos, SeekOrigin.Current);
                // get the position pointer once more to check that the whole table is within the stream
                byte* junk = _ums.PositionPointer;
            }
        }

This code containts another integer overflow and lack of checking whether _namePositionsPtr points out-of-bounds. As we know already where and how _numResources and _nameHashesPtr is set and that there is lack of sanitization for these variables, we can run test application with malformed .NET resource manifest and see where it crashes.

If application has one .NET resource and it’s major resource e.g MainPage.xaml it will be automatically loaded during application start in components initialization method which will trigger the vulnerability:

 47        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
 48        public void InitializeComponent() {
 49            if (_contentLoaded) {
 50                return;
 51            }
 52            _contentLoaded = true;
 53            System.Windows.Application.LoadComponent(this, new System.Uri("/SilverlightApplication1;component/MainPage.xaml", System.UriKind.Relative));
Crash Analysis
 1:035> r
 eax=40000000 ebx=20000000 ecx=80be126c edx=20000000 esi=00000000 edi=055b5318
 eip=795a8a9c esp=0037ebe8 ebp=0037ebec iopl=0         nv up ei pl nz na po nc
 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210200
 mscorlib_ni+0x348a9c:
 795a8a9c 0fb601          movzx   eax,byte ptr [ecx]         ds:002b:80be126c=??
 1:035> !u eip
 preJIT generated code
 System.Resources.ResourceReader.ReadUnalignedI4(Int32*)
 Begin 795a8a9c, size 1f
 >>> 795a8a9c 0fb601          movzx   eax,byte ptr [ecx]
 795a8a9f 0fb65101        movzx   edx,byte ptr [ecx+1]

Access violation appears in ReadUnalignedI4 method in the following way:

 245        [System.Security.SecurityCritical]  // auto-generated
 246        internal static unsafe int ReadUnalignedI4(int* p)
 247        {
 248            byte* buffer = (byte*)p;
 249            // Unaligned, little endian format
 250            return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
 251        }

To understand where this *p pointer came from and how its value was calculated let’s we take a look at the call stack:

 >!dumpstack

 System.Resources.ResourceReader.ReadUnalignedI4(Int32*))
 System.Resources.ResourceReader.GetNameHash(Int32)),
 System.Resources.ResourceReader.FindPosForResource(System.String))
 System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.Resources.ResourceLocator, mscorlib]].TryGetValue(System.__Canon, System.Resources.ResourceLocator ByRef)),
 System.Resources.RuntimeResourceSet.GetObject(System.String, Boolean, Boolean)),
 System.Resources.RuntimeResourceSet.GetObject(System.String, Boolean)),
 System.Resources.ResourceManager.GetObject(System.String, System.Globalization.CultureInfo, Boolean))
 System.Resources.ResourceManager.GetStream(System.String, System.Globalization.CultureInfo)),
 System.Windows.ResourceManagerWrapper.GetResourceForUri(System.Uri, System.Type)),
 System.Windows.Application.LoadComponent(System.Object, System.Uri)),
 SilverlightApp1.MainPage.InitializeComponent()),
 (...)

We will start our investigation in FindPosForResource method.

 316        internal int FindPosForResource(String name)
 317        {
 318            Contract.Assert(_store != null, "ResourceReader is closed!");
 319            int hash = FastResourceComparer.HashFunction(name);
 320            BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+"  hash: "+hash.ToString("x", CultureInfo.InvariantCulture));
 321            // Binary search over the hashes.  Use the _namePositions array to
 322            // determine where they exist in the underlying stream.
 323            int lo = 0;
 324            int hi = _numResources - 1;
 325            int index = -1;
 326            bool success = false;
 327            while (lo <= hi) {
 328                index = (lo + hi) >> 1;
 329                // Do NOT use subtraction here, since it will wrap for large
 330                // negative numbers.
 331                int currentHash = GetNameHash(index);

This function is searching for a particular resource name using its hash representation on Line 319. Calculated hash is searched for in the array pointed by _nameHashesPtr which is initialized in the _ReadResources method. Index for this array is calculated based on malformed by us _numResources. In this example we modified the original value of _numResources from 1 to 0x40000001 in the manifest. Setting a breakpoint at the beginning of FindPosForResource method we can easy check it out:

 eax=055b52c0 ebx=055b2e0c ecx=055b5318 edx=055b3938 esi=ffffffff edi=0037ec88
 eip=795a8c3c esp=0037ec3c ebp=0037ec98 iopl=0         nv up ei pl nz na po nc
 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
 mscorlib_ni+0x348c3c:
 795a8c3c 55              push    ebp
 1:035> !dumpobj ecx
 Name:        System.Resources.ResourceReader
 MethodTable: 796f446c
 EEClass:     792a6fd4
 Size:        68(0x44) bytes
 File:        C:\Program Files (x86)\Microsoft Silverlight\5.1.30514.0\mscorlib.dll
 Fields:
       MT    Field   Offset                 Type VT     Attr    Value Name
 796f40c0  4000f6e       14 ...m.IO.BinaryReader  0 instance 055b535c _store
 796f42cc  4000f6f       18 ...cator, mscorlib]]  0 instance 055b52e4 _resCache
 796e737c  4000f70        4         System.Int64  1 instance 188 _nameSectionOffset
 796e737c  4000f71        c         System.Int64  1 instance 211 _dataSectionOffset
 796cf828  4000f72       1c       System.Int32[]  0 instance 00000000 _nameHashes
 796f8ae8  4000f73       30                  PTR  0 instance 00be126c _nameHashesPtr
 796cf828  4000f74       20       System.Int32[]  0 instance 00000000 _namePositions
 796f8ae8  4000f75       34                  PTR  0 instance 00be1270 _namePositionsPtr
 796d17d0  4000f76       24      System.Object[]  0 instance 055b5850 _typeTable
 796cf828  4000f77       28       System.Int32[]  0 instance 055b5860 _typeNamePositions
 796e71d4  4000f78       38         System.Int32  1 instance 1073741825 _numResources
 796ee0d4  4000f79       2c ...nagedMemoryStream  0 instance 055b4d04 _ums
 796e71d4  4000f7a       3c         System.Int32  1 instance        2 _version

First index value calculated will be 0x20000000. Further index is passed to GetNameHash function.

 266        [System.Security.SecuritySafeCritical]  // auto-generated
 267        private unsafe int GetNameHash(int index)
 268        {
 269            Contract.Assert(index >=0 && index < _numResources, "Bad index into hash array.  index: "+index);
 270            Contract.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) ||
 271                            (_ums != null && _nameHashes == null && _nameHashesPtr != null), "Internal state mangled.");
 272            if (_ums == null)
 273                return _nameHashes[index];
 274            else
 275                return ReadUnalignedI4(&_nameHashesPtr[index]);
 276        }

According to call stack and _ums value which we can observe on ResourceRader object dump above another call is made to ReadUnalignedI4 method where crash appears. Pointer passed to this method as argument is just pointer to element from _nameHashesPtr table. As we can see index argument is used as index into the name hashes array. In this case the corrupted value will point outside array (Resource section/directory) space.

 1:035> !dumpobj 055b4d04  //dump of _ums object
 Name:        System.IO.UnmanagedMemoryStream
 MethodTable: 796ee0d4
 EEClass:     792a39ac
 Size:        56(0x38) bytes
 File:        C:\Program Files (x86)\Microsoft Silverlight\5.1.30514.0\mscorlib.dll
 Fields:
       MT    Field   Offset                 Type VT     Attr    Value Name
 796f2674  400123d      3c0     System.IO.Stream  0   shared   static Null
     >> Domain:Value  0500a6b0:NotInit  050acb70:NotInit  <<
 796efb84  4001320       24 ...rvices.SafeBuffer  0 instance 00000000 _buffer
 796f8ae8  4001321       28                  PTR  0 instance 00be11bc _mem
 796e737c  4001322        4         System.Int64  1 instance 1954 _length
 796e737c  4001323        c         System.Int64  1 instance 1954 _capacity
 796e737c  4001324       14         System.Int64  1 instance 188 _position
 796e737c  4001325       1c         System.Int64  1 instance 0 _offset
 796cf860  4001326       2c         System.Int32  1 instance        1 _access
 796e6b40  4001327       30       System.Boolean  1 instance        1 _isOpen

00be11bc _mem is a pointer to the beginning of .Net Resources Manifest and the _nameHashesPtr should be constrained within the _mem + _length memory boundary.

 Moment when pointer to hash element is calculated:
 eax=40000000 ebx=20000000 ecx=00be126c edx=20000000 esi=00000000 edi=055b5318
 eip=795a8b26 esp=0037ebec ebp=0037ebec iopl=0         nv up ei pl nz na po nc
 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
 mscorlib_ni+0x348b26:
 795a8b26 8d0c91          lea     ecx,[ecx+edx*4]

Notice that here the index is multiplied by 4 (sizeof of element in array) which is another place for possible overflow.

 1:035> p
 eax=40000000 ebx=20000000 ecx=80be126c edx=20000000 esi=00000000 edi=055b5318
 eip=795a8b29 esp=0037ebec ebp=0037ebec iopl=0         nv up ei pl nz na po nc
 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
 mscorlib_ni+0x348b29:
 795a8b29 e86effffff      call    mscorlib_ni+0x348a9c (795a8a9c)

Finally read access violation appears during read of first byte from calculated pointer:

 1:035> p
 (23e8.2504): Access violation - code c0000005 (first chance)
 First chance exceptions are reported before any exception handling.
 This exception may be expected and handled.
 eax=40000000 ebx=20000000 ecx=80be126c edx=20000000 esi=00000000 edi=055b5318
 eip=795a8a9c esp=0037ebe8 ebp=0037ebec iopl=0         nv up ei pl nz na po nc
 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210202
 mscorlib_ni+0x348a9c:
 795a8a9c 0fb601          movzx   eax,byte ptr [ecx]         ds:002b:80be126c=??

 FAULTING_IP:
 mscorlib_ni+348a9c
 795a8a9c 0fb601          movzx   eax,byte ptr [ecx]

 EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)
 ExceptionAddress: 795a8a9c (mscorlib_ni+0x00348a9c)
    ExceptionCode: c0000005 (Access violation)
   ExceptionFlags: 00000000
 NumberParameters: 2
    Parameter[0]: 00000000
    Parameter[1]: 80be126c
 Attempt to read from address 80be126c

 FAULTING_THREAD:  00002504

 DEFAULT_BUCKET_ID:  WRONG_SYMBOLS

 PROCESS_NAME:  plugin-container.exe

 ERROR_CODE: (NTSTATUS) 0xc0000005 - Instrukcja spod 0x%08lx odwo

 EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - Instrukcja spod 0x%08lx odwo

 EXCEPTION_PARAMETER1:  00000000

 EXCEPTION_PARAMETER2:  80be126c

 READ_ADDRESS:  80be126c

 FOLLOWUP_IP:
 mscorlib_ni+348a9c
 795a8a9c 0fb601          movzx   eax,byte ptr [ecx]

 NTGLOBALFLAG:  470

 APPLICATION_VERIFIER_FLAGS:  0

 APP:  plugin-container.exe

 MANAGED_STACK:
 (TransitionMU)
 0037EBE8 795A8A9C mscorlib_ni!System.Resources.ResourceReader.ReadUnalignedI4(Int32*)
 0037EBEC 795A8B2E mscorlib_ni!System.Resources.ResourceReader.GetNameHash(Int32)+0x22
 0037EBF4 795A8C88 mscorlib_ni!System.Resources.ResourceReader.FindPosForResource(System.String)+0x4c
 0037EC40 795A6774 mscorlib_ni!System.Resources.RuntimeResourceSet.GetObject(System.String, Boolean, Boolean)+0xb8
 0037ECA8 795A646D mscorlib_ni!System.Resources.RuntimeResourceSet.GetObject(System.String, Boolean)+0xd
 0037ECB0 795A3057 mscorlib_ni!System.Resources.ResourceManager.GetObject(System.String, System.Globalization.CultureInfo, Boolean)+0xc7
 0037ECF4 795A3163 mscorlib_ni!System.Resources.ResourceManager.GetStream(System.String, System.Globalization.CultureInfo)+0x13
 0037ED08 56E28AB7 System_Windows_ni!System.Windows.ResourceManagerWrapper.GetResourceForUri(System.Uri, System.Type)+0x197
 0037ED4C 56E0604A System_Windows_ni!System.Windows.Application.LoadComponent(System.Object, System.Uri)+0x17a
 0037ED90 00BF02C6 UNKNOWN!Inplay.Page.InitializeComponent()+0x8e
 0037EDD8 00BF0217 UNKNOWN!Inplay.Page..ctor(System.Collections.Generic.IDictionary`2<System.String,System.String>)+0xc7
 0037EDEC 00BF0126 UNKNOWN!Inplay.Application.Application_Startup(System.Object, System.Windows.StartupEventArgs)+0x5e
 0037EE0C 56E27053 System_Windows_ni!MS.Internal.CoreInvokeHandler.InvokeEventHandler(UInt32, System.Delegate, System.Object, System.Object)+0x3d3
 0037EE38 56E052A9 System_Windows_ni!MS.Internal.JoltHelper.FireEvent(IntPtr, IntPtr, Int32, Int32, System.String, UInt32)+0x38d
 0037EE88 56EA0A59 System_Windows_ni!DomainNeutralILStubClass.IL_STUB_ReversePInvoke(Int32, Int32, Int32, Int32, IntPtr, Int32)+0x61
 (TransitionUM)

 MANAGED_STACK_COMMAND:  _EFN_StackTrace

 LAST_CONTROL_TRANSFER:  from 795a8c88 to 795a8a9c

 PRIMARY_PROBLEM_CLASS:  WRONG_SYMBOLS

 BUGCHECK_STR:  APPLICATION_FAULT_WRONG_SYMBOLS

 STACK_TEXT:
 0037ebe8 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037ebec 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037ebf4 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037ec40 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037eca8 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037ecb0 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037ecf4 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037ed08 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037ed4c 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037ed90 00000000 unknown.dll!Inplay.Page.InitializeComponent+0x8e
 0037edd8 00000000 unknown.dll!Inplay.Page..ctor+0xc7
 0037edec 00000000 unknown.dll!Inplay.Application.Application_Startup+0x5e
 0037ee0c 56e27053 system_windows_ni!MS.Internal.CoreInvokeHandler.InvokeEventHandler+0x3d3
 0037ee38 56e052a9 system_windows_ni!MS.Internal.JoltHelper.FireEvent+0x38d
 0037ee88 56ea0a59 system_windows_ni!DomainNeutralILStubClass.IL_STUB_ReversePInvoke+0x61


 SYMBOL_STACK_INDEX:  0

 SYMBOL_NAME:  unknown.dll!Inplay.Page.InitializeComponent

 FOLLOWUP_NAME:  MachineOwner

 MODULE_NAME: unknown

 IMAGE_NAME:  unknown.dll

 DEBUG_FLR_IMAGE_TIMESTAMP:  0

 STACK_COMMAND:  _EFN_StackTrace ; ** Pseudo Context ** ; kb

 FAILURE_BUCKET_ID:  WRONG_SYMBOLS_c0000005_unknown.dll!Inplay.Page.InitializeComponent

 BUCKET_ID:  APPLICATION_FAULT_WRONG_SYMBOLS_unknown.dll!Inplay.Page.InitializeComponent

 Followup: MachineOwner ---------
References

[1] http://www.codeproject.com/Articles/12096/NET-Manifest-Resources
[2] https://github.com/dotnet/coreclr

Timeline

2015-05-08 - Vendor Disclosure
2015-12-08 - Public Disclosure

Credit

Marcin ‘Icewall’ Noga of Cisco Talos