diff --git a/core/main/autorun_engine/engine.rb b/core/main/autorun_engine/engine.rb index a1a335573..82f3753d9 100644 --- a/core/main/autorun_engine/engine.rb +++ b/core/main/autorun_engine/engine.rb @@ -44,6 +44,10 @@ module BeEF mods_codes = Array.new mods_conditions = Array.new + # 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.first(:name => cmd_mod['name']) options = [] @@ -53,7 +57,9 @@ module BeEF replace_input = true if v == '<>' end - command_body = prepare_command(mod, options, hb_id, replace_input) + 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']) @@ -62,11 +68,12 @@ 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) + 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) + 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 @@ -74,6 +81,7 @@ module BeEF :session => hb_session, :mod_count => modules.length, :mod_successful => 0, + :rule_token => rule_token, :mod_body => wrapper, :is_sent => false, :rule_id => rule_id @@ -93,14 +101,14 @@ module BeEF # setTimeout(module_three(), 3000); # Note: no result status is checked here!! Useful if you just want to launch a bunch of modules without caring # what their status will be (for instance, a bunch of XSRFs on a set of targets) - def prepare_sequential_wrapper(mods, order, delay) + def prepare_sequential_wrapper(mods, order, delay, rule_token) wrapper = '' delayed_exec = '' c = 0 - while c < mods.length - delayed_exec += %Q| setTimeout("#{mods[order[c]][:mod_name]}();", #{delay[c]}); | - wrapped_mod = "#{mods[order[c]][:mod_body]}\n" + delayed_exec += %Q| setTimeout("#{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 c += 1 end @@ -124,7 +132,7 @@ module BeEF # Note: Useful in situations where you want to launch 2 modules, where the second one will execute only # 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) + def prepare_nested_forward_wrapper(mods, code, conditions, order, rule_token) wrapper, delayed_exec = '','' delayed_exec_footers = Array.new c = 0 @@ -148,8 +156,8 @@ module BeEF if c == 0 # this is the first wrapper to prepare delayed_exec += %Q| - function #{mods[order[c]][:mod_name]}_f(){ - #{mods[order[c]][:mod_name]}(); + function #{mods[order[c]][:mod_name]}_#{rule_token}_f(){ + #{mods[order[c]][:mod_name]}_#{rule_token}(); // TODO add timeout to prevent infinite loops function isResReady(mod_result, start){ @@ -165,8 +173,8 @@ module BeEF } var status = mod_result[0]; if(#{conditions[i]}){ - #{mods[order[i]][:mod_name]}_can_exec = true; - #{mods[order[c]][:mod_name]}_mod_output = mod_result[1]; + #{mods[order[i]][:mod_name]}_#{rule_token}_can_exec = true; + #{mods[order[c]][:mod_name]}_#{rule_token}_mod_output = mod_result[1]; | delayed_exec_footer = %Q| @@ -174,20 +182,22 @@ module BeEF } } var start = (new Date()).getTime(); - var resultReady = setInterval(function(){var start = (new Date()).getTime(); isResReady(#{mods[order[c]][:mod_name]}_mod_output, start);},#{@result_poll_interval}); + var resultReady = setInterval(function(){var start = (new Date()).getTime(); isResReady(#{mods[order[c]][:mod_name]}_#{rule_token}_mod_output, start);},#{@result_poll_interval}); } - #{mods[order[c]][:mod_name]}_f(); + #{mods[order[c]][:mod_name]}_#{rule_token}_f(); | 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}") + # this is one of the wrappers in the middle of the chain delayed_exec += %Q| - function #{mods[order[c]][:mod_name]}_f(){ - if(#{mods[order[c]][:mod_name]}_can_exec){ + function #{mods[order[c]][:mod_name]}_#{rule_token}_f(){ + if(#{mods[order[c]][:mod_name]}_#{rule_token}_can_exec){ #{code_snippet} - #{mods[order[c]][:mod_name]}(#{mod_input}); + #{mods[order[c]][:mod_name]}_#{rule_token}(#{mod_input}); function isResReady(mod_result, start){ if (mod_result === null && parseInt(((new Date().getTime()) - start)) < #{@result_poll_timeout}){ // loop @@ -201,8 +211,8 @@ module BeEF } var status = mod_result[0]; if(#{conditions[i]}){ - #{mods[order[i]][:mod_name]}_can_exec = true; - #{mods[order[c]][:mod_name]}_mod_output = mod_result[1]; + #{mods[order[i]][:mod_name]}_#{rule_token}_can_exec = true; + #{mods[order[c]][:mod_name]}_#{rule_token}_mod_output = mod_result[1]; | delayed_exec_footer = %Q| @@ -210,26 +220,28 @@ module BeEF } } var start = (new Date()).getTime(); - var resultReady = setInterval(function(){ isResReady(#{mods[order[c]][:mod_name]}_mod_output, start);},#{@result_poll_interval}); + var resultReady = setInterval(function(){ isResReady(#{mods[order[c]][:mod_name]}_#{rule_token}_mod_output, start);},#{@result_poll_interval}); } } - #{mods[order[c]][:mod_name]}_f(); + #{mods[order[c]][:mod_name]}_#{rule_token}_f(); | 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}") # this is the last wrapper to prepare delayed_exec += %Q| - function #{mods[order[c]][:mod_name]}_f(){ - if(#{mods[order[c]][:mod_name]}_can_exec){ + function #{mods[order[c]][:mod_name]}_#{rule_token}_f(){ + if(#{mods[order[c]][:mod_name]}_#{rule_token}_can_exec){ #{code_snippet} - #{mods[order[c]][:mod_name]}(#{mod_input}); + #{mods[order[c]][:mod_name]}_#{rule_token}(#{mod_input}); } } - #{mods[order[c]][:mod_name]}_f(); + #{mods[order[c]][:mod_name]}_#{rule_token}_f(); | end - wrapped_mod = "#{mods[order[c]][:mod_body]}\n" + 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 c += 1 end @@ -242,7 +254,7 @@ module BeEF # 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. - def prepare_command(mod, options, hb_id, replace_input) + def prepare_command(mod, options, hb_id, replace_input, rule_token) config = BeEF::Core::Configuration.instance begin command = BeEF::Core::Models::Command.new( @@ -285,11 +297,11 @@ module BeEF replace_input ? mod_input = 'mod_input' : mod_input = '' result = %Q| - var #{mod.name} = function(#{mod_input}){ + var #{mod.name}_#{rule_token} = function(#{mod_input}){ #{clean_command_body(command_body, replace_input)} }; - var #{mod.name}_can_exec = false; - var #{mod.name}_mod_output = null; + var #{mod.name}_#{rule_token}_can_exec = false; + var #{mod.name}_#{rule_token}_mod_output = null; | return {:mod_name => mod.name, :mod_body => result} @@ -349,7 +361,7 @@ module BeEF 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.dd" + print_error "[ARE] There is likely a problem with the module's command.js parsing. Check Engine.clean_command_body" end end diff --git a/core/main/autorun_engine/models/execution.rb b/core/main/autorun_engine/models/execution.rb index b89fd0372..6dc0cef7c 100644 --- a/core/main/autorun_engine/models/execution.rb +++ b/core/main/autorun_engine/models/execution.rb @@ -22,6 +22,7 @@ module BeEF # By default Text is only 65K, so field length increased to 1 MB property :mod_body, Text, :length => 1024000 # entire command module(s) body to be sent property :exec_time, String, :length => 15 # timestamp of ruleset triggering + property :rule_token, String, :length => 10 # unique token to be appended to wrapper function names property :is_sent, Boolean end end