Finished commenting BeEF core. From this point on each developer should be commenting their own core changes

git-svn-id: https://beef.googlecode.com/svn/trunk@1362 b87d56ec-f9c0-11de-8c8a-61c5e9addfc9
This commit is contained in:
passbe
2011-10-15 03:59:24 +00:00
parent e22332e1f8
commit 65b8652f26
23 changed files with 253 additions and 329 deletions

View File

@@ -13,42 +13,38 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
#
# This module contains a list of utils functions to use
# when writing commands.
#
# @note This module contains a list of utils functions to use when writing commands
module CommandUtils
# Format a string to support multiline in javascript.
# @param [String] text String to convert
# @return [String] Formatted string
def format_multiline(text); text.gsub(/\n/, '\n'); end
end
#
# The Command Module Context is being used when evaluating code in eruby.
# In other words, we use that code to add funky functions to the
# javascript templates of our commands.
#
# @note The Command Module Context is being used when evaluating code in eruby.
# In other words, we use that code to add funky functions to the
# javascript templates of our commands.
class CommandContext < Erubis::Context
include BeEF::Core::CommandUtils
# Constructor
# @param [Hash] hash
def initialize(hash=nil);
super(hash);
end
end
#
# This class is the base class for all command modules in the framework.
#
# Two instances of this object are created during the execution of command module.
#
# @note This class is the base class for all command modules in the framework.
# Two instances of this object are created during the execution of command module.
class Command
attr_reader :datastore, :path, :default_command_url, :beefjs_components, :friendlyname
@@ -59,6 +55,7 @@ module Core
include BeEF::Core::Constants::CommandModule
# Super class controller
# @param [String] key command module key
def initialize(key)
get_extensions
config = BeEF::Core::Configuration.instance
@@ -75,42 +72,32 @@ module Core
@beefjs_components = {}
end
#
# Uses the API to include all the code from extensions that need to add
# methods, constants etc to that class.
#
# See BeEF::API::Command for examples.
#
# Uses the API to include all the code from extensions that need to add methods, constants etc to that class.
# @todo Determine if this method is deprecated
def get_extensions
BeEF::API::Command.extended_in_modules.each do |mod|
self.class.send(:include, mod)
end
end
#
# This function is called just before the intructions are sent to hooked browser.
# The derived class can use this function to update params used in the command module.
#
# This function is called just before the instructions are sent to hooked browser.
def pre_send; end
#
# Callback method. This function is called when the hooked browser sends results back.
#
def callback; end
#
# If the command requires some data to be sent back, this function will process them.
#
# @param [] head
# @param [Hash] params Hash of parameters
# @todo Determine argument "head" type
def process_zombie_response(head, params); end
#
# Returns true if the command needs configurations to work. False if not.
#
# @deprecated This command should not be used since the implementation of the new configuration system
def needs_configuration?; !@datastore.nil?; end
#
# Returns information about the command in a JSON format.
#
# @return [String] JSON formatted string
def to_json
{
'Name' => @friendlyname,
@@ -120,18 +107,16 @@ module Core
}.to_json
end
#
# Builds the 'datastore' attribute of the command which is used to generate javascript code.
#
# @param [Hash] data Data to be inserted into the datastore
# @todo Confirm argument "data" type
def build_datastore(data);
@datastore = JSON.parse(data)
end
#
# Sets the datastore for the callback function. This function is meant to be called by the CommandHandler
#
# build_callback_datastore(http_params, http_header)
#
# @param [Hash] http_params HTTP parameters
# @param [Hash] http_header HTTP headers
def build_callback_datastore(http_params, http_header)
@datastore = {'http_headers' => {}} # init the datastore
@@ -152,9 +137,8 @@ module Core
}
end
#
# Returns the output of the command. These are the actual instructions sent to the browser.
#
# @return [String] The command output
def output
f = @path+'command.js'
raise WEBrick::HTTPStatus::BadRequest, "#{f} file does not exist" if not File.exists? f
@@ -178,28 +162,25 @@ module Core
@output
end
#
# Saves the results received from the zombie.
#
# Saves the results received from the hooked browser
# @param [Hash] results Results from hooked browser
def save(results);
@results = results;
end
# If nothing else than the file is specified, the function will map the file to a random path
# without any extension.
# If nothing else than the file is specified, the function will map the file to a random path without any extension.
# @param [String] file File to be mounted
# @param [String] path URL path to mounted file
# @param [String] extension URL extension
# @param [Integer] count The amount of times this file can be accessed before being automatically unmounted
# @deprecated This function is possibly deprecated in place of the API
def map_file_to_url(file, path=nil, extension=nil, count=1)
return BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind(file, path, extension, count)
end
#
# Tells the framework to load a specific module of the BeEFJS library that
# the command will be using.
#
# Example:
#
# use 'beef.net.local'
# use 'beef.encode.base64'
#
# Tells the framework to load a specific module of the BeEFJS library that the command will be using.
# @param [String] component String of BeEFJS component to load
# @note Example: use 'beef.net.local'
def use(component)
return if @beefjs_components.include? component
@@ -213,12 +194,14 @@ module Core
@beefjs_components[component] = component_path
end
# @todo Document
def oc_value(name)
option = BeEF::Core::Models::OptionCache.first(:name => name)
return nil if not option
return option.value
end
# @todo Document
def apply_defaults()
@datastore.each { |opt|
opt["value"] = oc_value(opt["name"]) || opt["value"]

View File

@@ -13,23 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
#
# Parses the user configuration file for beef.
#
# Example:
#
# configuration = BeEF::Core::Configuration.instance
# p configuration.get('beef.http.host) # => "0.0.0.0"
#
class Configuration
include Singleton
#
# Constructor
#
# Loads the default configuration system
# @param [String] configuration_file Configuration file to be loaded, by default loads $root_dir/config.yaml
def initialize(configuration_file="#{$root_dir}/config.yaml")
# argument type checking
raise Exception::TypeError, '"configuration_file" needs to be a string' if not configuration_file.string?
@@ -41,18 +34,18 @@ module Core
@config.default = nil
end
#
# Loads yaml file
#
# @param [String] file YAML file to be loaded
# @return [Hash] YAML formatted hash
def load(file)
return nil if not File.exists?(file)
raw = File.read(file)
return YAML.load(raw)
end
#
# Returns the value of a selected key in the configuration file.
#
# @param [String] key Key of configuration item
# @return [Hash|String] The resulting value stored against the 'key'
def get(key)
subkeys = key.split('.')
lastkey = subkeys.pop
@@ -62,9 +55,10 @@ module Core
return (subhash != nil and subhash.has_key?(lastkey)) ? subhash[lastkey] : nil
end
#
# Sets the give key value pair to the config instance
#
# @param [String] key The configuration key
# @param value The value to be stored against the 'key'
# @return [Boolean] If the store procedure was successful
def set(key, value)
subkeys = key.split('.').reverse
return false if subkeys.length == 0
@@ -76,9 +70,9 @@ module Core
return true
end
#
# Clears the given key hash
#
# @param [String] key Configuration key to be cleared
# @return [Boolean] If the configuration key was cleared
def clear(key)
subkeys = key.split('.')
return false if subkeys.length == 0
@@ -90,9 +84,7 @@ module Core
return (hash.delete(lastkey) == nil) ? false : true
end
#
# load extensions configurations
#
# Load extensions configurations
def load_extensions_config
self.set('beef.extension', {})
Dir.glob("#{$root_dir}/extensions/*/config.yaml") do | cf |
@@ -104,9 +96,7 @@ module Core
end
end
#
# Load module configurations
#
def load_modules_config
self.set('beef.module', {})
Dir.glob("#{$root_dir}/modules/**/*/config.yaml") do | cf |

View File

@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
module Constants
@@ -38,6 +39,9 @@ module Constants
FRIENDLY_O_NAME = 'Opera'
FRIENDLY_UN_NAME = "UNKNOWN"
# Attempt to retrieve a browsers friendly name
# @param [String] browser_name Short browser name
# @return [String] Friendly browser name
def self.friendly_name(browser_name)
case browser_name
@@ -53,6 +57,10 @@ module Constants
end
# Attempt to match the browserstring to a browser constant
# @param [String] browserstring Browser UA string
# @return [Array] An array of matching browser constants
# @todo Confirm this function returns an array if multiple constants are matched
def self.match_browser(browserstring)
matches = []
browserstring.split(" ").each do |chunk|
@@ -80,4 +88,4 @@ module Constants
end
end
end
end

View File

@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
module Constants
@@ -20,6 +21,7 @@ module Constants
module CommandModule
# @note Constants to define the execution probability of a command module (this is browser dependant)
VERIFIED_WORKING = 0
VERIFIED_UNKNOWN = 1
VERIFIED_USER_NOTIFY = 2
@@ -30,4 +32,4 @@ module Constants
end
end
end
end

View File

@@ -13,11 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
module Constants
# The distributed engine codes
# @note The distributed engine codes
module DistributedEngine
REQUESTER = 1
@@ -27,4 +28,4 @@ module Constants
end
end
end
end

View File

@@ -13,11 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
module Constants
# The OS'es strings for os detection.
# @note The OS'es strings for os detection.
module Os
OS_UNKNOWN_IMG = 'unknown.png'
@@ -47,6 +48,9 @@ module Constants
OS_ANDROID_IMG = 'android.png'
OS_ALL_UA_STR = 'All'
# Attempt to match operating system string to constant
# @param [String] name Name of operating system
# @return [String] Constant name of matched operating system, returns 'ALL' if nothing are matched
def self.match_os(name)
case name.downcase
when /win/

View File

@@ -13,21 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
#
# This module provides crypto functionality
#
module Crypto
# the minimum length of the security token
# @note the minimum length of the security token
TOKEN_MINIMUM_LENGTH = 15
#
# Generate a secure random token
#
# @param: {Integer} the length of the secure token
#
# @param [Integer] len The length of the secure token
# @return [String] Security token
def self.secure_token(len = nil)
# get default length from config
config = BeEF::Core::Configuration.instance

View File

@@ -13,13 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
module DistributedEngine
module Models
#
# Table stores the rules for the Distributed Engine.
#
# @note Table stores the rules for the Distributed Engine.
class Rules
include DataMapper::Resource
@@ -35,4 +35,4 @@ module Models
end
end
end
end
end

View File

@@ -25,6 +25,10 @@ module Handlers
attr_reader :guard
@data = {}
# Handles command data
# @param [Hash] data Data from command execution
# @param [Class] kclass Class of command
# @todo Confirm argument data variable type.
def initialize(data, kclass)
@guard = Mutex.new
@kclass = BeEF::Core::Command.const_get(kclass.capitalize)
@@ -32,40 +36,45 @@ module Handlers
setup()
end
# Initial setup function, creates the command module and saves details to datastore
def setup()
@http_params = @data['request'].query # used to populate datastore
@http_header = @data['request'].header # used to populate datastore
@http_header['referer'] ||= '' # used to populate datastore
@http_params = @data['request'].query
@http_header = @data['request'].header
@http_header['referer'] ||= ''
# get and check command id from the request
# @note get and check command id from the request
command_id = get_param(@data, 'cid')
# ruby filter needs to be updated to detect fixnums not strings
# @todo ruby filter needs to be updated to detect fixnums not strings
command_id = command_id.to_s()
raise WEBrick::HTTPStatus::BadRequest, "command_id is invalid" if not BeEF::Filters.is_valid_command_id?(command_id.to_s())
# get and check session id from the request
# @note get and check session id from the request
beefhook = get_param(@data, 'beefhook')
raise WEBrick::HTTPStatus::BadRequest, "beefhook is invalid" if not BeEF::Filters.is_valid_hook_session_id?(beefhook)
# create the command module to handle the response
command = @kclass.new(BeEF::Module.get_key_by_class(@kclass)) # create the commamd module
command.build_callback_datastore(@http_params, @http_header) # build datastore from the response
# @note create the command module to handle the response
command = @kclass.new(BeEF::Module.get_key_by_class(@kclass))
command.build_callback_datastore(@http_params, @http_header)
command.session_id = beefhook
if command.respond_to?(:post_execute)
command.post_execute
end
# get/set details for datastore and log entry
# @note get/set details for datastore and log entry
command_friendly_name = command.friendlyname
raise WEBrick::HTTPStatus::BadRequest, "command friendly name empty" if command_friendly_name.empty?
command_results = get_param(@data, 'results')
raise WEBrick::HTTPStatus::BadRequest, "command results empty" if command_results.empty?
# save the command module results to the datastore and create a log entry
# @note save the command module results to the datastore and create a log entry
command_results = {'data' => command_results}
BeEF::Core::Models::Command.save_result(beefhook, command_id, command_friendly_name, command_results)
end
# Returns parameter from hash
# @param [Hash] query Hash of data to return data from
# @param [String] key Key to search for and return inside `query`
# @return Value referenced in hash at the supplied key
def get_param(query, key)
return (query.class == Hash and query.has_key?(key)) ? query[key] : nil
end

View File

@@ -17,9 +17,7 @@ module BeEF
module Core
module Handlers
#
# This class handles connections from zombies to the framework.
#
# @note This class handles connections from hooked browsers to the framework.
class HookedBrowsers < WEBrick::HTTPServlet::AbstractServlet
include BeEF::Core::Handlers::Modules::BeEFJS
@@ -31,11 +29,10 @@ module Handlers
@guard = Mutex.new
end
#
# This method processes the http requests sent by a zombie to the framework.
# It will update the database to add or update the current zombie and deploy
# some command modules or plugins.
#
# 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)
@body = ''
@params = request.query
@@ -43,7 +40,7 @@ module Handlers
@response = response
config = BeEF::Core::Configuration.instance
# check source ip address of browser
# @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)
@@ -52,23 +49,24 @@ module Handlers
return
end
# get zombie if already hooked the framework
# @note get zombie if already hooked the framework
hook_session_id = request.get_hook_session_id()
hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => hook_session_id) if not hook_session_id.nil?
if not hooked_browser # is a new browser so return instructions to set up the hook
# @note is a new browser so return instructions to set up the hook
if not hooked_browser
# generate the instructions to hook the browser
host_name = @request.host # get the host from the HOST attribute in the HTTP header
# @note generate the instructions to hook the browser
host_name = @request.host
raise WEBrick::HTTPStatus::BadRequest, "Invalid host name" if not BeEF::Filters.is_valid_hostname?(host_name)
build_beefjs!(host_name)
else # is a known browseer so send instructions
# record the last poll from the browser
# @note is a known browser so send instructions
else
# @note record the last poll from the browser
hooked_browser.lastseen = Time.new.to_i
# Check for a change in zombie IP and log an event
# @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
@@ -77,17 +75,15 @@ module Handlers
hooked_browser.count!
hooked_browser.save
# add all availible command module instructions to the response
# @note add all available command module instructions to the response
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)}
#
# We dynamically get the list of all browser hook handler using the API and register them
#
# @note We dynamically get the list of all browser hook handler using the API and register them
BeEF::API::Registrar.instance.fire(BeEF::API::Server::Hook, 'pre_hook_send', hooked_browser, @body, @params, @request, @response)
end
# set response headers and body
# @note set response headers and body
response.set_no_cache
response.header['Content-Type'] = 'text/javascript'
response.header['Access-Control-Allow-Origin'] = '*'
@@ -96,17 +92,19 @@ module Handlers
end
# @note alias do_POST function to do_GET
alias do_POST do_GET
private
# Object representing the HTTP request
# @note Object representing the HTTP request
@request
# Object representing the HTTP response
# @note Object representing the HTTP response
@response
# A string containing the list of BeEF components active in the hooked browser
# @note A string containing the list of BeEF components active in the hooked browser
# @todo Confirm this variable is still used
@beef_js_cmps
end

View File

@@ -18,50 +18,45 @@ module Core
module Handlers
module Modules
#
# Purpose: avoid rewriting several times the same code.
#
# @note Purpose: avoid rewriting several times the same code.
module BeEFJS
#
# Builds the default beefjs library (all default components of the library).
#
# @param: {Object} the hook session id
# @param: {Boolean} if the framework is already loaded in the hooked browser
#
# @param [Object] req_host The request object
def build_beefjs!(req_host)
# set up values required to construct beefjs
beefjs = '' # init the beefjs string (to be sent as the beefjs file)
beefjs_path = "#{$root_dir}/core/main/client/" # location of sub files
# @note set up values required to construct beefjs
beefjs = ''
# @note location of sub files
beefjs_path = "#{$root_dir}/core/main/client/"
js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js)
# construct the beefjs string from file(s)
# @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 # construct absolute path
beefjs << (File.read(js_sub_file_abs_path) + "\n\n") # concat each js sub file
js_sub_file_abs_path = beefjs_path + js_sub_file_name
beefjs << (File.read(js_sub_file_abs_path) + "\n\n")
}
# create the config for the hooked browser session
# @note create the config for the hooked browser session
config = BeEF::Core::Configuration.instance
hook_session_name = config.get('beef.http.hook_session_name')
hook_session_config = BeEF::Core::Server.instance.to_h
# if http_host="0.0.0.0" in config ini, use the host requested by client
# @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
# populate place holders in the beefjs string and set the response body
# @note populate place holders in the beefjs string and set the response body
eruby = Erubis::FastEruby.new(beefjs)
@body << eruby.evaluate(hook_session_config)
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./, '')
@@ -73,13 +68,10 @@ module Modules
component_path
end
#
# Builds missing beefjs components.
#
# Ex: build_missing_beefjs_components(['beef.net.local', 'beef.net.requester'])
#
# @param [Array] beefjs_components An array of component names
def build_missing_beefjs_components(beefjs_components)
# verifies that @beef_js_cmps is not nil to avoid bugs
# @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
@@ -91,14 +83,13 @@ module Modules
beefjs_components.keys.each {|k|
next if @beef_js_cmps.include? beefjs_components[k]
# path to the component
# @note path to the component
component_path = beefjs_components[k]
# we output the component to the hooked browser
# @note we output the component to the hooked browser
@body << File.read(component_path)+"\n\n"
# finally we add the component to the list of components already generated so it does not
# get generated numerous times.
# @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

View File

@@ -20,9 +20,9 @@ module Modules
module Command
#
# Adds the command module instructions to a hooked browser's http response.
#
# @param [Object] command Command object
# @param [Object] hooked_browser Hooked Browser object
def add_command_instructions(command, hooked_browser)
raise WEBrick::HTTPStatus::BadRequest, "hooked_browser is nil" if hooked_browser.nil?
@@ -30,7 +30,7 @@ module Modules
raise WEBrick::HTTPStatus::BadRequest, "hooked_browser is nil" if command.nil?
raise WEBrick::HTTPStatus::BadRequest, "hooked_browser.command_module_id is nil" if command.command_module_id.nil?
# get the command module
# @note get the command module
command_module = BeEF::Core::Models::CommandModule.first(:id => command.command_module_id)
raise WEBrick::HTTPStatus::BadRequest, "command_module is nil" if command_module.nil?
raise WEBrick::HTTPStatus::BadRequest, "command_module.path is nil" if command_module.path.nil?
@@ -51,13 +51,13 @@ module Modules
@body << command_module.output + "\n\n"
# prints the event to the console
# @note prints the event to the console
if BeEF::Settings.console?
name = command_module.friendlyname || kclass
print_info "Hooked browser #{hooked_browser.ip} has been sent instructions from command module '#{name}'"
end
# flag that the command has been sent to the hooked browser
# @note flag that the command has been sent to the hooked browser
command.instructions_sent = true
command.save
end

View File

@@ -13,48 +13,35 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
#
# This class takes care of logging events in the db.
#
# Example:
#
# logger = BeEF::Core::Logger.instance
# logger.register('Authentication', 'User with ip 127.0.0.1 has successfully authenticated into the application') # => true
#
# zombie_id = 1
# logger.register('Zombie', '123.456.789.123 just joined the horde', zombie_id) # => true
#
class Logger
include Singleton
#
# Constructor
#
def initialize
@logs = BeEF::Core::Models::Log
end
#
# Registers a new event in the logs
#
# @param: {String} the origine of the event (i.e. Authentication, Zombie)
# @param: {String} the event description
# @param: {Integer} the id of the hooked browser affected (default = 0 if no HB)
#
def register(from, event, zombie = 0)
# @param [String] from The origin of the event (i.e. Authentication, Hooked Browser)
# @param [String] event The event description
# @param [Integer] hb The id of the hooked browser affected (default = 0 if no HB)
# @return [Boolean] True if the register was successful
def register(from, event, hb = 0)
# type conversion to enforce standards
zombie = zombie.to_i
hb = hb.to_i
# arguments type checking
raise Exception::TypeError, '"from" needs to be a string' if not from.string?
raise Exception::TypeError, '"event" needs to be a string' if not event.string?
raise Exception::TypeError, '"zombie" needs to be an integer' if not zombie.integer?
raise Exception::TypeError, '"Hooked Browser ID" needs to be an integer' if not hb.integer?
# logging the new event into the database
@logs.new(:type => "#{from}", :event => "#{event}", :date => Time.now, :hooked_browser_id => zombie).save
@logs.new(:type => "#{from}", :event => "#{event}", :date => Time.now, :hooked_browser_id => hb).save
# return
true

View File

@@ -13,27 +13,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
#
# This class migrates and updates values in the database each time you restart BeEF.
# So for example, when you want to add a new command module, you stop BeEF, copy your command module into the framework
# and then restart BeEF. That class will take care of installing automatically the new command module in the db.
#
# @note This class migrates and updates values in the database each time you restart BeEF.
# So for example, when you want to add a new command module, you stop BeEF, copy your command module into the framework
# and then restart BeEF. That class will take care of installing automatically the new command module in the db.
class Migration
include Singleton
#
# Updates the database.
#
def update_db!
update_commands!
end
#
# Checks for new command modules and updates the database.
#
def update_commands!
config = BeEF::Core::Configuration.instance

View File

@@ -13,22 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
module Models
#
# Table stores the commands that have been sent to the Hooked Browsers.
#
# Attributes:
#
# - id
# - data
# - creationdate
# - label
# - instructions_sent
# - command_module_id
# - hooked_browser_id
#
# @note Table stores the commands that have been sent to the Hooked Browsers.
class Command
include DataMapper::Resource
@@ -43,25 +33,22 @@ module Models
has n, :results
#
# Save results and flag that the command has been run on the hooked browser
#
# @param: {String} the session_id.
# @param: {String} the command_id.
# @param: {String} the command friendly name.
# @param: {String} the result of the command module.
#
# @param [String] hook_session_id The session_id.
# @param [String] command_id The command_id.
# @param [String] command_friendly_name The command friendly name.
# @param [String] result The result of the command module.
def self.save_result(hook_session_id, command_id, command_friendly_name, result)
# enforcing arguments types
# @note enforcing arguments types
command_id = command_id.to_i
# argument type checking
# @note argument type checking
raise Exception::TypeError, '"hook_session_id" needs to be a string' if not hook_session_id.string?
raise Exception::TypeError, '"command_id" needs to be an integer' if not command_id.integer?
raise Exception::TypeError, '"command_friendly_name" needs to be a string' if not command_friendly_name.string?
raise Exception::TypeError, '"result" needs to be a hash' if not result.hash?
# get the hooked browser structure and id from the database
# @note get the hooked browser structure and id from the database
hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => hook_session_id) || nil
raise Exception::TypeError, "hooked_browser is nil" if hooked_browser.nil?
raise Exception::TypeError, "hooked_browser.id is nil" if hooked_browser.id.nil?
@@ -69,18 +56,18 @@ module Models
raise Exception::TypeError, "hooked_browser.ip is nil" if hooked_browser.ip.nil?
hooked_browser_ip = hooked_browser.ip
# get the command module data structure from the database
# @note get the command module data structure from the database
command = first(:id => command_id.to_i, :hooked_browser_id => hooked_browser_id) || nil
raise Exception::TypeError, "command is nil" if command.nil?
# create the entry for the results
# @note create the entry for the results
command.results.new(:hooked_browser_id => hooked_browser_id, :data => result.to_json, :date => Time.now.to_i)
command.save
# log that the result was returned
# @note log that the result was returned
BeEF::Core::Logger.instance.register('Command', "Hooked browser #{hooked_browser.ip} has executed instructions from command module '#{command_friendly_name}'", hooked_browser_id)
# prints the event into the console
# @note prints the event into the console
if BeEF::Settings.console?
print_info "Hooked browser #{hooked_browser.ip} has executed instructions from command module '#{command_friendly_name}'"
end
@@ -90,4 +77,4 @@ module Models
end
end
end
end

View File

@@ -31,27 +31,25 @@ module Models
property :firstseen, String, :length => 15
property :lastseen, String, :length => 15
property :httpheaders, Text, :lazy => false
property :domain, Text, :lazy => false # the domain originating the hook request
# @note the domain originating the hook request
property :domain, Text, :lazy => false
property :count, Integer, :lazy => false
property :has_init, Boolean, :default => false
property :is_proxy, Boolean, :default => false # if true the HB is used as a tunneling proxy
property :is_proxy, Boolean, :default => false
# @note if true the HB is used as a tunneling proxy
has n, :commands
has n, :results
has n, :logs
#has n, :https
#
# Increases the count of a zombie
#
def count!
if not self.count.nil? then self.count += 1; else self.count = 1; end
end
#
# Returns the icon representing the browser type the
# zombie is using (i.e. Firefox, Internet Explorer)
#
# Returns the icon representing the browser type the hooked browser is using (i.e. Firefox, Internet Explorer)
# @return [String] String constant containing browser icon path
def browser_icon
agent = JSON.parse(self.httpheaders)['user-agent'].to_s || nil
@@ -67,10 +65,8 @@ module Models
BeEF::Extension::AdminUI::Constants::Agents::AGENT_UNKNOWN_IMG
end
#
# Returns the icon representing the os type the
# hooked browser is running (i.e. Windows, Linux)
#
# Returns the icon representing the os type the hooked browser is running (i.e. Windows, Linux)
# @return [String] String constant containing operating system icon path
def os_icon
agent = JSON.parse(self.httpheaders)['user-agent'].to_s || nil
@@ -86,4 +82,4 @@ module Models
end
end
end
end

View File

@@ -16,11 +16,7 @@
module BeEF
module Core
module Models
#
# This table stores the logs from the framework.
#
# See BeEF::Core::Logger for how to log events.
#
class Log
include DataMapper::Resource
@@ -36,4 +32,4 @@ module Models
end
end
end
end
end

View File

@@ -16,9 +16,7 @@
module BeEF
module Core
module Models
#
# Table stores the results from commands.
#
class Result
include DataMapper::Resource
@@ -33,4 +31,4 @@ module Models
end
end
end
end

View File

@@ -16,11 +16,8 @@
module BeEF
module Core
module Models
#
# Table stores the list of users that have authenticated into beef.
#
# TODO: move this table into the AdminUI extension folder.
#
# @todo move this table into the AdminUI extension folder.
class User
include DataMapper::Resource
@@ -31,9 +28,8 @@ module Models
property :session_id, String, :length => 255
property :ip, Text
#
# Checks if the user has been authenticated
#
# @return [Boolean] If the user is authenticated
def authenticated?
true || false if not @ip.nil?
end
@@ -42,4 +38,4 @@ module Models
end
end
end
end

View File

@@ -18,17 +18,16 @@ module Core
module NetworkStack
module RegisterHttpHandler
#
# Register the http handler for the network stack
#
# @param [Object] server HTTP server instance
def self.mount_handler(server)
#dynamic handler
# @note this mounts the dynamic handler
server.mount('/dh', true, BeEF::Core::NetworkStack::Handlers::DynamicReconstruction)
end
end
# Register core API calls
BeEF::API::Registrar.instance.register(BeEF::Core::NetworkStack::RegisterHttpHandler, BeEF::API::Server, 'mount_handler')
end

View File

@@ -18,25 +18,28 @@ module Core
module NetworkStack
module Handlers
#
# Class defining BeEF assets
#
# @note Class defining BeEF assets
class AssetHandler
# call BeEF::Core::NetworkStack::Handlers::AssetHandler.instance
# @note call BeEF::Core::NetworkStack::Handlers::AssetHandler.instance
include Singleton
attr_reader :allocations, :root_dir
# Starts the AssetHandler instance
def initialize
@allocations = {}
@http_server = BeEF::Core::Server.instance
@root_dir = File.expand_path('../../../../', __FILE__)
end
#
# Binds a file to a mount point
#
# @param [String] file File path to asset
# @param [String] path URL path to mount the asset to (can be nil for random path)
# @param [String] extension Extension to append to the URL path (can be nil for none)
# @param [Integer] count The amount of times the asset can be accessed before being automatically unbinded (-1 = unlimited)
# @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)
@allocations[url] = {'file' => "#{root_dir}"+file, 'path' => path, 'extension' => extension, 'count' => count}
@@ -45,26 +48,27 @@ module Handlers
return url
end
#
# Unbinds a file from a mount point
#
# @param [String] url URL path of asset to be unbinded
def unbind(url)
@allocations.delete(url)
@http_server.unmount(url, true)
end
#
# Builds a URL based on the path and extention, if neither are passed a random URL will be generated
#
# Builds a URL based on the path and extension, if neither are passed a random URL will be generated
# @param [String] path URL Path defined by bind()
# @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
end
#
# Checks if the file is allocated, if the file isn't return true to pass onto FileHandler.
#
# @param [String] url URL Path of mounted file
# @return [Boolean] Returns true if the file is mounted
def check(url)
if @allocations.has_key?(url)
count = @allocations[url]['count']

View File

@@ -18,19 +18,20 @@ module Core
module NetworkStack
module Handlers
#DynamicHanlder is used reconstruct segmented traffic from the zombies
# @note DynamicHanlder is used reconstruct segmented traffic from the hooked browser
class DynamicReconstruction < WEBrick::HTTPServlet::AbstractServlet
attr_reader :guard
#holds packet queue
# @note holds packet queue
PQ = Array.new()
#obtain dynamic mount points from HttpHookServer
# @note obtain dynamic mount points from HttpHookServer
MOUNTS = BeEF::Core::Server.instance.mounts
#Combines packet information and pushes to PQ, then checks packets
# 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
@@ -48,9 +49,10 @@ module Handlers
check_packets()
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
# Check packets goes through the PQ array and attempts to reconstruct the stream from multiple packets
def check_packets()
checked = Array.new()
PQ.each do |packet|
@@ -88,14 +90,17 @@ module Handlers
end
end
#delete packets that have been reconstructed, return deleted packets
# Delete packets that have been reconstructed, return deleted packets
# @param [String] beefhook Beefhook of hooked browser
# @param [Integer] stream_id The stream ID
def expunge(beefhook, stream_id)
packets = PQ.select{ |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id }
PQ.delete_if { |p| p[:beefhook] == beefhook and p[:stream_id] == stream_id }
return 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
# Execute is called once a stream has been rebuilt. it searches the mounts and passes the data to the correct handler
# @param [Hash] data Hash of data that has been rebuilt by the dynamic reconstruction
def execute(data)
handler = get_param(data, 'handler')
if (MOUNTS.has_key?(handler))
@@ -107,7 +112,10 @@ module Handlers
end
end
#assist function for getting parameter from hash
# Assist function for getting parameter from hash
# @param [Hash] query Hash to pull key from
# @param [String] key The key association to return from `query`
# @return Value associated with `key`
def get_param(query, key)
return nil if query[key].nil?
query[key]

View File

@@ -13,30 +13,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Core
#
# Class defining the BeEF http server.
#
# Example:
#
# server = BeEF::Core::Server.instance
# server.prepare
# server.start
# ...
# server.stop
#
class Server
include Singleton
# Grabs the version of beef the framework is deployed on
# @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
#
# Constructor
#
# 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")
@@ -46,10 +36,8 @@ module Core
@mounts = {}
end
#
# Returns all server variables in a hash. Useful for Erubis when
# generating the javascript for the command modules and hooking.
#
# 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,
@@ -62,9 +50,11 @@ module Core
}
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?
@@ -76,9 +66,7 @@ module Core
end
end
#
# Starts the BeEF http server.
#
def prepare
if not @http_server
config = {}
@@ -93,16 +81,12 @@ module Core
# 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)
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 }
@@ -111,9 +95,7 @@ module Core
@http_server.start
end
#
# Stops the BeEF http server.
#
def stop
if @http_server
# shuts down the server
@@ -125,19 +107,14 @@ module Core
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} the url to mount
# @param: {Boolean} set to true for a hard mount, false for a soft mount.
# @param: {Class} class to call once mount is triggered
# @param: {Various} arguments to pass to the http handler class
#
# @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?
@@ -161,12 +138,9 @@ module Core
end
end
#
# Unmounts handler
#
# @param: {String} url to mount.
# @param: {Boolean} set to true for a hard mount, false for a soft mount.
#
# @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?