From 1b1c8543b3f2881a8c8adbf6a95f895e60bfa267 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 18 Feb 2024 21:05:02 -0500 Subject: [PATCH] Added REST PATCH endpoint for updating ARE rules. --- core/main/autorun_engine/rule_loader.rb | 93 +++++++++++++++++++ core/main/rest/handlers/autorun_engine.rb | 20 ++++ .../javascript/ui/panel/AutoRunRuleForm.js | 24 ++++- .../media/javascript/ui/panel/AutoRunTab.js | 6 +- 4 files changed, 138 insertions(+), 5 deletions(-) diff --git a/core/main/autorun_engine/rule_loader.rb b/core/main/autorun_engine/rule_loader.rb index e42a6082b..31748b0e3 100644 --- a/core/main/autorun_engine/rule_loader.rb +++ b/core/main/autorun_engine/rule_loader.rb @@ -105,6 +105,99 @@ module BeEF { 'success' => false, 'error' => e.message } end + # Update an ARE rule set. + # @param [Hash] ARE rule ID. + # @param [Hash] ARE ruleset as JSON + # @return [Hash] {"success": Boolean, "rule_id": Integer, "error": String} + def update_rule_json(id, data) + # Quite similar in implementation to load_rule_json. Might benefit from a refactor. + 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'] + execution_order = data['execution_order'] + execution_delay = data['execution_delay'] + chain_mode = data['chain_mode'] || 'sequential' + + begin + BeEF::Core::AutorunEngine::Parser.instance.parse( + name, + author, + browser, + browser_version, + os, + os_version, + modules, + execution_order, + execution_delay, + chain_mode + ) + rescue => e + print_error("[ARE] Error updating ruleset (#{name}): #{e.message}") + return { 'success' => false, 'error' => e.message } + end + + existing_rule = BeEF::Core::Models::Rule.where( + name: name, + author: author, + browser: browser, + browser_version: browser_version, + os: os, + os_version: os_version, + modules: modules.to_json, + execution_order: execution_order.to_s, + execution_delay: execution_delay.to_s, + chain_mode: chain_mode + ).first + + unless existing_rule.nil? + msg = "Duplicate rule already exists in the database (ID: #{existing_rule.id})" + print_info("[ARE] Skipping ruleset (#{name}): #{msg}") + return { 'success' => false, 'error' => msg } + end + old_are_rule = BeEF::Core::Models::Rule.find_by(id: id) + + old_are_rule.update( + name: name, + author: author, + browser: browser, + browser_version: browser_version, + os: os, + os_version: os_version, + modules: modules.to_json, + execution_order: execution_order.to_s, + execution_delay: execution_delay.to_s, + chain_mode: chain_mode + ) + + print_info("[ARE] Ruleset (#{name}) updated successfully.") + + if @debug_on + print_more "Target Browser: #{browser} (#{browser_version})" + print_more "Target OS: #{os} (#{os_version})" + print_more 'Modules to run:' + 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: #{execution_order}" + print_more "Exec delay: #{exec_delay}" + end + + { 'success' => true } + rescue TypeError, ArgumentError => e + print_error("[ARE] Failed to update ruleset (#{name}): #{e.message}") + { 'success' => false, 'error' => e.message } + end + # Load an ARE ruleset from file # @param [String] JSON ARE ruleset file path def load_rule_file(json_rule_path) diff --git a/core/main/rest/handlers/autorun_engine.rb b/core/main/rest/handlers/autorun_engine.rb index 2b9278d93..fc2ba5467 100644 --- a/core/main/rest/handlers/autorun_engine.rb +++ b/core/main/rest/handlers/autorun_engine.rb @@ -84,6 +84,26 @@ module BeEF { 'success' => false, 'error' => e.message }.to_json end + # + # Update a ruleset + # + patch '/rule/:rule_id' do + rule_id = params[:rule_id] + rule = BeEF::Core::Models::Rule.find(rule_id) + raise InvalidParameterError, 'id' if rule.nil? + data = JSON.parse request.body.read + rloader = BeEF::Core::AutorunEngine::RuleLoader.instance + rloader.update_rule_json(rule_id, data) + + { 'success' => true }.to_json + rescue InvalidParameterError => e + print_error e.message + halt 400 + rescue StandardError => e + print_error "Internal error while updating Autorun rule: #{e.message}" + { 'success' => false, 'error' => e.message }.to_json + end + # # Run a specified rule on all online hooked browsers (if the zombie matches the rule). # Offline hooked browsers are ignored diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index bcfc7af04..f1ac899b7 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -1,4 +1,19 @@ - +const areNotificationUpdateTest = { + "name": "Display an alert-----", + "author": "mgeeky", + "modules": [ + { + "name": "alert_dialog", + "condition": null, + "options": { + "text":"You've been BeEFed ;>" + } + } + ], + "execution_order": [0], + "execution_delay": [0], + "chain_mode": "nested-forward" +}; /** * Form for the user to read, update and delete a specific Auto Run rule. * @@ -8,6 +23,11 @@ */ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { + function handleUpdateRule() { + // TODO: Get data from form inputs. + updateFn(areNotificationUpdateTest); + } + AutoRunRuleForm.superclass.constructor.call(this, { padding:'10 10 10 10', items: [{ @@ -35,7 +55,7 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { handler: deleteFn }, { text: 'Save', - handler: updateFn + handler: handleUpdateRule }, { text: 'Add New', diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index f9daa2c94..516967ff0 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -87,9 +87,9 @@ AutoRunTab = function() { async function updateRule(id, newRuleData) { // TODO: Check if this API endpoint even exists. const res = await fetch(`/api/autorun/rule/${id}?token=${token}`, { - method: 'PUT', + method: 'PATCH', headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(areNotification) + body: JSON.stringify(newRuleData) }); if (!res.ok) { console.error(`Failed when adding a new rule with status ${res.status}.`); @@ -106,7 +106,7 @@ AutoRunTab = function() { ruleForm = new AutoRunRuleForm( rules[i], function() {deleteRule(rules[i].id)}, - function(newRuleData) {updateRule(rules[i].id, newRuleData)}, // TODO: Implement rule update. + function(newRuleData) {updateRule(rules[i].id, newRuleData)}, addRule ); container.add(ruleForm);