#1# Copyright (c) 2006-2025 Wade Alcorn - [email protected]2# Browser Exploitation Framework (BeEF) - https://beefproject.com3# See the file 'doc/COPYING' for copying permission4#56module BeEF7module Core8class Configuration9attr_accessor :config1011# antisnatchor: still a singleton, but implemented by hand because we want to have only one instance12# of the Configuration object while having the possibility to specify a parameter to the constructor.13# This is why we don't use anymore the default Ruby implementation -> include Singleton14def self.instance15@@instance16end1718# Loads the default configuration system19# @param [String] configuration_file Configuration file to be loaded,20# by default loads $root_dir/config.yaml21def initialize(config)22raise TypeError, "'config' needs to be a string" unless config.is_a?(String)23raise TypeError, "Configuration file '#{config}' cannot be found" unless File.exist? config2425begin26# open base config27@config = load(config)28@config.default = nil29@@config = config30rescue StandardError => e31print_error "Fatal Error: cannot load configuration file '#{config}' : #{e.message}"32print_more e.backtrace33exit(1)34end3536@@instance = self37end3839# Loads yaml file40# @param [String] file YAML file to be loaded41# @return [Hash] YAML formatted hash42def load(file)43return nil unless File.exist?(file)44YAML.safe_load(File.binread(file))45end4647#48# @note balidate the configuration file49#50def validate51if @config.empty?52print_error 'Configuration file is empty'53return54end5556if @config['beef'].nil?57print_error "Configuration file is malformed: 'beef' is nil"58return59end6061if @config['beef']['credentials'].nil?62print_error "Configuration file is malformed: 'beef.credentials' is nil"63return64end6566if @config['beef']['http'].nil?67print_error "Configuration file is malformed: 'beef.http' is nil"68return69end7071return unless validate_public_config_variable?(@config)7273# Note for developers:74# The configuration path 'beef.http.public_port' is deprecated.75# Use the new format for public_port variables as described in the BeEF project documentation.76# Refer to the BeEF configuration guide for the web server configuration details:77# https://github.com/beefproject/beef/wiki/Configuration#web-server-configuration78if @config['beef']['http']['public_port']79return80end8182true83end8485#86# Returns the configuration value for the http server host87# If nothing is set it should default to 0.0.0.0 (all interfaces)88def local_host89get('beef.http.host') || '0.0.0.0'90end9192#93# Returns the configuration value for the http server port94# If nothing is set it should default to 300095def local_port96get('beef.http.port') || '3000'97end9899#100# Return the local protocol101# if nothing is set default to http102def local_proto103local_https_enabled ? 'https' : 'http'104end105106#107# Returns the configuration value for the local https enabled108# If nothing is set it should default to false109def local_https_enabled110get('beef.http.https.enable') || false111end112113#114# Returns the configuration value for the http server host115def public_host116get('beef.http.public.host')117end118119#120# Returns the beef host which is used by external resources121# e.g. hooked browsers122def beef_host123public_host || local_host124end125126#127# Returns the beef port which is used by external resource128# e.g. hooked browsers129def beef_port130public_port || local_port131end132133def public_enabled?134!get('beef.http.public.host').nil?135end136137#138# Returns the beef protocol that is used by external resources139# e.g. hooked browsers140def beef_proto141if public_enabled? && public_https_enabled?142'https'143elsif public_enabled? && !public_https_enabled?144'http'145elsif !public_enabled?146local_proto147end148end149150#151# Returns the beef scheme://host:port for external resources152# e.g. hooked browsers153def beef_url_str154"#{beef_proto}://#{beef_host}:#{beef_port}"155end156157# Returns the hook path value stored in the config file158#159# @return [String] hook file path160def hook_file_path161get('beef.http.hook_file') || '/hook.js'162end163164# Returns the url to the hook file165#166# @return [String] the url string167def hook_url168"#{beef_url_str}#{hook_file_path}"169end170171# Returns the configuration value for the http server port172# If nothing is set it should default to 3000173def public_port174return get('beef.http.public.port') unless get('beef.http.public.port').nil?175176return '443' if public_https_enabled?177return '80' unless public_host.nil?178179nil180end181182#183# Returns the configuration value for the local https enabled184# If nothing is set it should default to false185def public_https_enabled?186get('beef.http.public.https') || false187end188189#190# Returns the value of a selected key in the configuration file.191# @param [String] key Key of configuration item192# @return [Hash|String] The resulting value stored against the 'key'193#194def get(key)195subkeys = key.split('.')196lastkey = subkeys.pop197subhash = subkeys.inject(@config) do |hash, k|198hash[k]199end200return nil if subhash.nil?201202subhash.key?(lastkey) ? subhash[lastkey] : nil203end204205#206# Sets the give key value pair to the config instance207# @param [String] key The configuration key208# @param value The value to be stored against the 'key'209# @return [Boolean] If the store procedure was successful210#211def set(key, value)212subkeys = key.split('.').reverse213return false if subkeys.empty?214215hash = { subkeys.shift.to_s => value }216subkeys.each { |v| hash = { v.to_s => hash } }217@config = @config.deep_merge hash218true219end220221#222# Clears the given key hash223# @param [String] key Configuration key to be cleared224# @return [Boolean] If the configuration key was cleared225#226def clear(key)227subkeys = key.split('.')228return false if subkeys.empty?229230lastkey = subkeys.pop231hash = @config232subkeys.each { |v| hash = hash[v] }233hash.delete(lastkey).nil? ? false : true234end235236#237# Load extensions configurations238#239def load_extensions_config240set('beef.extension', {})241Dir.glob("#{$root_dir}/extensions/*/config.yaml") do |cf|242y = load(cf)243if y.nil?244print_error "Unable to load extension configuration '#{cf}'"245next246end247248y['beef']['extension'][y['beef']['extension'].keys.first]['path'] = cf.gsub(/config\.yaml/, '').gsub(%r{#{$root_dir}/}, '')249@config = y.deep_merge(@config)250end251end252253#254# Load module configurations255#256def load_modules_config257set('beef.module', {})258# support nested sub-categories, like browser/hooked_origin/ajax_fingerprint259module_configs = File.join("#{$root_dir}/modules/**", 'config.yaml')260Dir.glob(module_configs) do |cf|261y = load(cf)262if y.nil?263print_error "Unable to load module configuration '#{cf}'"264next265end266267y['beef']['module'][y['beef']['module'].keys.first]['path'] = cf.gsub('config.yaml', '').gsub(%r{#{$root_dir}/}, '')268@config = y.deep_merge @config269# API call for post module config load270BeEF::API::Registrar.instance.fire(271BeEF::API::Configuration,272'module_configuration_load',273y['beef']['module'].keys.first274)275end276end277278private279280# Note for developers:281# The configuration path 'beef.http.public' is deprecated.282# Use the new format for public variables as described in the BeEF project documentation.283# Refer to the BeEF configuration guide for the web server configuration details:284# https://github.com/beefproject/beef/wiki/Configuration#web-server-configuration285def validate_public_config_variable?(config)286return true if config['beef']['http']['public'].is_a?(Hash) ||287config['beef']['http']['public'].is_a?(NilClass)288289false290end291end292end293end294295296