Advisory

Yorick Koster, May 2015

Integer overflow in .NET Framework System.DirectoryServices.Protocols.Utility class

Abstract

An integer overflow exists in the System.DirectoryServices.Protocols.Utility class of the .NET Framework. Triggering this issue results in an overflown integer that is used to allocate a buffer on the heap that is too small, resulting in memory corruption.

See also

- CVE-2015-2504
- MS15-101: Vulnerabilities in .NET Framework Could Allow Elevation of Privilege (3089662)

Affected versions

This issue affects .NET Framework version 4.5 and 4.6. Other versions are not affected as this issue can only be triggered using large arrays (> 2GB).

Fix

Microsoft released MS15-101 that addresses this issue.

Introduction

The System.DirectoryServices.Protocols namespace provides the methods defined in the Lightweight Directory Access Protocol (LDAP) version 3 (V3) and Directory Services Markup Language (DSML) version 2.0 (V2) standards.

System.DirectoryServices.Protocols.dll is located in the Global Assembly Cache (GAC). As a result the Assembly is trusted by the .NET Framework and will run with Full Trust permissions. In addition, the Assembly is compiled with the AllowPartiallyTrustedCallers attribute, which allows it to be used from Assemblies running with Partial Trust permissions.

/advisory/SFY20150501/System.DirectoryServices.Protocols.dll.png
Figure 1: AllowPartiallyTrustedCallers set in System.DirectoryServices.Protocols.dll

Details

An integer overflow exists in AllocHGlobalIntPtrArray() method of the System.DirectoryServices.Protocols.Utility class. This methods accepts a size parameter that is multiplied with the size of IntPtr. No check is performed to determine whether this multiplication overflows. This can result in the allocation of a heap buffer that is too small.

internal static IntPtr AllocHGlobalIntPtrArray(int size)
{
   IntPtr num1 = (IntPtr) 0;
   IntPtr num2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof (IntPtr)) * size);
   for (int index = 0; index < size; ++index)
      Marshal.WriteIntPtr((IntPtr) ((long) num2 + (long) (Marshal.SizeOf(typeof (IntPtr)) * index)), IntPtr.Zero);
   return num2;
}

Since this is an internal class, it cannot be called directly. However it is exposed in various classes located in the System.DirectoryServices.Protocols namespace.

The following proof of concept triggers the integer overflow, resulting in a crash of the application. The Platform target should be set to x64 and gcAllowVeryLargeObjects must be enabled.

static class Crash
{
   [STAThread]
   static void Main()
   {
      Object[] value = new Object[1] { new byte[0x1FFFFFFF][] };
      System.DirectoryServices.Protocols.BerConverter.Encode("V", value);
   }
}

This issue appears difficult to exploit. First of all it can only be triggered when the application is running on a 64-bit platform and support for large arrays is enabled. In addition, after the buffer is allocated it is zeroized using the user-supplied length.

Successful exploitation could result in an application running with Partial Trust permissions to break from the CLR sandbox and run arbitrary code with Full Trust permissions.

Work with us →