#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 Core8#9# @note This module contains a list of utils functions to use when writing commands10#11module CommandUtils12#13# Format a string to support multiline in javascript.14# @param [String] text String to convert15#16# @return [String] Formatted string17#18def format_multiline(text)19text.gsub(/\n/, '\n')20end21end2223#24# @note The Command Module Context is being used when evaluating code in eruby.25# In other words, we use that code to add funky functions to the26# javascript templates of our commands.27#28class CommandContext < Erubis::Context29include BeEF::Core::CommandUtils30#31# Constructor32# @param [Hash] hash33#34def initialize(hash = nil)35super(hash)36end37end3839#40# @note This class is the base class for all command modules in the framework.41# Two instances of this object are created during the execution of command module.42#43class Command44attr_reader :datastore, :path, :default_command_url, :beefjs_components, :friendlyname,45:config46attr_accessor :zombie, :command_id, :session_id4748include BeEF::Core::CommandUtils49include BeEF::Core::Constants::Browsers50include BeEF::Core::Constants::CommandModule5152#53# Super class controller54#55# @param [String] key command module key56#57def initialize(key)58@config = BeEF::Core::Configuration.instance5960@key = key61@datastore = {}62@friendlyname = @config.get("beef.module.#{key}.name")63@output = ''64@path = @config.get("beef.module.#{key}.path")65@default_command_url = config.get("beef.module.#{key}.mount")66@id = @config.get("beef.module.#{key}.db.id")67@auto_update_zombie = false68@results = {}69@beefjs_components = {}70end7172#73# This function is called just before the instructions are sent to hooked browser.74#75def pre_send; end7677#78# Callback method. This function is called when the hooked browser sends results back.79#80def callback; end8182#83# If the command requires some data to be sent back, this function will process them.84# @param [] head85# @param [Hash] params Hash of parameters86# @todo Determine argument "head" type87#88def process_zombie_response(head, params); end8990#91# Returns true if the command needs configurations to work. False if not.92# @deprecated This command should not be used since the implementation of the new configuration system93#94def needs_configuration?95!@datastore.nil?96end9798#99# Returns information about the command in a JSON format.100# @return [String] JSON formatted string101#102def to_json(*_args)103{104'Name' => @friendlyname,105'Description' => BeEF::Core::Configuration.instance.get("beef.module.#{@key}.description"),106'Category' => BeEF::Core::Configuration.instance.get("beef.module.#{@key}.category"),107'Data' => BeEF::Module.get_options(@key)108}.to_json109end110111#112# Builds the 'datastore' attribute of the command which is used to generate javascript code.113# @param [Hash] data Data to be inserted into the datastore114# @todo TODO Confirm argument "data" type115#116def build_datastore(data)117@datastore = JSON.parse data118rescue StandardError => e119print_error "Could not build datastore: #{e.message}"120end121122#123# Sets the datastore for the callback function. This function is meant to be called by the CommandHandler124# @param [Hash] http_params HTTP parameters125# @param [Hash] http_headers HTTP headers126#127def build_callback_datastore(result, command_id, beefhook, http_params, http_headers)128@datastore = { 'http_headers' => {} } # init the datastore129130if !http_params.nil? && !http_headers.nil?131# get, check and add the http_params to the datastore132http_params.keys.each do |http_params_key|133unless BeEF::Filters.is_valid_command_module_datastore_key? http_params_key134print_error 'http_params_key is invalid'135return136end137138http_params_value = Erubis::XmlHelper.escape_xml http_params[http_params_key]139unless BeEF::Filters.is_valid_command_module_datastore_param?(http_params_value)140print_error 'http_params_value is invalid'141return142end143144# add the checked key and value to the datastore145@datastore[http_params_key] = http_params_value146end147148# get, check and add the http_headers to the datastore149http_headers.keys.each do |http_header_key|150unless BeEF::Filters.is_valid_command_module_datastore_key? http_header_key151print_error 'http_header_key is invalid'152return153end154155http_header_value = Erubis::XmlHelper.escape_xml http_headers[http_header_key][0]156unless BeEF::Filters.is_valid_command_module_datastore_param? http_header_value157print_error 'http_header_value is invalid'158return159end160161# add the checked key and value to the datastore162@datastore['http_headers'][http_header_key] = http_header_value163end164end165166@datastore['results'] = result167@datastore['cid'] = command_id168@datastore['beefhook'] = beefhook169end170171#172# Returns the output of the command. These are the actual instructions sent to the browser.173# @return [String] The command output174#175def output176f = "#{@path}command.js"177unless File.exist? f178print_error "File does not exist: #{f}"179return180end181182command = BeEF::Core::Models::Command.find(@command_id)183184@eruby = Erubis::FastEruby.new(File.read(f))185186# data = BeEF::Core::Configuration.instance.get "beef.module.#{@key}"187cc = BeEF::Core::CommandContext.new188cc['command_url'] = @default_command_url189cc['command_id'] = @command_id190JSON.parse(command['data']).each do |v|191cc[v['name']] = v['value']192end193194execute if respond_to?(:execute)195196@output = @eruby.evaluate cc197@output198end199200# Saves the results received from the hooked browser201# @param [Hash] results Results from hooked browser202def save(results)203@results = results204end205206#207# If nothing else than the file is specified,208# the function will map the file to a random path without any extension.209#210# @param [String] file File to be mounted211# @param [String] path URL path to mounted file212# @param [String] extension URL extension213# @param [Integer] count The amount of times this file can be accessed before being automatically unmounted214# @deprecated This function is possibly deprecated in place of the API215#216def map_file_to_url(file, path = nil, extension = nil, count = 1)217BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind(file, path, extension, count)218end219220#221# Tells the framework to load a specific module of the BeEFJS library that the command will be using.222# @param [String] component String of BeEFJS component to load223# @note Example: use 'beef.net.local'224#225def use(component)226return if @beefjs_components.include? component227228component_path = '/' + component229component_path.gsub!(/beef./, '')230component_path.gsub!(/\./, '/')231component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js"232233raise "Invalid beefjs component for command module #{@path}" unless File.exist? component_path234235@beefjs_components[component] = component_path236end237238# @todo TODO Document239def oc_value(name)240option = BeEF::Core::Models::OptionCache.where(name: name).first241return nil unless option242243option.value244end245246# @todo TODO Document247def apply_defaults248@datastore.each do |opt|249opt['value'] = oc_value(opt['name']) || opt['value']250end251end252253@use_template254@eruby255@update_zombie256@results257end258end259end260261262