Code cleanup

This commit is contained in:
Brendan Coles
2018-06-11 11:36:35 +00:00
parent 21af0ae705
commit 1dea97511b
15 changed files with 689 additions and 558 deletions

View File

@@ -7,168 +7,205 @@
module BeEF
module API
#
# Registrar class to handle all registered timed API calls
#
class Registrar
include Singleton
#
# Create registrar
#
def initialize
@registry = []
@count = 1
end
# Register timed API calls to an owner
#
# @param [Class] owner the owner of the API hook
# @param [Class] c the API class the owner would like to hook into
# @param [String] method the method of the class the owner would like to execute
# @param [Array] params an array of parameters that need to be matched before the owner will be called
#
def register(owner, c, method, params = [])
if self.verify_api_path(c, method)
if not self.registered?(owner, c, method, params)
id = @count
@registry << {
'id' => id,
'owner' => owner,
'class' => c,
'method' => method,
'params' => params
}
@count += 1
return id
else
print_debug "API Registrar: Attempting to re-register API call #{c.to_s} :#{method.to_s}"
end
else
print_error "API Registrar: Attempted to register non-existant API method #{c.to_s} :#{method.to_s}"
unless verify_api_path(c, method)
print_error "API Registrar: Attempted to register non-existant API method #{c} :#{method}"
return
end
if registered?(owner, c, method, params)
print_debug "API Registrar: Attempting to re-register API call #{c} :#{method}"
return
end
id = @count
@registry << {
'id' => id,
'owner' => owner,
'class' => c,
'method' => method,
'params' => params
}
@count += 1
id
end
#
# Tests whether the owner is registered for an API hook
#
# @param [Class] owner the owner of the API hook
# @param [Class] c the API class
# @param [String] method the method of the class
# @param [Array] params an array of parameters that need to be matched
#
# @return [Boolean] whether or not the owner is registered
#
def registered?(owner, c, method, params = [])
@registry.each{|r|
if r['owner'] == owner and r['class'] == c and r['method'] == method and self.is_matched_params?(r, params)
return true
end
}
return false
@registry.each do |r|
next unless r['owner'] == owner
next unless r['class'] == c
next unless r['method'] == method
next unless is_matched_params? r, params
return true
end
false
end
#
# Match a timed API call to determine if an API.fire() is required
#
# @param [Class] c the target API class
# @param [String] method the method of the target API class
# @param [Array] params an array of parameters that need to be matched
#
# @return [Boolean] whether or not the arguments match an entry in the API registry
#
def matched?(c, method, params = [])
@registry.each{|r|
if r['class'] == c and r['method'] == method and self.is_matched_params?(r, params)
return true
end
}
return false
@registry.each do |r|
next unless r['class'] == c
next unless r['method'] == method
next unless is_matched_params? r, params
return true
end
false
end
#
# Un-registers an API hook
#
# @param [Integer] id the ID of the API hook
#
def unregister(id)
@registry.delete_if{|r|
r['id'] == id
}
@registry.delete_if {|r| r['id'] == id }
end
#
# Retrieves all the owners and ID's of an API hook
# @param [Class] c the target API class
# @param [String] method the method of the target API class
# @param [Array] params an array of parameters that need to be matched
#
# @return [Array] an array of hashes consisting of two keys :owner and :id
#
def get_owners(c, method, params = [])
owners = []
@registry.each{|r|
if r['class'] == c and r['method'] == method
if self.is_matched_params?(r, params)
owners << { :owner => r['owner'], :id => r['id']}
end
end
}
return owners
@registry.each do |r|
next unless r['class'] == c
next unless r['method'] == method
next unless is_matched_params? r, params
owners << { :owner => r['owner'], :id => r['id'] }
end
owners
end
#
# Verifies that the api_path has been regitered
# Verifies the API path has been registered.
#
# @note This is a security precaution
#
# @param [Class] c the target API class to verify
# @param [String] m the target method to verify
#
def verify_api_path(c, m)
return (c.const_defined?('API_PATHS') and c.const_get('API_PATHS').has_key?(m))
(c.const_defined?('API_PATHS') && c.const_get('API_PATHS').key?(m))
end
#
# Retrieves the registered symbol reference for an API hook
#
# @param [Class] c the target API class to verify
# @param [String] m the target method to verify
#
# @return [Symbol] the API path
#
def get_api_path(c, m)
return (self.verify_api_path(c, m)) ? c.const_get('API_PATHS')[m] : nil;
verify_api_path(c, m) ? c.const_get('API_PATHS')[m] : nil
end
#
# Matches stored API params to params
#
# @note If a stored API parameter has a NilClass the parameter matching is skipped for that parameter
# @note By default this method returns true, this is either because the API.fire() did not include any parameters or there were no parameters defined for this registry entry
#
# @param [Hash] reg hash of registry element, must contain 'params' key
# @param [Array] params array of parameters to be compared to the stored parameters
#
# @return [Boolean] whether params matches the stored API parameters
#
def is_matched_params?(reg, params)
stored = reg['params']
if stored.length == params.length
matched = true
stored.each_index{|i|
next if stored[i] == nil
if not stored[i] == params[i]
matched = false
end
}
return false if not matched
return true unless stored.length == params.length
stored.each_index do |i|
next if stored[i].nil?
return false unless stored[i] == params[i]
end
return true
true
end
#
# Fires all owners registered to this API hook
#
# @param [Class] c the target API class
# @param [String] m the target API method
# @param [Array] *args parameters passed for the API call
# @return [Hash, NilClass] returns either a Hash of :api_id and :data if the owners return data, otherwise NilClass
#
# @return [Hash, NilClass] returns either a Hash of :api_id and :data
# if the owners return data, otherwise NilClass
#
def fire(c, m, *args)
mods = self.get_owners(c, m, args)
if mods.length > 0
data = []
if self.verify_api_path(c, m) and c.ancestors[0].to_s > "BeEF::API"
method = self.get_api_path(c, m)
mods.each do |mod|
begin
#Only used for API Development (very verbose)
#print_info "API: #{mod} fired #{method}"
result = mod[:owner].method(method).call(*args)
if not result == nil
data << {:api_id => mod[:id], :data => result}
end
rescue => e
print_error "API Fire Error: #{e.message} in #{mod.to_s}.#{method.to_s}()"
end
end
else
print_error "API Path not defined for Class: #{c.to_s} method:#{method.to_s}"
end
return data
mods = get_owners(c, m, args)
return nil unless mods.length.positive?
unless verify_api_path(c, m) && c.ancestors[0].to_s > 'BeEF::API'
print_error "API Path not defined for Class: #{c} method:#{method}"
return []
end
return nil
data = []
method = get_api_path(c, m)
mods.each do |mod|
begin
# Only used for API Development (very verbose)
# print_info "API: #{mod} fired #{method}"
result = mod[:owner].method(method).call(*args)
unless result.nil?
data << { :api_id => mod[:id], :data => result }
end
rescue => e
print_error "API Fire Error: #{e.message} in #{mod}.#{method}()"
end
end
data
end
end
end
end

View File

@@ -37,7 +37,3 @@ require 'core/main/migration'
require 'core/main/console/commandline'
require 'core/main/console/banners'
# @note Include rubyzip lib
require 'zip'

View File

@@ -10,36 +10,40 @@ module BeEF
# @param [String] ext the extension key
# @return [Boolean] whether or not the extension exists in BeEF's configuration
def self.is_present(ext)
return BeEF::Core::Configuration.instance.get('beef.extension').has_key?(ext.to_s)
BeEF::Core::Configuration.instance.get('beef.extension').key? ext.to_s
end
# Checks to see if extension is enabled in configuration
# @param [String] ext the extension key
# @return [Boolean] whether or not the extension is enabled
def self.is_enabled(ext)
return (self.is_present(ext) and BeEF::Core::Configuration.instance.get('beef.extension.'+ext.to_s+'.enable') == true)
return false unless is_present(ext)
BeEF::Core::Configuration.instance.get("beef.extension.#{ext}.enable") == true
end
# Checks to see if extension has been loaded
# @param [String] ext the extension key
# @return [Boolean] whether or not the extension is loaded
# @return [Boolean] whether or not the extension is loaded
def self.is_loaded(ext)
return (self.is_enabled(ext) and BeEF::Core::Configuration.instance.get('beef.extension.'+ext.to_s+'.loaded') == true)
return false unless is_enabled(ext)
BeEF::Core::Configuration.instance.get("beef.extension.#{ext}.loaded") == true
end
# Loads an extension
# @param [String] ext the extension key
# @return [Boolean] whether or not the extension loaded successfully
# @todo Wrap the require() statement in a try catch block to allow BeEF to fail gracefully if there is a problem with that extension - Issue #480
def self.load(ext)
if File.exists?("#{$root_dir}/extensions/#{ext}/extension.rb")
if File.exist? "#{$root_dir}/extensions/#{ext}/extension.rb"
require "#{$root_dir}/extensions/#{ext}/extension.rb"
print_debug "Loaded extension: '#{ext}'"
BeEF::Core::Configuration.instance.set('beef.extension.'+ext+'.loaded', true)
BeEF::Core::Configuration.instance.set "beef.extension.#{ext}.loaded", true
return true
end
print_error "Unable to load extension '#{ext}'"
return false
false
rescue => e
print_error "Unable to load extension '#{ext}':"
print_more e.message
end
end
end

View File

@@ -9,13 +9,13 @@ module BeEF
# Returns configuration of all enabled extensions
# @return [Array] an array of extension configuration hashes that are enabled
def self.get_enabled
return BeEF::Core::Configuration.instance.get('beef.extension').select { |k,v| v['enable'] == true }
BeEF::Core::Configuration.instance.get('beef.extension').select { |k,v| v['enable'] == true }
end
# Returns configuration of all loaded extensions
# @return [Array] an array of extension configuration hashes that are loaded
def self.get_loaded
return BeEF::Core::Configuration.instance.get('beef.extension').select {|k,v| v['loaded'] == true }
BeEF::Core::Configuration.instance.get('beef.extension').select {|k,v| v['loaded'] == true }
end
# Load all enabled extensions
@@ -23,12 +23,10 @@ module BeEF
def self.load
BeEF::Core::Configuration.instance.load_extensions_config
self.get_enabled.each { |k,v|
BeEF::Extension.load(k)
BeEF::Extension.load k
}
# API post extension load
BeEF::API::Registrar.instance.fire(BeEF::API::Extensions, 'post_load')
BeEF::API::Registrar.instance.fire BeEF::API::Extensions, 'post_load'
end
end
end

View File

@@ -32,6 +32,7 @@ require 'mime/types'
require 'optparse'
require 'resolv'
require 'digest'
require 'zip'
# @note Include the filters
require 'core/filters'

View File

@@ -6,37 +6,42 @@
module BeEF
module Core
#
# @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
#
def format_multiline(text)
text.gsub(/\n/, '\n')
end
end
#
# @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);
#
def initialize(hash = nil)
super(hash)
end
end
#
# @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.
# 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
attr_accessor :zombie, :command_id, :session_id
@@ -44,8 +49,11 @@ module BeEF
include BeEF::Core::Constants::Browsers
include BeEF::Core::Constants::CommandModule
#
# Super class controller
#
# @param [String] key command module key
#
def initialize(key)
config = BeEF::Core::Configuration.instance
@@ -61,62 +69,98 @@ module BeEF
@beefjs_components = {}
end
#
# 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
#
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,
'Description' => BeEF::Core::Configuration.instance.get("beef.module.#{@key}.description"),
'Category' => BeEF::Core::Configuration.instance.get("beef.module.#{@key}.category"),
'Data' => BeEF::Module.get_options(@key)
'Name' => @friendlyname,
'Description' => BeEF::Core::Configuration.instance.get("beef.module.#{@key}.description"),
'Category' => BeEF::Core::Configuration.instance.get("beef.module.#{@key}.category"),
'Data' => BeEF::Module.get_options(@key)
}.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)
# @todo TODO Confirm argument "data" type
#
def build_datastore(data)
@datastore = JSON.parse data
rescue => e
print_error "Could not build datastore: #{e.message}"
end
#
# 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_headers HTTP headers
#
def build_callback_datastore(result, command_id, beefhook, http_params, http_headers)
@datastore = {'http_headers' => {}} # init the datastore
if http_params != nil && http_headers != nil
if !http_params.nil? && !http_headers.nil?
# get, check and add the http_params to the datastore
http_params.keys.each { |http_params_key|
(print_error 'http_params_key is invalid';return) if not BeEF::Filters.is_valid_command_module_datastore_key?(http_params_key)
http_params_value = Erubis::XmlHelper.escape_xml(http_params[http_params_key])
(print_error 'http_params_value is invalid';return) if not BeEF::Filters.is_valid_command_module_datastore_param?(http_params_value)
@datastore[http_params_key] = http_params_value # add the checked key and value to the datastore
}
http_params.keys.each do |http_params_key|
unless BeEF::Filters.is_valid_command_module_datastore_key? http_params_key
print_error 'http_params_key is invalid'
return
end
http_params_value = Erubis::XmlHelper.escape_xml http_params[http_params_key]
unless BeEF::Filters.is_valid_command_module_datastore_param?(http_params_value)
print_error 'http_params_value is invalid'
return
end
# add the checked key and value to the datastore
@datastore[http_params_key] = http_params_value
end
# get, check and add the http_headers to the datastore
http_headers.keys.each { |http_header_key|
(print_error 'http_header_key is invalid';return) if not BeEF::Filters.is_valid_command_module_datastore_key?(http_header_key)
http_header_value = Erubis::XmlHelper.escape_xml(http_headers[http_header_key][0])
(print_error 'http_header_value is invalid';return) 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
}
http_headers.keys.each do |http_header_key|
unless BeEF::Filters.is_valid_command_module_datastore_key? http_header_key
print_error 'http_header_key is invalid'
return
end
http_header_value = Erubis::XmlHelper.escape_xml http_headers[http_header_key][0]
unless BeEF::Filters.is_valid_command_module_datastore_param? http_header_value
print_error 'http_header_value is invalid'
return
end
# add the checked key and value to the datastore
@datastore['http_headers'][http_header_key] = http_header_value
end
end
@datastore['results'] = result
@@ -124,28 +168,32 @@ module BeEF
@datastore['beefhook'] = beefhook
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'
(print_error "#{f} file does not exist";return) if not File.exists? f
f = "#{@path}command.js"
unless File.exist? f
print_error "File does not exist: #{f}"
return
end
command = BeEF::Core::Models::Command.first(:id => @command_id)
@eruby = Erubis::FastEruby.new(File.read(f))
data = BeEF::Core::Configuration.instance.get("beef.module.#{@key}")
#data = BeEF::Core::Configuration.instance.get "beef.module.#{@key}"
cc = BeEF::Core::CommandContext.new
cc['command_url'] = @default_command_url
cc['command_id'] = @command_id
JSON.parse(command['data']).each{|v|
JSON.parse(command['data']).each do |v|
cc[v['name']] = v['value']
}
if self.respond_to?(:execute)
self.execute
end
@output = @eruby.evaluate(cc)
execute if respond_to?(:execute)
@output = @eruby.evaluate cc
@output
end
@@ -155,19 +203,25 @@ module BeEF
@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)
#
def map_file_to_url(file, path = nil, extension = nil, count = 1)
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.
# @param [String] component String of BeEFJS component to load
# @note Example: use 'beef.net.local'
#
def use(component)
return if @beefjs_components.include? component
@@ -176,23 +230,23 @@ module BeEF
component_path.gsub!(/\./, '/')
component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js"
raise "Invalid beefjs component for command module #{@path}" if not File.exists?(component_path)
raise "Invalid beefjs component for command module #{@path}" unless File.exist? component_path
@beefjs_components[component] = component_path
end
# @todo Document
# @todo TODO Document
def oc_value(name)
option = BeEF::Core::Models::OptionCache.first(:name => name)
return nil if not option
return option.value
option = BeEF::Core::Models::OptionCache.first(:name => name)
return nil unless option
option.value
end
# @todo Document
def apply_defaults()
@datastore.each { |opt|
opt["value"] = oc_value(opt["name"]) || opt["value"]
}
# @todo TODO Document
def apply_defaults
@datastore.each do |opt|
opt['value'] = oc_value(opt['name']) || opt['value']
end
end
private
@@ -201,9 +255,6 @@ module BeEF
@eruby
@update_zombie
@results
end
end
end

View File

@@ -6,26 +6,26 @@
module BeEF
module Core
class Configuration
attr_accessor :config
# antisnatchor: still a singleton, but implemented by hand because we want to have only one instance
# of the Configuration object while having the possibility to specify a parameter to the constructor.
# This is why we don't use anymore the default Ruby implementation -> include Singleton
def self.instance()
return @@instance
def self.instance
@@instance
end
# Loads the default configuration system
# @param [String] configuration_file Configuration file to be loaded, by default loads $root_dir/config.yaml
# @param [String] configuration_file Configuration file to be loaded,
# by default loads $root_dir/config.yaml
def initialize(config)
raise Exception::TypeError, '"config" needs to be a string' if not config.string?
raise Exception::TypeError, "Configuration file '#{config}' cannot be found" if not File.exist?(config)
raise Exception::TypeError, "'config' needs to be a string" unless config.string?
raise Exception::TypeError, "Configuration file '#{config}' cannot be found" unless File.exist? config
begin
#open base config
@config = self.load(config)
@config = load(config)
# set default value if key? does not exist
@config.default = nil
@@config = config
@@ -33,6 +33,7 @@ module BeEF
print_error "Fatal Error: cannot load configuration file"
print_debug e
end
@@instance = self
end
@@ -40,89 +41,101 @@ module BeEF
# @param [String] file YAML file to be loaded
# @return [Hash] YAML formatted hash
def load(file)
begin
return nil if not File.exists?(file)
raw = File.read(file)
return YAML.load(raw)
rescue => e
print_debug "Unable to load '#{file}' #{e}"
return nil
end
return nil unless File.exist? file
raw = File.read file
YAML.safe_load raw
rescue => e
print_debug "Unable to load '#{file}' #{e}"
nil
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
subhash = subkeys.inject(@config) do |hash, k|
hash[k]
end
return (subhash != nil and subhash.has_key?(lastkey)) ? subhash[lastkey] : nil
return nil if subhash.nil?
subhash.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
hash = {subkeys.shift.to_s => value}
subkeys.each{|v|
hash = {v.to_s => hash}
}
@config = @config.deep_merge(hash)
return true
return false if subkeys.empty?
hash = { subkeys.shift.to_s => value }
subkeys.each { |v| hash = {v.to_s => hash} }
@config = @config.deep_merge hash
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
return false if subkeys.empty?
lastkey = subkeys.pop
hash = @config
subkeys.each{|v|
hash = hash[v]
}
return (hash.delete(lastkey) == nil) ? false : true
subkeys.each {|v| hash = hash[v] }
hash.delete(lastkey).nil? ? false : true
end
#
# Load extensions configurations
#
def load_extensions_config
self.set('beef.extension', {})
Dir.glob("#{$root_dir}/extensions/*/config.yaml") do | cf |
y = self.load(cf)
if y != nil
y['beef']['extension'][y['beef']['extension'].keys.first]['path'] = cf.gsub(/config\.yaml/, '').gsub(/#{$root_dir}\//, '')
@config = y.deep_merge(@config)
else
set('beef.extension', {})
Dir.glob("#{$root_dir}/extensions/*/config.yaml") do |cf|
y = load(cf)
if y.nil?
print_error "Unable to load extension configuration '#{cf}'"
next
end
y['beef']['extension'][y['beef']['extension'].keys.first]['path'] = cf.gsub(/config\.yaml/, '').gsub(%r{#{$root_dir}/}, '')
@config = y.deep_merge(@config)
end
end
#
# Load module configurations
#
def load_modules_config
self.set('beef.module', {})
set('beef.module', {})
# support nested sub-categories, like browser/hooked_domain/ajax_fingerprint
module_configs = File.join("#{$root_dir}/modules/**", "config.yaml")
Dir.glob(module_configs) do | cf |
y = self.load(cf)
if y != nil
y['beef']['module'][y['beef']['module'].keys.first]['path'] = cf.gsub(/config\.yaml/, '').gsub(/#{$root_dir}\//, '')
@config = y.deep_merge(@config)
# API call for post module config load
BeEF::API::Registrar.instance.fire(BeEF::API::Configuration, 'module_configuration_load', y['beef']['module'].keys.first)
else
Dir.glob(module_configs) do |cf|
y = load(cf)
if y.nil?
print_error "Unable to load module configuration '#{cf}'"
next
end
y['beef']['module'][y['beef']['module'].keys.first]['path'] = cf.gsub('config.yaml', '').gsub(%r{#{$root_dir}/}, '')
@config = y.deep_merge @config
# API call for post module config load
BeEF::API::Registrar.instance.fire(
BeEF::API::Configuration,
'module_configuration_load',
y['beef']['module'].keys.first
)
end
end
end
end
end

View File

@@ -6,15 +6,18 @@
module BeEF
module Core
module Crypto
# @note the minimum length of the security token
TOKEN_MINIMUM_LENGTH = 15
#
# Generate a secure random 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
@@ -24,12 +27,15 @@ module Core
raise Exception::TypeError, "Token length is less than the minimum length enforced by the framework: #{TOKEN_MINIMUM_LENGTH}" if (token_length < TOKEN_MINIMUM_LENGTH)
# return random hex string
return OpenSSL::Random.random_bytes(token_length).unpack("H*")[0]
OpenSSL::Random.random_bytes(token_length).unpack("H*")[0]
end
#
# Generate a secure random token, 20 chars, used as an auth token for the RESTful API.
# After creation it's stored in the BeEF configuration object => conf.get('beef.api_token')
#
# @return [String] Security token
#
def self.api_token
config = BeEF::Core::Configuration.instance
token_length = 20
@@ -40,9 +46,11 @@ module Core
token
end
#
# Generates a unique identifier for DNS rules.
#
# @return [String] 8-character hex identifier
#
def self.dns_rule_id
id = nil
length = 4
@@ -56,7 +64,6 @@ module Core
id.to_s
end
end
end
end

View File

@@ -6,9 +6,7 @@
module BeEF
module Core
class Logger
include Singleton
# Constructor
@@ -19,12 +17,15 @@ module Core
# if notifications are enabled create a new instance
@notifications = BeEF::Extension::Notifications::Notifications unless @config.get('beef.extension.notifications.enable') == false
end
#
# Registers a new event in the logs
# @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
hb = hb.to_i
@@ -33,24 +34,23 @@ module Core
time_now = Time.now
# 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, '"Hooked Browser ID" needs to be an integer' if not hb.integer?
raise Exception::TypeError, '"from" needs to be a string' unless from.string?
raise Exception::TypeError, '"event" needs to be a string' unless event.string?
raise Exception::TypeError, '"Hooked Browser ID" needs to be an integer' unless hb.integer?
# logging the new event into the database
@logs.new(:type => "#{from}", :event => "#{event}", :date => time_now, :hooked_browser_id => hb).save
@logs.new(:type => from.to_s, :event => event.to_s, :date => time_now, :hooked_browser_id => hb).save
print_debug "Event: #{event}"
# if notifications are enabled send the info there too
if @notifications
@notifications.new(from, event, time_now, hb)
end
# return
true
end
private
@logs
end
end
end

View File

@@ -8,18 +8,22 @@ module BeEF
module Core
# @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.
# 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
@@ -27,22 +31,21 @@ module Core
BeEF::Core::Models::CommandModule.all.each do |mod|
db_modules << mod.name
end
config.get('beef.module').each{|k,v|
BeEF::Core::Models::CommandModule.new(:name => k, :path => "#{v['path']}module.rb").save if not db_modules.include? k
}
config.get('beef.module').each do |k, v|
h = { :name => k, :path => "#{v['path']}module.rb" }
BeEF::Core::Models::CommandModule.new(h).save unless db_modules.include? k
end
BeEF::Core::Models::CommandModule.all.each{|mod|
if config.get('beef.module.'+mod.name) != nil
config.set('beef.module.'+mod.name+'.db.id', mod.id)
config.set('beef.module.'+mod.name+'.db.path', mod.path)
BeEF::Core::Models::CommandModule.all.each do |mod|
unless config.get("beef.module.#{mod.name}").nil?
config.set "beef.module.#{mod.name}.db.id", mod.id
config.set "beef.module.#{mod.name}.db.path", mod.path
end
}
end
# Call Migration method
BeEF::API::Registrar.instance.fire(BeEF::API::Migration, 'migrate_commands')
BeEF::API::Registrar.instance.fire BeEF::API::Migration, 'migrate_commands'
end
end
end

View File

@@ -10,9 +10,7 @@ Thin::SERVER = nil
module BeEF
module Core
class Server
include Singleton
# @note Grabs the version of beef the framework is deployed on
@@ -43,46 +41,56 @@ module BeEF
'beef_public' => @configuration.get('beef.http.public'),
'beef_public_port' => @configuration.get('beef.http.public_port'),
'beef_hook' => @configuration.get('beef.http.hook_file'),
'beef_proto' => @configuration.get('beef.http.https.enable') == true ? "https" : "http",
'client_debug' => @configuration.get("beef.client_debug")
'beef_proto' => @configuration.get('beef.http.https.enable') == true ? 'https' : 'http',
'client_debug' => @configuration.get('beef.client_debug')
}
end
#
# 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, '"url" needs to be a string' unless url.string?
if args == nil
if args.nil?
@mounts[url] = http_handler_class
else
@mounts[url] = http_handler_class, *args
end
print_debug("Server: mounted handler '#{url}'")
print_debug "Server: mounted handler '#{url}'"
end
#
# 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)
raise Exception::TypeError, '"url" needs to be a string' unless 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)
@rack_app.remap @mounts
end
#
# Prepares 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)
mount(@configuration.get("beef.http.hook_file").to_s, BeEF::Core::Handlers::HookedBrowsers.new)
# Create handler for the initialization checks (Browser Details)
self.mount("/init", BeEF::Core::Handlers::BrowserDetails)
mount('/init', BeEF::Core::Handlers::BrowserDetails)
# 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)
@@ -111,10 +119,10 @@ module BeEF
openssl_version = OpenSSL::OPENSSL_VERSION
if openssl_version =~ / 1\.0\.1([a-f])? /
print_warning "Warning: #{openssl_version} is vulnerable to Heartbleed (CVE-2014-0160)."
print_more "Upgrade OpenSSL to version 1.0.1g or newer."
print_more 'Upgrade OpenSSL to version 1.0.1g or newer.'
end
cert_key = @configuration.get('beef.http.https.key')
cert_key = @configuration.get 'beef.http.https.key'
unless cert_key.start_with? '/'
cert_key = File.expand_path cert_key, $root_dir
end
@@ -123,7 +131,7 @@ module BeEF
exit 1
end
cert = @configuration.get('beef.http.https.cert')
cert = @configuration.get 'beef.http.https.cert'
unless cert.start_with? '/'
cert = File.expand_path cert, $root_dir
end
@@ -146,14 +154,16 @@ module BeEF
end
end
#
# Starts the BeEF http server
#
def start
@http_server.start
rescue RuntimeError => e
# port is in use
raise unless e.message.include? 'no acceptor'
print_error "Another process is already listening on port #{@configuration.get('beef.http.port')}, or you're trying to bind BeEF to an invalid IP."
print_error "Is BeEF already running? Exiting..."
print_error 'Is BeEF already running? Exiting...'
exit 127
end
end

View File

@@ -10,28 +10,28 @@ module BeEF
# @param [String] mod module key
# @return [Boolean] if the module key exists in BeEF's configuration
def self.is_present(mod)
return BeEF::Core::Configuration.instance.get('beef.module').has_key?(mod.to_s)
BeEF::Core::Configuration.instance.get('beef.module').key? mod.to_s
end
# Checks to see if module is enabled in configuration
# @param [String] mod module key
# @return [Boolean] if the module key is enabled in BeEF's configuration
def self.is_enabled(mod)
return (self.is_present(mod) and BeEF::Core::Configuration.instance.get("beef.module.#{mod}.enable") == true)
(is_present(mod) && BeEF::Core::Configuration.instance.get("beef.module.#{mod}.enable") == true)
end
# Checks to see if the module reports that it has loaded through the configuration
# @param [String] mod module key
# @return [Boolean] if the module key is loaded in BeEF's configuration
def self.is_loaded(mod)
return (self.is_enabled(mod) and BeEF::Core::Configuration.instance.get("beef.module.#{mod}.loaded") == true)
(is_enabled(mod) && BeEF::Core::Configuration.instance.get("beef.module.#{mod}.loaded") == true)
end
# Returns module class definition
# @param [String] mod module key
# @return [Class] the module class
def self.get_definition(mod)
return BeEF::Core::Command.const_get(BeEF::Core::Configuration.instance.get("beef.module.#{mod}.class"))
BeEF::Core::Command.const_get(BeEF::Core::Configuration.instance.get("beef.module.#{mod}.class"))
end
# Gets all module options
@@ -39,26 +39,30 @@ module BeEF
# @return [Hash] a hash of all the module options
# @note API Fire: get_options
def self.get_options(mod)
if BeEF::API::Registrar.instance.matched?(BeEF::API::Module, 'get_options', [mod])
options = BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'get_options', mod)
if BeEF::API::Registrar.instance.matched? BeEF::API::Module, 'get_options', [mod]
options = BeEF::API::Registrar.instance.fire BeEF::API::Module, 'get_options', mod
mo = []
options.each{|o|
if o[:data].kind_of?(Array)
mo += o[:data]
else
options.each do |o|
unless o[:data].is_a?(Array)
print_debug "API Warning: return result for BeEF::Module.get_options() was not an array."
next
end
}
mo += o[:data]
end
return mo
end
if self.check_hard_load(mod)
class_name = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.class")
class_symbol = BeEF::Core::Command.const_get(class_name)
if class_symbol and class_symbol.respond_to?(:options)
return class_symbol.options
end
unless check_hard_load mod
print_debug "get_opts called on unloaded module '#{mod}'"
return []
end
return []
class_name = BeEF::Core::Configuration.instance.get "beef.module.#{mod}.class"
class_symbol = BeEF::Core::Command.const_get class_name
return [] unless class_symbol && class_symbol.respond_to?(:options)
class_symbol.options
end
# Gets all module payload options
@@ -66,11 +70,8 @@ module BeEF
# @return [Hash] a hash of all the module options
# @note API Fire: get_options
def self.get_payload_options(mod,payload)
if BeEF::API::Registrar.instance.matched?(BeEF::API::Module, 'get_payload_options', [mod,nil])
options = BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'get_payload_options', mod,payload)
return options
end
return []
return [] unless BeEF::API::Registrar.instance.matched?(BeEF::API::Module, 'get_payload_options', [mod, nil])
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'get_payload_options', mod, payload)
end
# Soft loads a module
@@ -83,21 +84,29 @@ module BeEF
# API call for pre-soft-load module
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'pre_soft_load', mod)
config = BeEF::Core::Configuration.instance
mod_str = "beef.module.#{mod}"
if not config.get("#{mod_str}.loaded")
if not File.exists?("#{$root_dir}/#{config.get("#{mod_str}.path")}/module.rb")
print_debug "Unable to locate module file: #{config.get("#{mod_str}.path")}/module.rb"
return false
end
BeEF::Core::Configuration.instance.set("#{mod_str}.class", mod.capitalize)
self.parse_targets(mod)
print_debug "Soft Load module: '#{mod}'"
# API call for post-soft-load module
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'post_soft_load', mod)
return true
if config.get("#{mod_str}.loaded")
print_error "Unable to load module '#{mod}'"
return false
end
print_error "Unable to load module '#{mod}'"
return false
mod_path = "#{$root_dir}/#{config.get("#{mod_str}.path")}/module.rb"
unless File.exist? mod_path
print_debug "Unable to locate module file: #{mod_path}"
return false
end
BeEF::Core::Configuration.instance.set("#{mod_str}.class", mod.capitalize)
parse_targets mod
print_debug "Soft Load module: '#{mod}'"
# API call for post-soft-load module
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'post_soft_load', mod)
true
rescue => e
print_error "There was a problem soft loading the module '#{mod}'"
false
end
# Hard loads a module
@@ -110,69 +119,73 @@ module BeEF
# API call for pre-hard-load module
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'pre_hard_load', mod)
config = BeEF::Core::Configuration.instance
if not self.is_enabled(mod)
unless is_enabled mod
print_error "Hard load attempted on module '#{mod}' that is not enabled."
return false
end
mod_str = "beef.module.#{mod}"
begin
require config.get("#{mod_str}.path")+'module.rb'
if self.exists?(config.get("#{mod_str}.class"))
# start server mount point
BeEF::Core::Server.instance.mount("/command/#{mod}.js", BeEF::Core::Handlers::Commands, mod)
BeEF::Core::Configuration.instance.set("#{mod_str}.mount", "/command/#{mod}.js")
BeEF::Core::Configuration.instance.set("#{mod_str}.loaded", true)
print_debug "Hard Load module: '#{mod}'"
# API call for post-hard-load module
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'post_hard_load', mod)
return true
else
print_error "Hard loaded module '#{mod}' but the class BeEF::Core::Commands::#{mod.capitalize} does not exist"
end
rescue => e
BeEF::Core::Configuration.instance.set("#{mod_str}.loaded", false)
print_error "There was a problem loading the module '#{mod}'"
print_debug "Hard load module syntax error: #{e}"
mod_path = "#{config.get("#{mod_str}.path")}/module.rb"
require mod_path
unless exists? config.get("#{mod_str}.class")
print_error "Hard loaded module '#{mod}' but the class BeEF::Core::Commands::#{mod.capitalize} does not exist"
return false
end
return false
# start server mount point
BeEF::Core::Server.instance.mount("/command/#{mod}.js", BeEF::Core::Handlers::Commands, mod)
BeEF::Core::Configuration.instance.set("#{mod_str}.mount", "/command/#{mod}.js")
BeEF::Core::Configuration.instance.set("#{mod_str}.loaded", true)
print_debug "Hard Load module: '#{mod}'"
# API call for post-hard-load module
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'post_hard_load', mod)
true
rescue => e
BeEF::Core::Configuration.instance.set("#{mod_str}.loaded", false)
print_error "There was a problem loading the module '#{mod}'"
print_debug "Hard load module syntax error: #{e}"
false
end
# Checks to see if a module has been hard loaded, if not a hard load is attempted
# @param [String] mod module key
# @return [Boolean] if already hard loaded then true otherwise (see #hard_load)
def self.check_hard_load(mod)
if not self.is_loaded(mod)
return self.hard_load(mod)
end
return true
return true if is_loaded mod
hard_load mod
end
# Get module key by database ID
# @param [Integer] id module database ID
# @return [String] module key
def self.get_key_by_database_id(id)
ret = BeEF::Core::Configuration.instance.get('beef.module').select {|k, v| v.has_key?('db') and v['db']['id'].to_i == id.to_i }
return (ret.kind_of?(Array)) ? ret.first.first : ret.keys.first
ret = BeEF::Core::Configuration.instance.get('beef.module').select do |k, v|
v.key?('db') && v['db']['id'].to_i == id.to_i
end
ret.is_a?(Array) ? ret.first.first : ret.keys.first
end
# Get module key by module class
# @param [Class] c module class
# @return [String] module key
def self.get_key_by_class(c)
ret = BeEF::Core::Configuration.instance.get('beef.module').select {|k, v| v.has_key?('class') and v['class'].to_s == c.to_s }
return (ret.kind_of?(Array)) ? ret.first.first : ret.keys.first
ret = BeEF::Core::Configuration.instance.get('beef.module').select do |k, v|
v.key?('class') && v['class'].to_s.eql?(c.to_s)
end
ret.is_a?(Array) ? ret.first.first : ret.keys.first
end
# Checks to see if module class exists
# @param [String] mod module key
# @return [Boolean] returns whether or not the class exists
def self.exists?(mod)
begin
kclass = BeEF::Core::Command.const_get(mod.capitalize)
return kclass.is_a?(Class)
rescue NameError
return false
end
kclass = BeEF::Core::Command.const_get mod.capitalize
kclass.is_a? Class
rescue NameError
false
end
# Checks target configuration to see if browser / version / operating system is supported
@@ -187,104 +200,102 @@ module BeEF
# Please note this rating system has no correlation to the return constant value BeEF::Core::Constants::CommandModule::*
def self.support(mod, opts)
target_config = BeEF::Core::Configuration.instance.get("beef.module.#{mod}.target")
if not target_config or not opts.kind_of? Hash
return nil
end
if not opts.key?('browser')
return nil unless target_config
return nil unless opts.is_a? Hash
unless opts.key? 'browser'
print_error "BeEF::Module.support() was passed a hash without a valid browser constant"
return nil
end
results = []
target_config.each{|k,m|
m.each{|v|
target_config.each do |k, m|
m.each do |v|
case v
when String
if opts['browser'] == v
# if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
# rating += 1
# end
results << {'rating' => 2, 'const' => k}
end
when Hash
if opts['browser'] == v.keys.first or v.keys.first == BeEF::Core::Constants::Browsers::ALL
subv = v[v.keys.first]
rating = 1
#version check
if opts.key?('ver')
if subv.key?('min_ver')
if subv['min_ver'].kind_of? Fixnum and opts['ver'].to_i >= subv['min_ver']
rating += 1
else
break
end
end
if subv.key?('max_ver')
if (subv['max_ver'].kind_of? Fixnum and opts['ver'].to_i <= subv['max_ver']) or subv['max_ver'] == "latest"
rating += 1
else
break
end
end
end
# os check
if opts.key?('os') and subv.key?('os')
match = false
opts['os'].each{|o|
case subv['os']
when String
if o == subv['os']
rating += 1
match = true
elsif subv['os'] == BeEF::Core::Constants::Os::OS_ALL_UA_STR
match = true
end
when Array
subv['os'].each{|p|
if o == p
rating += 1
match = true
elsif p == BeEF::Core::Constants::Os::OS_ALL_UA_STR
match = true
end
}
end
}
if not match
break
end
end
if rating > 0
# if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
# rating += 1
# end
results << {'rating' => rating, 'const' => k}
end
end
end
if v == BeEF::Core::Constants::Browsers::ALL
when String
if opts['browser'] == v
# if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
# rating += 1
# end
results << {'rating' => 2, 'const' => k}
end
when Hash
break if opts['browser'] != v.keys.first && v.keys.first != BeEF::Core::Constants::Browsers::ALL
subv = v[v.keys.first]
rating = 1
if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
rating = 1
# version check
if opts.key?('ver')
if subv.key?('min_ver')
if subv['min_ver'].is_a?(Integer) && opts['ver'].to_i >= subv['min_ver']
rating += 1
else
break
end
end
if subv.key?('max_ver')
if (subv['max_ver'].is_a?(Integer) && opts['ver'].to_i <= subv['max_ver']) || subv['max_ver'] == 'latest'
rating += 1
else
break
end
end
end
results << {'rating' => rating, 'const' => k}
end
}
}
if results.count > 0
result = {}
results.each {|r|
if result == {}
result = {'rating' => r['rating'], 'const' => r['const']}
else
if r['rating'] > result['rating']
result = {'rating' => r['rating'], 'const' => r['const']}
# os check
if opts.key?('os') && subv.key?('os')
match = false
opts['os'].each do |o|
case subv['os']
when String
if o == subv['os']
rating += 1
match = true
elsif subv['os'].eql? BeEF::Core::Constants::Os::OS_ALL_UA_STR
match = true
end
when Array
subv['os'].each do |p|
if o == p
rating += 1
match = true
elsif p.eql? BeEF::Core::Constants::Os::OS_ALL_UA_STR
match = true
end
end
end
end
break unless match
end
if rating.positive?
# if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
# rating += 1
# end
results << {'rating' => rating, 'const' => k}
end
end
}
return result['const']
else
return BeEF::Core::Constants::CommandModule::VERIFIED_UNKNOWN
next unless v.eql? BeEF::Core::Constants::Browsers::ALL
rating = 1
if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
rating = 1
end
results << {'rating' => rating, 'const' => k}
end
end
return BeEF::Core::Constants::CommandModule::VERIFIED_UNKNOWN unless results.count.positive?
result = {}
results.each do |r|
if result == {} || r['rating'] > result['rating']
result = {'rating' => r['rating'], 'const' => r['const']}
end
end
result['const']
end
# Translates module target configuration
@@ -293,53 +304,46 @@ module BeEF
def self.parse_targets(mod)
mod_str = "beef.module.#{mod}"
target_config = BeEF::Core::Configuration.instance.get("#{mod_str}.target")
if target_config
targets = {}
target_config.each{|k,v|
begin
if BeEF::Core::Constants::CommandModule.const_defined?('VERIFIED_'+k.upcase)
key = BeEF::Core::Constants::CommandModule.const_get('VERIFIED_'+k.upcase)
if not targets.key?(key)
targets[key] = []
end
browser = nil
case v
when String
browser = self.match_target_browser(v)
if browser
targets[key] << browser
end
when Array
v.each{|c|
browser = self.match_target_browser(c)
if browser
targets[key] << browser
end
}
when Hash
v.each{|k,c|
browser = self.match_target_browser(k)
if browser
case c
when TrueClass
targets[key] << browser
when Hash
details = self.match_target_browser_spec(c)
if details
targets[key] << {browser => details}
end
end
end
}
return unless target_config
targets = {}
target_config.each do |k, v|
begin
next unless BeEF::Core::Constants::CommandModule.const_defined? "VERIFIED_#{k.upcase}"
key = BeEF::Core::Constants::CommandModule.const_get "VERIFIED_#{k.upcase}"
targets[key] = [] unless targets.key? key
browser = nil
case v
when String
browser = match_target_browser v
targets[key] << browser if browser
when Array
v.each do |c|
browser = match_target_browser c
targets[key] << browser if browser
end
when Hash
v.each do |k, c|
browser = match_target_browser k
next unless browser
case c
when TrueClass
targets[key] << browser
when Hash
details = match_target_browser_spec c
targets[key] << {browser => details} if details
end
end
rescue NameError
print_error "Module '#{mod}' configuration has invalid target status defined '#{k}'"
end
}
BeEF::Core::Configuration.instance.clear("#{mod_str}.target")
BeEF::Core::Configuration.instance.set("#{mod_str}.target", targets)
rescue NameError
print_error "Module '#{mod}' configuration has invalid target status defined '#{k}'"
end
end
BeEF::Core::Configuration.instance.clear "#{mod_str}.target"
BeEF::Core::Configuration.instance.set "#{mod_str}.target", targets
end
# Translates simple browser target configuration
@@ -347,19 +351,17 @@ module BeEF
# @param [String] v user defined browser
# @return [Constant] a BeEF browser constant
def self.match_target_browser(v)
browser = false
if v.class == String
begin
if BeEF::Core::Constants::Browsers.const_defined?(v.upcase)
browser = BeEF::Core::Constants::Browsers.const_get(v.upcase)
end
rescue NameError
print_error "Could not identify browser target specified as '#{v}'"
end
else
print_error "Invalid datatype passed to BeEF::Module.match_target_browser()"
unless v.class == String
print_error 'Invalid datatype passed to BeEF::Module.match_target_browser()'
return false
end
return browser
return false unless BeEF::Core::Constants::Browsers.const_defined? v.upcase
BeEF::Core::Constants::Browsers.const_get v.upcase
rescue NameError
print_error "Could not identify browser target specified as '#{v}'"
false
end
# Translates complex browser target configuration
@@ -367,35 +369,35 @@ module BeEF
# @param [Hash] v user defined browser hash
# @return [Hash] BeEF constants hash
def self.match_target_browser_spec(v)
browser = {}
if v.class == Hash
if v.key?('max_ver') and (v['max_ver'].is_a?(Fixnum) or v['max_ver'].is_a?(Float) or v['max_ver'] == "latest")
browser['max_ver'] = v['max_ver']
end
if v.key?('min_ver') and (v['min_ver'].is_a?(Fixnum) or v['min_ver'].is_a?(Float))
browser['min_ver'] = v['min_ver']
end
if v.key?('os')
case v['os']
when String
os = self.match_target_os(v['os'])
if os
browser['os'] = os
end
when Array
browser['os'] = []
v['os'].each{|c|
os = self.match_target_os(c)
if os
browser['os'] << os
end
}
end
end
else
print_error "Invalid datatype passed to BeEF::Module.match_target_browser_spec()"
unless v.class == Hash
print_error 'Invalid datatype passed to BeEF::Module.match_target_browser_spec()'
return {}
end
return browser
browser = {}
if v.key?('max_ver') && (v['max_ver'].is_a?(Integer) || v['max_ver'].is_a?(Float) || v['max_ver'] == 'latest')
browser['max_ver'] = v['max_ver']
end
if v.key?('min_ver') && (v['min_ver'].is_a?(Integer) || v['min_ver'].is_a?(Float))
browser['min_ver'] = v['min_ver']
end
return browser unless v.key? 'os'
case v['os']
when String
os = match_target_os v['os']
browser['os'] = os if os
when Array
browser['os'] = []
v['os'].each do |c|
os = match_target_os c
browser['os'] << os if os
end
end
browser
end
# Translates simple OS target configuration
@@ -403,84 +405,81 @@ module BeEF
# @param [String] v user defined OS string
# @return [Constant] BeEF OS Constant
def self.match_target_os(v)
os = false
if v.class == String
begin
if BeEF::Core::Constants::Os.const_defined?("OS_#{v.upcase}_UA_STR")
os = BeEF::Core::Constants::Os.const_get("OS_#{v.upcase}_UA_STR")
end
rescue NameError
print_error "Could not identify OS target specified as '#{v}'"
end
else
print_error "Invalid datatype passed to BeEF::Module.match_target_os()"
unless v.class == String
print_error 'Invalid datatype passed to BeEF::Module.match_target_os()'
return false
end
return os
return false unless BeEF::Core::Constants::Os.const_defined? "OS_#{v.upcase}_UA_STR"
BeEF::Core::Constants::Os.const_get "OS_#{v.upcase}_UA_STR"
rescue NameError
print_error "Could not identify OS target specified as '#{v}'"
false
end
# Executes a module
# @param [String] mod module key
# @param [String] hbsession hooked browser session
# @param [Array] opts array of module execute options (see #get_options)
# @return [Fixnum] the command_id associated to the module execution when info is persisted. nil if there are errors.
# @return [Integer] the command_id associated to the module execution when info is persisted. nil if there are errors.
# @note The return value of this function does not specify if the module was successful, only that it was executed within the framework
def self.execute(mod, hbsession, opts=[])
if not (self.is_present(mod) and self.is_enabled(mod))
unless is_present(mod) && is_enabled(mod)
print_error "Module not found '#{mod}'. Failed to execute module."
return nil
end
if BeEF::API::Registrar.instance.matched?(BeEF::API::Module, 'override_execute', [mod, nil,nil])
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'override_execute', mod, hbsession,opts)
if BeEF::API::Registrar.instance.matched? BeEF::API::Module, 'override_execute', [mod, nil, nil]
BeEF::API::Registrar.instance.fire BeEF::API::Module, 'override_execute', mod, hbsession, opts
# @note We return not_nil by default as we cannot determine the correct status if multiple API hooks have been called
return 'not_available' # @note using metasploit, we cannot know if the module execution was successful or not
# @note using metasploit, we cannot know if the module execution was successful or not
return 'not_available'
end
hb = BeEF::HBManager.get_by_session(hbsession)
if not hb
hb = BeEF::HBManager.get_by_session hbsession
unless hb
print_error "Could not find hooked browser when attempting to execute module '#{mod}'"
return nil
end
self.check_hard_load(mod)
command_module = self.get_definition(mod).new(mod)
check_hard_load mod
command_module = get_definition(mod).new(mod)
if command_module.respond_to?(:pre_execute)
command_module.pre_execute
end
h = self.merge_options(mod, [])
c = BeEF::Core::Models::Command.create(:data => self.merge_options(mod, opts).to_json,
:hooked_browser_id => hb.id,
:command_module_id => BeEF::Core::Configuration.instance.get("beef.module.#{mod}.db.id"),
:creationdate => Time.new.to_i
merge_options(mod, [])
c = BeEF::Core::Models::Command.create(
:data => merge_options(mod, opts).to_json,
:hooked_browser_id => hb.id,
:command_module_id => BeEF::Core::Configuration.instance.get("beef.module.#{mod}.db.id"),
:creationdate => Time.new.to_i
)
return c.id
c.id
end
# Merges default module options with array of custom options
# @param [String] mod module key
# @param [Hash] h module options customised by user input
# @param [Hash] opts module options customised by user input
# @return [Hash, nil] returns merged options
def self.merge_options(mod, h)
if self.is_present(mod)
self.check_hard_load(mod)
merged = []
defaults = self.get_options(mod)
defaults.each{|v|
mer = nil
h.each{|o|
if v.has_key?('name') and o.has_key?('name') and v['name'] == o['name']
mer = v.deep_merge(o)
end
}
if mer != nil
merged.push(mer)
else
merged.push(v)
end
}
return merged
end
return nil
end
def self.merge_options(mod, opts)
return nil unless is_present mod
check_hard_load mod
merged = []
defaults = get_options mod
defaults.each do |v|
mer = nil
opts.each do |o|
if v.key?('name') && o.key?('name') && v['name'] == o['name']
mer = v.deep_merge o
end
end
mer.nil? ? merged.push(v) : merged.push(mer)
end
merged
end
end
end

View File

@@ -9,50 +9,56 @@ module BeEF
# Return configuration hashes of all modules that are enabled
# @return [Array] configuration hashes of all enabled modules
def self.get_enabled
return BeEF::Core::Configuration.instance.get('beef.module').select {|k,v| v['enable'] == true and v['category'] != nil }
BeEF::Core::Configuration.instance.get('beef.module').select do |_k, v|
v['enable'] == true && !v['category'].nil?
end
end
# Return configuration hashes of all modules that are loaded
# @return [Array] configuration hashes of all loaded modules
def self.get_loaded
return BeEF::Core::Configuration.instance.get('beef.module').select {|k,v| v['loaded'] == true }
BeEF::Core::Configuration.instance.get('beef.module').select do |_k, v|
v['loaded'] == true
end
end
# Return an array of categories specified in module configuration files
# @return [Array] all available module categories sorted alphabetically
def self.get_categories
categories = []
BeEF::Core::Configuration.instance.get('beef.module').each {|k,v|
flatcategory = ""
if v['category'].kind_of?(Array)
# Therefore this module has nested categories (sub-folders), munge them together into a string with '/' characters, like a folder.
v['category'].each {|cat|
flatcategory << cat + "/"
}
BeEF::Core::Configuration.instance.get('beef.module').each_value do |v|
flatcategory = ''
if v['category'].is_a?(Array)
# Therefore this module has nested categories (sub-folders),
# munge them together into a string with '/' characters, like a folder.
v['category'].each do |cat|
flatcategory << "#{cat}/"
end
else
flatcategory = v['category']
end
if not categories.include?(flatcategory)
categories << flatcategory
end
}
return categories.sort.uniq #This is now uniqued, because otherwise the recursive function to build the json tree breaks if there are duplicates.
categories << flatcategory unless categories.include? flatcategory
end
# This is now uniqued, because otherwise the recursive function to build
# the json tree breaks if there are duplicates.
categories.sort.uniq
end
# Get all modules currently stored in the database
# @return [Array] DataMapper array of all BeEF::Core::Models::CommandModule's in the database
def self.get_stored_in_db
return BeEF::Core::Models::CommandModule.all(:order => [:id.asc])
BeEF::Core::Models::CommandModule.all(:order => [:id.asc])
end
# Loads all enabled modules
# Loads all enabled modules
# @note API Fire: post_soft_load
def self.load
BeEF::Core::Configuration.instance.load_modules_config
self.get_enabled.each { |k,v|
BeEF::Module.soft_load(k)
}
BeEF::API::Registrar.instance.fire(BeEF::API::Modules, 'post_soft_load')
get_enabled.each_key do |k|
BeEF::Module.soft_load k
end
BeEF::API::Registrar.instance.fire BeEF::API::Modules, 'post_soft_load'
end
end
end

View File

@@ -8,13 +8,14 @@ class Hash
# Recursively deep merge two hashes together
# @param [Hash] hash Hash to be merged
# @return [Hash] Combined hash
# @note Duplicate keys are overwritten by the value defined in the hash calling deep_merge (not the parameter hash)
# @note Duplicate keys are overwritten by the value defined
# in the hash calling deep_merge (not the parameter hash)
# @note http://snippets.dzone.com/posts/show/4706
def deep_merge(hash)
target = dup
hash.keys.each do |key|
if hash[key].is_a? Hash and self[key].is_a? Hash
target[key] = target[key].deep_merge(hash[key])
if hash[key].is_a?(Hash) && self[key].is_a?(Hash)
target[key] = target[key].deep_merge hash[key]
next
end
target[key] = hash[key]

View File

@@ -35,4 +35,9 @@ class Object
self.is_a?(Class)
end
# Returns true if the object is nil, and empty string, or empty array
# @return [Boolean]
def blank?
self.respond_to?(:empty?) ? !!empty? : !self
end
end