Add support for webrtc in meterpreter
parent
ab4f62c44e
commit
f66fc15b9e
|
@ -0,0 +1,81 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<script src="webrtc_api.js"> </script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
var channel = 'msfsinn3rtest123';
|
||||
var websocket = new WebSocket('ws://wsnodejs.jit.su:80');
|
||||
|
||||
websocket.onopen = function() {
|
||||
websocket.push(JSON.stringify({
|
||||
open: true,
|
||||
channel: channel
|
||||
}));
|
||||
};
|
||||
|
||||
websocket.push = websocket.send;
|
||||
websocket.send = function(data) {
|
||||
websocket.push(JSON.stringify({
|
||||
data: data,
|
||||
channel: channel
|
||||
}));
|
||||
};
|
||||
|
||||
var peer = new PeerConnection(websocket);
|
||||
peer.onUserFound = function(userid) {
|
||||
console.debug("Found userid: " + userid);
|
||||
getUserMedia(function(stream) {
|
||||
peer.addStream(stream);
|
||||
peer.sendParticipationRequest(userid);
|
||||
});
|
||||
};
|
||||
|
||||
peer.onStreamAdded = function(e) {
|
||||
var video = e.mediaElement;
|
||||
video.setAttribute('width', 600);
|
||||
video.setAttribute('controls', true);
|
||||
document.body.appendChild(video);
|
||||
video.play();
|
||||
};
|
||||
|
||||
peer.onStreamEnded = function(e) {
|
||||
var video = e.mediaElement;
|
||||
if (video) {
|
||||
video.style.opacity = 0;
|
||||
setTimeout(function() {
|
||||
video.parentNode.removeChild(video);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
function getUserMedia(callback) {
|
||||
var hints = {audio:true,video:{
|
||||
optional: [],
|
||||
mandatory: {
|
||||
minWidth: 1280,
|
||||
minHeight: 720,
|
||||
maxWidth: 1920,
|
||||
maxHeight: 1080,
|
||||
minAspectRatio: 1.77
|
||||
}
|
||||
}};
|
||||
|
||||
navigator.getUserMedia(hints,function(stream) {
|
||||
var video = document.createElement('video');
|
||||
video.src = URL.createObjectURL(stream);
|
||||
video.controls = true;
|
||||
video.muted = true;
|
||||
|
||||
peer.onStreamAdded({
|
||||
mediaElement: video,
|
||||
userid: 'self',
|
||||
stream: stream
|
||||
});
|
||||
|
||||
callback(stream);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,373 @@
|
|||
// Muaz Khan - https://github.com/muaz-khan
|
||||
// MIT License - https://www.webrtc-experiment.com/licence/
|
||||
// Documentation - https://github.com/muaz-khan/WebRTC-Experiment/tree/master/websocket
|
||||
|
||||
(function () {
|
||||
|
||||
window.PeerConnection = function (socketURL, userid) {
|
||||
console.debug("Creating connection");
|
||||
this.userid = userid || getToken();
|
||||
this.peers = {};
|
||||
|
||||
if (!socketURL) throw 'Socket-URL is mandatory.';
|
||||
|
||||
new Signaler(this, socketURL);
|
||||
|
||||
this.addStream = function(stream) {
|
||||
this.MediaStream = stream;
|
||||
};
|
||||
};
|
||||
|
||||
function Signaler(root, socketURL) {
|
||||
var self = this;
|
||||
|
||||
root.startBroadcasting = function () {
|
||||
console.debug("broadcasting as: " + root.userid);
|
||||
if(!root.MediaStream) throw 'Offerer must have media stream.';
|
||||
|
||||
(function transmit() {
|
||||
socket.send({
|
||||
userid: root.userid,
|
||||
broadcasting: true
|
||||
});
|
||||
!self.participantFound &&
|
||||
!self.stopBroadcasting &&
|
||||
setTimeout(transmit, 3000);
|
||||
})();
|
||||
};
|
||||
|
||||
root.sendParticipationRequest = function (userid) {
|
||||
console.debug("sending participation request to userid: " + userid);
|
||||
console.debug("root.userid = " + root.userid);
|
||||
socket.send({
|
||||
participationRequest: true,
|
||||
userid: root.userid,
|
||||
to: userid
|
||||
});
|
||||
};
|
||||
|
||||
// if someone shared SDP
|
||||
this.onsdp = function (message) {
|
||||
var sdp = message.sdp;
|
||||
|
||||
if (sdp.type == 'offer') {
|
||||
root.peers[message.userid] = Answer.createAnswer(merge(options, {
|
||||
MediaStream: root.MediaStream,
|
||||
sdp: sdp
|
||||
}));
|
||||
}
|
||||
|
||||
if (sdp.type == 'answer') {
|
||||
root.peers[message.userid].setRemoteDescription(sdp);
|
||||
}
|
||||
};
|
||||
|
||||
root.acceptRequest = function (userid) {
|
||||
console.debug("accepts request");
|
||||
root.peers[userid] = Offer.createOffer(merge(options, {
|
||||
MediaStream: root.MediaStream
|
||||
}));
|
||||
};
|
||||
|
||||
var candidates = [];
|
||||
// if someone shared ICE
|
||||
this.onice = function (message) {
|
||||
var peer = root.peers[message.userid];
|
||||
if (peer) {
|
||||
peer.addIceCandidate(message.candidate);
|
||||
for (var i = 0; i < candidates.length; i++) {
|
||||
peer.addIceCandidate(candidates[i]);
|
||||
}
|
||||
candidates = [];
|
||||
} else candidates.push(candidates);
|
||||
};
|
||||
|
||||
// it is passed over Offer/Answer objects for reusability
|
||||
var options = {
|
||||
onsdp: function (sdp) {
|
||||
socket.send({
|
||||
userid: root.userid,
|
||||
sdp: sdp,
|
||||
to: root.participant
|
||||
});
|
||||
},
|
||||
onicecandidate: function (candidate) {
|
||||
socket.send({
|
||||
userid: root.userid,
|
||||
candidate: candidate,
|
||||
to: root.participant
|
||||
});
|
||||
},
|
||||
onStreamAdded: function (stream) {
|
||||
console.debug('onStreamAdded', '>>>>>>', stream);
|
||||
|
||||
stream.onended = function () {
|
||||
if (root.onStreamEnded) root.onStreamEnded(streamObject);
|
||||
};
|
||||
|
||||
var mediaElement = document.createElement('video');
|
||||
mediaElement.id = root.participant;
|
||||
mediaElement[isFirefox ? 'mozSrcObject' : 'src'] = isFirefox ? stream : window.webkitURL.createObjectURL(stream);
|
||||
mediaElement.autoplay = true;
|
||||
mediaElement.controls = true;
|
||||
mediaElement.play();
|
||||
|
||||
var streamObject = {
|
||||
mediaElement: mediaElement,
|
||||
stream: stream,
|
||||
userid: root.participant,
|
||||
type: 'remote'
|
||||
};
|
||||
|
||||
function afterRemoteStreamStartedFlowing() {
|
||||
if (!root.onStreamAdded) return;
|
||||
root.onStreamAdded(streamObject);
|
||||
}
|
||||
|
||||
afterRemoteStreamStartedFlowing();
|
||||
}
|
||||
};
|
||||
|
||||
function closePeerConnections() {
|
||||
self.stopBroadcasting = true;
|
||||
if (root.MediaStream) root.MediaStream.stop();
|
||||
|
||||
for (var userid in root.peers) {
|
||||
root.peers[userid].peer.close();
|
||||
}
|
||||
root.peers = {};
|
||||
}
|
||||
|
||||
root.close = function () {
|
||||
socket.send({
|
||||
userLeft: true,
|
||||
userid: root.userid,
|
||||
to: root.participant
|
||||
});
|
||||
closePeerConnections();
|
||||
};
|
||||
|
||||
window.onbeforeunload = function () {
|
||||
root.close();
|
||||
};
|
||||
|
||||
window.onkeyup = function (e) {
|
||||
if (e.keyCode == 116)
|
||||
root.close();
|
||||
};
|
||||
|
||||
function onmessage(e) {
|
||||
console.debug("message");
|
||||
var message = JSON.parse(e.data);
|
||||
|
||||
if (message.userid == root.userid) return;
|
||||
root.participant = message.userid;
|
||||
|
||||
// for pretty logging
|
||||
console.debug(JSON.stringify(message, function (key, value) {
|
||||
if (value && value.sdp) {
|
||||
console.log(value.sdp.type, '---', value.sdp.sdp);
|
||||
return '';
|
||||
} else return value;
|
||||
}, '---'));
|
||||
|
||||
// if someone shared SDP
|
||||
if (message.sdp && message.to == root.userid) {
|
||||
self.onsdp(message);
|
||||
}
|
||||
|
||||
// if someone shared ICE
|
||||
if (message.candidate && message.to == root.userid) {
|
||||
self.onice(message);
|
||||
}
|
||||
|
||||
// if someone sent participation request
|
||||
if (message.participationRequest && message.to == root.userid) {
|
||||
console.debug('someone is joining');
|
||||
self.participantFound = true;
|
||||
|
||||
if (root.onParticipationRequest) {
|
||||
root.onParticipationRequest(message.userid);
|
||||
} else root.acceptRequest(message.userid);
|
||||
}
|
||||
|
||||
// if someone is broadcasting himself!
|
||||
if (message.broadcasting && root.onUserFound) {
|
||||
console.debug("someone is broadcasting");
|
||||
root.onUserFound(message.userid);
|
||||
}
|
||||
|
||||
if (message.userLeft && message.to == root.userid) {
|
||||
closePeerConnections();
|
||||
}
|
||||
}
|
||||
|
||||
var socket = socketURL;
|
||||
if(typeof socketURL == 'string') {
|
||||
socket = new WebSocket(socketURL);
|
||||
socket.push = socket.send;
|
||||
socket.send = function (data) {
|
||||
console.debug(data);
|
||||
socket.push(JSON.stringify(data));
|
||||
console.debug("data sent");
|
||||
};
|
||||
|
||||
socket.onopen = function () {
|
||||
console.log('websocket connection opened.');
|
||||
};
|
||||
}
|
||||
socket.onmessage = onmessage;
|
||||
}
|
||||
|
||||
var RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
|
||||
var RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
|
||||
var RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
|
||||
|
||||
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
|
||||
window.URL = window.webkitURL || window.URL;
|
||||
|
||||
var isFirefox = !!navigator.mozGetUserMedia;
|
||||
var isChrome = !!navigator.webkitGetUserMedia;
|
||||
|
||||
var STUN = {
|
||||
url: isChrome ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121'
|
||||
};
|
||||
|
||||
var TURN = {
|
||||
url: 'turn:homeo@turn.bistri.com:80',
|
||||
credential: 'homeo'
|
||||
};
|
||||
|
||||
var iceServers = {
|
||||
iceServers: [STUN]
|
||||
};
|
||||
|
||||
if (isChrome) {
|
||||
if (parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]) >= 28)
|
||||
TURN = {
|
||||
url: 'turn:turn.bistri.com:80',
|
||||
credential: 'homeo',
|
||||
username: 'homeo'
|
||||
};
|
||||
|
||||
iceServers.iceServers = [STUN, TURN];
|
||||
}
|
||||
|
||||
var optionalArgument = {
|
||||
optional: [{
|
||||
DtlsSrtpKeyAgreement: true
|
||||
}]
|
||||
};
|
||||
|
||||
var offerAnswerConstraints = {
|
||||
optional: [],
|
||||
mandatory: {
|
||||
OfferToReceiveAudio: true,
|
||||
OfferToReceiveVideo: true
|
||||
}
|
||||
};
|
||||
|
||||
function getToken() {
|
||||
return Math.round(Math.random() * 9999999999) + 9999999999;
|
||||
}
|
||||
|
||||
function onSdpError() {}
|
||||
|
||||
// var offer = Offer.createOffer(config);
|
||||
// offer.setRemoteDescription(sdp);
|
||||
// offer.addIceCandidate(candidate);
|
||||
var Offer = {
|
||||
createOffer: function (config) {
|
||||
var peer = new RTCPeerConnection(iceServers, optionalArgument);
|
||||
|
||||
if (config.MediaStream) peer.addStream(config.MediaStream);
|
||||
peer.onaddstream = function (event) {
|
||||
config.onStreamAdded(event.stream);
|
||||
};
|
||||
|
||||
peer.onicecandidate = function (event) {
|
||||
if (event.candidate)
|
||||
config.onicecandidate(event.candidate);
|
||||
};
|
||||
|
||||
peer.createOffer(function (sdp) {
|
||||
peer.setLocalDescription(sdp);
|
||||
config.onsdp(sdp);
|
||||
}, onSdpError, offerAnswerConstraints);
|
||||
|
||||
this.peer = peer;
|
||||
|
||||
return this;
|
||||
},
|
||||
setRemoteDescription: function (sdp) {
|
||||
this.peer.setRemoteDescription(new RTCSessionDescription(sdp));
|
||||
},
|
||||
addIceCandidate: function (candidate) {
|
||||
this.peer.addIceCandidate(new RTCIceCandidate({
|
||||
sdpMLineIndex: candidate.sdpMLineIndex,
|
||||
candidate: candidate.candidate
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// var answer = Answer.createAnswer(config);
|
||||
// answer.setRemoteDescription(sdp);
|
||||
// answer.addIceCandidate(candidate);
|
||||
var Answer = {
|
||||
createAnswer: function (config) {
|
||||
var peer = new RTCPeerConnection(iceServers, optionalArgument);
|
||||
|
||||
if (config.MediaStream) peer.addStream(config.MediaStream);
|
||||
peer.onaddstream = function (event) {
|
||||
config.onStreamAdded(event.stream);
|
||||
};
|
||||
|
||||
peer.onicecandidate = function (event) {
|
||||
if (event.candidate)
|
||||
config.onicecandidate(event.candidate);
|
||||
};
|
||||
|
||||
peer.setRemoteDescription(new RTCSessionDescription(config.sdp));
|
||||
peer.createAnswer(function (sdp) {
|
||||
peer.setLocalDescription(sdp);
|
||||
config.onsdp(sdp);
|
||||
}, onSdpError, offerAnswerConstraints);
|
||||
|
||||
this.peer = peer;
|
||||
|
||||
return this;
|
||||
},
|
||||
addIceCandidate: function (candidate) {
|
||||
this.peer.addIceCandidate(new RTCIceCandidate({
|
||||
sdpMLineIndex: candidate.sdpMLineIndex,
|
||||
candidate: candidate.candidate
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
function merge(mergein, mergeto) {
|
||||
for (var t in mergeto) {
|
||||
mergein[t] = mergeto[t];
|
||||
}
|
||||
return mergein;
|
||||
}
|
||||
|
||||
window.URL = window.webkitURL || window.URL;
|
||||
navigator.getMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
||||
navigator.getUserMedia = function(hints, onsuccess, onfailure) {
|
||||
if(!hints) hints = {audio:true,video:true};
|
||||
if(!onsuccess) throw 'Second argument is mandatory. navigator.getUserMedia(hints,onsuccess,onfailure)';
|
||||
|
||||
navigator.getMedia(hints, _onsuccess, _onfailure);
|
||||
|
||||
function _onsuccess(stream) {
|
||||
onsuccess(stream);
|
||||
}
|
||||
|
||||
function _onfailure(e) {
|
||||
if(onfailure) onfailure(e);
|
||||
else throw Error('getUserMedia failed: ' + JSON.stringify(e, null, '\t'));
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
|
@ -0,0 +1,83 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<script src="webrtc_api.js"> </script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
var channel = 'msfsinn3rtest123';
|
||||
var websocket = new WebSocket('ws://wsnodejs.jit.su:80');
|
||||
|
||||
websocket.onopen = function() {
|
||||
websocket.push(JSON.stringify({
|
||||
open: true,
|
||||
channel: channel
|
||||
}));
|
||||
};
|
||||
|
||||
websocket.push = websocket.send;
|
||||
websocket.send = function(data) {
|
||||
websocket.push(JSON.stringify({
|
||||
data: data,
|
||||
channel: channel
|
||||
}));
|
||||
};
|
||||
|
||||
var peer = new PeerConnection(websocket);
|
||||
|
||||
peer.onStreamAdded = function(e) {
|
||||
var video = e.mediaElement;
|
||||
video.setAttribute('width', 600);
|
||||
video.setAttribute('controls', true);
|
||||
document.body.appendChild(video);
|
||||
video.play();
|
||||
};
|
||||
|
||||
peer.onStreamEnded = function(e) {
|
||||
var video = e.mediaElement;
|
||||
if (video) {
|
||||
video.style.opacity = 0;
|
||||
setTimeout(function() {
|
||||
video.parentNode.removeChild(video);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
console.debug("onload");
|
||||
getUserMedia(function(stream) {
|
||||
peer.addStream(stream);
|
||||
console.debug("broadcasting");
|
||||
peer.startBroadcasting();
|
||||
});
|
||||
};
|
||||
|
||||
function getUserMedia(callback) {
|
||||
var hints = {audio:true,video:{
|
||||
optional: [],
|
||||
mandatory: {
|
||||
minWidth: 1280,
|
||||
minHeight: 720,
|
||||
maxWidth: 1920,
|
||||
maxHeight: 1080,
|
||||
minAspectRatio: 1.77
|
||||
}
|
||||
}};
|
||||
|
||||
navigator.getUserMedia(hints,function(stream) {
|
||||
var video = document.createElement('video');
|
||||
video.src = URL.createObjectURL(stream);
|
||||
video.controls = true;
|
||||
video.muted = true;
|
||||
|
||||
peer.onStreamAdded({
|
||||
mediaElement: video,
|
||||
userid: 'self',
|
||||
stream: stream
|
||||
});
|
||||
|
||||
callback(stream);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -47,6 +47,14 @@ class Webcam
|
|||
true
|
||||
end
|
||||
|
||||
def webcam_chat
|
||||
offerer_id = 'sinn3r_offer'
|
||||
remote_browser_path = find_remote_webrtc_browser
|
||||
local_browser_path = find_local_webrtc_browser
|
||||
init_video_chat(local_browser_path, offerer_id)
|
||||
connect_video_chat(offerer_id)
|
||||
end
|
||||
|
||||
# Record from default audio source for +duration+ seconds;
|
||||
# returns a low-quality wav file
|
||||
def record_mic(duration)
|
||||
|
@ -58,6 +66,47 @@ class Webcam
|
|||
|
||||
attr_accessor :client
|
||||
|
||||
|
||||
private
|
||||
|
||||
|
||||
def find_remote_webrtc_browser
|
||||
puts "Looking for a web browser on the target machine that supports WebRTC..."
|
||||
''
|
||||
end
|
||||
|
||||
|
||||
def find_local_webrtc_browser
|
||||
puts "Looking for a web browser on the local machine that supports WebRTC..."
|
||||
''
|
||||
end
|
||||
|
||||
|
||||
def init_video_chat(local_browser_path, offerer_id, httpserver_port=8080)
|
||||
interface = load_interface('offerer.html')
|
||||
api = load_api_code
|
||||
end
|
||||
|
||||
|
||||
def connect_video_chat(offerer_id)
|
||||
interface = load_interface('answerer.html')
|
||||
api = load_api_code
|
||||
end
|
||||
|
||||
def load_interface(html_name)
|
||||
interface_path = File.join(Msf::Config.data_directory, 'webcam', html_name)
|
||||
interface_code = ''
|
||||
File.open(interface_path) { |f| interface_code = f.read }
|
||||
interface_code
|
||||
end
|
||||
|
||||
def load_api_code
|
||||
js_api_path = File.join(Msf::Config.data_directory, 'webcam', 'api.js')
|
||||
api = ''
|
||||
File.open(js_api_path) { |f| api = f.read }
|
||||
api
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end; end; end; end; end; end
|
||||
|
|
|
@ -25,12 +25,14 @@ class Console::CommandDispatcher::Stdapi::Webcam
|
|||
"webcam_list" => "List webcams",
|
||||
"webcam_snap" => "Take a snapshot from the specified webcam",
|
||||
"webcam_stream" => "Play a video stream from the specified webcam",
|
||||
"webcam_chat" => "Start a video chat",
|
||||
"record_mic" => "Record audio from the default microphone for X seconds"
|
||||
}
|
||||
reqs = {
|
||||
"webcam_list" => [ "webcam_list" ],
|
||||
"webcam_snap" => [ "webcam_start", "webcam_get_frame", "webcam_stop" ],
|
||||
"webcam_stream" => [ "webcam_start", "webcam_get_frame", "webcam_stop" ],
|
||||
"webcam_chat" => [ "webcam_list" ],
|
||||
"record_mic" => [ "webcam_audio_record" ],
|
||||
}
|
||||
|
||||
|
@ -129,6 +131,9 @@ class Console::CommandDispatcher::Stdapi::Webcam
|
|||
end
|
||||
end
|
||||
|
||||
def cmd_webcam_chat(*args)
|
||||
client.webcam.webcam_chat
|
||||
end
|
||||
|
||||
def cmd_webcam_stream(*args)
|
||||
print_status("Starting...")
|
||||
|
|
Loading…
Reference in New Issue