From 920a5c5a5d8ec0bb1c0ae276103d921f087faad9 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 15 Feb 2024 20:39:38 -0500 Subject: [PATCH 01/21] Created a new panel in the UI for auto run elements. --- extensions/admin_ui/api/handler.rb | 1 + .../media/javascript/ui/panel/AutoRunTab.js | 19 +++++++++++++++++++ .../media/javascript/ui/panel/MainPanel.js | 11 +++++++++++ 3 files changed, 31 insertions(+) create mode 100644 extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js diff --git a/extensions/admin_ui/api/handler.rb b/extensions/admin_ui/api/handler.rb index 374b2cd0a..7f25af979 100644 --- a/extensions/admin_ui/api/handler.rb +++ b/extensions/admin_ui/api/handler.rb @@ -90,6 +90,7 @@ module BeEF ui/panel/tabs/ZombieTabRTC.js ui/panel/Logout.js ui/panel/WelcomeTab.js + ui/panel/AutoRunTab.js ui/panel/ModuleSearching.js ] diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js new file mode 100644 index 000000000..f06f44e8b --- /dev/null +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -0,0 +1,19 @@ + +AutoRunTab = function() { + + autoRunHtml = " \ +
\ +

My custom panel :)

\ +
\ + "; + + AutoRunTab.superclass.constructor.call(this, { + region:'center', + padding:'10 10 10 10', + html: autoRunHtml, + autoScroll: true, + border: false + }); +}; + +Ext.extend(AutoRunTab, Ext.Panel, {}); \ No newline at end of file diff --git a/extensions/admin_ui/media/javascript/ui/panel/MainPanel.js b/extensions/admin_ui/media/javascript/ui/panel/MainPanel.js index bd84de36c..43c8dcf42 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/MainPanel.js +++ b/extensions/admin_ui/media/javascript/ui/panel/MainPanel.js @@ -38,6 +38,8 @@ MainPanel = function(){ this.welcome_tab = new WelcomeTab; + this.auto_run_tab = new AutoRunTab; + MainPanel.superclass.constructor.call(this, { id:'main-tabs', activeTab:0, @@ -76,6 +78,15 @@ MainPanel = function(){ items:[ this.zombies_grid ] + }, + { + id:'autorun-view', + title:'Auto Run', + layout:'border', + hideMode:'offsets', + items:[ + this.auto_run_tab + ] }] }); From 459a99dce44427e390827fdcd965a457ce320c5f Mon Sep 17 00:00:00 2001 From: root Date: Fri, 16 Feb 2024 00:17:33 -0500 Subject: [PATCH 02/21] Query the list of ARE rules on render. Display the count. --- .../media/javascript/ui/panel/AutoRunTab.js | 65 ++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index f06f44e8b..547c916ea 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -1,18 +1,67 @@ +/** + * Asynchronously returns the currently active rules in an array. + * Empty array means no rules are active. + * null if there was an error. + */ +getCurrentRules = async function(token) { + console.log(`token = ${token}`); + + try { + var res = await fetch(`/api/autorun/rule/list/all?token=${token}`) + console.log(res.body); + console.log("Successfully retrieved active rules."); + if (res.body.success === true && Array.isArray(res.body.rules)) { + console.log(res.body.rules); + return res.body.rules; + } else { + console.log("No active rules."); + return []; + } + } catch(error) { + console.error(error); + console.error("Failed to get rules."); + return null; + } +} + AutoRunTab = function() { + // RESTful API token. + var token = BeefWUI.get_rest_token(); - autoRunHtml = " \ -
\ -

My custom panel :)

\ -
\ - "; + // Setup container element. + var container = new Ext.Panel({ + style: { + font: '11px tahoma,arial,helvetica,sans-serif', + width: '500px' + }, + html: "

Loading Auto Run rules...

", + listeners: { + afterrender: updateRules + } + }); + + async function updateRules() { + const rules = await getCurrentRules(token); + if (rules !== null) { + console.log(`

Number of Auto Run rules enabled: ${rules.length}.

`); + container.update(`

Number of Auto Run rules enabled: ${rules.length}.

`); + + //ruleTitle = document.createElement('h4'); + //ruleTitle.innerHTML = "Rule title 1"; + //container.appendChild(ruleTitle); + } else { + container.update("

Failed to load Auto Run rules.

"); + } + } AutoRunTab.superclass.constructor.call(this, { - region:'center', + region: 'center', padding:'10 10 10 10', - html: autoRunHtml, + items: [container], autoScroll: true, - border: false + border: false, + closable: false }); }; From bf4883a0f05b4b213b2dbc45b39fa71e2f3919d7 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 16 Feb 2024 17:29:15 -0500 Subject: [PATCH 03/21] Fixed incorrect API url. Now loads ARE rules and displays titles. --- .../media/javascript/ui/panel/AutoRunTab.js | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index 547c916ea..75409ad13 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -8,19 +8,26 @@ getCurrentRules = async function(token) { console.log(`token = ${token}`); try { - var res = await fetch(`/api/autorun/rule/list/all?token=${token}`) - console.log(res.body); - console.log("Successfully retrieved active rules."); - if (res.body.success === true && Array.isArray(res.body.rules)) { - console.log(res.body.rules); - return res.body.rules; - } else { - console.log("No active rules."); - return []; + var res = await fetch(`/api/autorun/rules?token=${token}`); + if (!res.ok) { + throw new Error(`Getting auto run rules failed with status ${res.status}`); } + const data = await res.json(); + console.log("Successfully retrieved active rules."); + console.log(data); + const rules = JSON.parse(data.rules); + + if (data.success === true && Array.isArray(rules)) { + console.log(rules); + return rules; + } + + console.log("No active auto run rules."); + return []; + } catch(error) { console.error(error); - console.error("Failed to get rules."); + console.error("Failed to get auto run rules."); return null; } } @@ -47,9 +54,11 @@ AutoRunTab = function() { console.log(`

Number of Auto Run rules enabled: ${rules.length}.

`); container.update(`

Number of Auto Run rules enabled: ${rules.length}.

`); - //ruleTitle = document.createElement('h4'); - //ruleTitle.innerHTML = "Rule title 1"; - //container.appendChild(ruleTitle); + for (let i = 0; i < rules.length; i++) { + ruleTitle = document.createElement('h4'); + ruleTitle.innerHTML = rules[i].name ? rules[i].name : `Rule ${i + 1}`; + container.getEl().appendChild(ruleTitle); + } } else { container.update("

Failed to load Auto Run rules.

"); } From b1c04f9f6f4514de80bdc01dc28bd1c40d1abc9d Mon Sep 17 00:00:00 2001 From: root Date: Sat, 17 Feb 2024 03:18:03 -0500 Subject: [PATCH 04/21] Created form component for each ARE rule. --- extensions/admin_ui/api/handler.rb | 1 + .../javascript/ui/panel/AutoRunRuleForm.js | 30 +++++++++++++++++++ .../media/javascript/ui/panel/AutoRunTab.js | 6 ++-- 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js diff --git a/extensions/admin_ui/api/handler.rb b/extensions/admin_ui/api/handler.rb index 7f25af979..284d1d16c 100644 --- a/extensions/admin_ui/api/handler.rb +++ b/extensions/admin_ui/api/handler.rb @@ -91,6 +91,7 @@ module BeEF ui/panel/Logout.js ui/panel/WelcomeTab.js ui/panel/AutoRunTab.js + ui/panel/AutoRunRuleForm.js ui/panel/ModuleSearching.js ] diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js new file mode 100644 index 000000000..3ba7080c1 --- /dev/null +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -0,0 +1,30 @@ + +/** + * Form for the user to read, update and delete a specific Auto Run rule. + * + * rule: The object definition of this rule from the Auto Run Engine. + * deleteFn: callback function to delete this rule. + * updateFn: callback function to update this rule. + */ +AutoRunRuleForm = function(rule, deleteFn, updateFn) { + + AutoRunRuleForm.superclass.constructor.call(this, { + padding:'10 10 10 10', + items: [{ + xtype: 'textfield', + value: rule.name ? rule.name : '', + fieldLabel: 'Name', + }], + buttons: [{ + text: 'Delete', + handler: deleteFn + }, { + text: 'Save', + handler: updateFn + }], + border: false, + closable: false + }); +}; + +Ext.extend(AutoRunRuleForm, Ext.FormPanel, {}); \ No newline at end of file diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index 75409ad13..9c29e1a9b 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -55,10 +55,10 @@ AutoRunTab = function() { container.update(`

Number of Auto Run rules enabled: ${rules.length}.

`); for (let i = 0; i < rules.length; i++) { - ruleTitle = document.createElement('h4'); - ruleTitle.innerHTML = rules[i].name ? rules[i].name : `Rule ${i + 1}`; - container.getEl().appendChild(ruleTitle); + ruleForm = new AutoRunRuleForm(rules[i], function() {console.log('delete')}, function() {console.log('update')}); + container.add(ruleForm); } + container.doLayout(); } else { container.update("

Failed to load Auto Run rules.

"); } From aaac53f9fbead2f6026d461cfb3f400226a97c0e Mon Sep 17 00:00:00 2001 From: root Date: Sat, 17 Feb 2024 18:36:48 -0500 Subject: [PATCH 05/21] Add ARE rule button for testing. Created author, name and chain_mode fields. --- .../javascript/ui/panel/AutoRunRuleForm.js | 23 ++++++++++-- .../media/javascript/ui/panel/AutoRunTab.js | 36 ++++++++++++++++++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index 3ba7080c1..bcfc7af04 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -6,7 +6,7 @@ * deleteFn: callback function to delete this rule. * updateFn: callback function to update this rule. */ -AutoRunRuleForm = function(rule, deleteFn, updateFn) { +AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { AutoRunRuleForm.superclass.constructor.call(this, { padding:'10 10 10 10', @@ -14,13 +14,32 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { xtype: 'textfield', value: rule.name ? rule.name : '', fieldLabel: 'Name', - }], + }, + { + xtype: 'displayfield', + fieldLabel: 'Author', + value: rule.author ? rule.author : 'anonymous', + },{ + xtype: 'combo', + fieldLabel: 'Chain Mode', + store: ['sequential', 'nested-forward'], + queryMode: 'local', // Use local data. + triggerAction: 'all', // Show both options instead of just the default. + editable: false, // Disable manual text input. + forceSelection: true, + value: rule.chain_mode ? rule.chain_mode : 'sequential' + } + ], buttons: [{ text: 'Delete', handler: deleteFn }, { text: 'Save', handler: updateFn + }, + { + text: 'Add New', + handler: addFn }], border: false, closable: false diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index 9c29e1a9b..05649aa45 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -1,3 +1,21 @@ +const areNotification = { + "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" +}; + + /** * Asynchronously returns the currently active rules in an array. @@ -48,6 +66,17 @@ AutoRunTab = function() { } }); + async function addRule() { + const res = fetch(`/api/autorun/rule/add?token=${token}`, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(areNotification) + }); + if (!res.ok) { + console.error(`Failed when adding a new rule with status ${res.status}.`); + } + } + async function updateRules() { const rules = await getCurrentRules(token); if (rules !== null) { @@ -55,7 +84,12 @@ AutoRunTab = function() { container.update(`

Number of Auto Run rules enabled: ${rules.length}.

`); for (let i = 0; i < rules.length; i++) { - ruleForm = new AutoRunRuleForm(rules[i], function() {console.log('delete')}, function() {console.log('update')}); + ruleForm = new AutoRunRuleForm( + rules[i], + function() {console.log('delete')}, + function() {console.log('update')}, + addRule + ); container.add(ruleForm); } container.doLayout(); From 6a18655a480a2726c30a9ce6752c852cf7724cba Mon Sep 17 00:00:00 2001 From: root Date: Sun, 18 Feb 2024 16:57:56 -0500 Subject: [PATCH 06/21] Rules can now be deleted through the UI. --- .../media/javascript/ui/panel/AutoRunTab.js | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index 05649aa45..f9daa2c94 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -62,12 +62,19 @@ AutoRunTab = function() { }, html: "

Loading Auto Run rules...

", listeners: { - afterrender: updateRules + afterrender: loadRules } }); + async function deleteRule(id) { + const res = await fetch(`/api/autorun/rule/${id}?token=${token}`, {method: 'DELETE'}); + if (!res.ok) { + console.error(`Failed when deleting rule with id ${id}. Failed with status ${res.status}.`); + } + } + async function addRule() { - const res = fetch(`/api/autorun/rule/add?token=${token}`, { + const res = await fetch(`/api/autorun/rule/add?token=${token}`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(areNotification) @@ -77,7 +84,19 @@ AutoRunTab = function() { } } - async function updateRules() { + 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', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(areNotification) + }); + if (!res.ok) { + console.error(`Failed when adding a new rule with status ${res.status}.`); + } + } + + async function loadRules() { const rules = await getCurrentRules(token); if (rules !== null) { console.log(`

Number of Auto Run rules enabled: ${rules.length}.

`); @@ -86,8 +105,8 @@ AutoRunTab = function() { for (let i = 0; i < rules.length; i++) { ruleForm = new AutoRunRuleForm( rules[i], - function() {console.log('delete')}, - function() {console.log('update')}, + function() {deleteRule(rules[i].id)}, + function(newRuleData) {updateRule(rules[i].id, newRuleData)}, // TODO: Implement rule update. addRule ); container.add(ruleForm); From 1b1c8543b3f2881a8c8adbf6a95f895e60bfa267 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 18 Feb 2024 21:05:02 -0500 Subject: [PATCH 07/21] 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); From 503dd532f6aec18fd7797494a20c219426e4987f Mon Sep 17 00:00:00 2001 From: root Date: Sun, 18 Feb 2024 22:42:41 -0500 Subject: [PATCH 08/21] Can update rule name and chain_mode through UI. --- .../javascript/ui/panel/AutoRunRuleForm.js | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index f1ac899b7..b97bd6bb9 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -22,16 +22,31 @@ const areNotificationUpdateTest = { * updateFn: callback function to update this rule. */ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { + const self = this; + const ruleTextFieldId = `rule-name-${rule.id}`; + const chainModeComboId = `rule-chain-mode-${rule.id}`; function handleUpdateRule() { - // TODO: Get data from form inputs. - updateFn(areNotificationUpdateTest); + // TODO: Check if inputs are valid. + const form = self.getForm(); + const formValues = form.getValues(); + const updatedRule = { + ...rule, + modules: JSON.parse(rule['modules']), + execution_delay: JSON.parse(rule['execution_delay']), + execution_order: JSON.parse(rule['execution_order']), + name: formValues[ruleTextFieldId], + chain_mode: formValues[chainModeComboId] + }; + console.log(updatedRule); + updateFn(updatedRule); } AutoRunRuleForm.superclass.constructor.call(this, { padding:'10 10 10 10', items: [{ xtype: 'textfield', + id: ruleTextFieldId, value: rule.name ? rule.name : '', fieldLabel: 'Name', }, @@ -41,6 +56,7 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { value: rule.author ? rule.author : 'anonymous', },{ xtype: 'combo', + id: chainModeComboId, fieldLabel: 'Chain Mode', store: ['sequential', 'nested-forward'], queryMode: 'local', // Use local data. From c2267992c6098ebc963392f1dc67cec260df3c22 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 19 Feb 2024 03:16:26 -0500 Subject: [PATCH 09/21] Editing ARE rule modules as JSON in the UI. Added display condition fields for ARE rules too. --- .../javascript/ui/panel/AutoRunRuleForm.js | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index b97bd6bb9..1993265fb 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -25,6 +25,7 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { const self = this; const ruleTextFieldId = `rule-name-${rule.id}`; const chainModeComboId = `rule-chain-mode-${rule.id}`; + const moduleFieldId = `rule-module-field-${rule.id}`; function handleUpdateRule() { // TODO: Check if inputs are valid. @@ -32,11 +33,12 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { const formValues = form.getValues(); const updatedRule = { ...rule, - modules: JSON.parse(rule['modules']), + modules: JSON.parse(rule['modules']), // need this to prevent type error. execution_delay: JSON.parse(rule['execution_delay']), execution_order: JSON.parse(rule['execution_order']), name: formValues[ruleTextFieldId], - chain_mode: formValues[chainModeComboId] + chain_mode: formValues[chainModeComboId], + modules: JSON.parse(formValues[moduleFieldId]), }; console.log(updatedRule); updateFn(updatedRule); @@ -54,6 +56,22 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { xtype: 'displayfield', fieldLabel: 'Author', value: rule.author ? rule.author : 'anonymous', + },{ + xtype: 'displayfield', + fieldLabel: 'Browser(s)', + value: rule.browser ? rule.browser : 'All', + },{ + xtype: 'displayfield', + fieldLabel: 'Browser version(s)', + value: rule.browser_version ? rule.browser_version : 'All', + },{ + xtype: 'displayfield', + fieldLabel: 'OS(s)', + value: rule.os ? rule.os : 'All', + },{ + xtype: 'displayfield', + fieldLabel: 'OS version(s)', + value: rule.os_version ? rule.os_version : 'All', },{ xtype: 'combo', id: chainModeComboId, @@ -64,6 +82,20 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { editable: false, // Disable manual text input. forceSelection: true, value: rule.chain_mode ? rule.chain_mode : 'sequential' + },{ + xtype: 'textarea', + id: moduleFieldId, + fieldLabel: 'modules', + value: rule.modules ? rule.modules : '[]', + grow: true + },{ + xtype: 'displayfield', + fieldLabel: 'Execution Order', + value: rule.execution_order ? rule.execution_order : 'undefined', + },{ + xtype: 'displayfield', + fieldLabel: 'Execution Delay', + value: rule.execution_delay ? rule.execution_delay : 'undefined', } ], buttons: [{ From fd2e56dd121f2b2b0318b0c99f18c3466adc4383 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 20 Feb 2024 16:52:49 -0500 Subject: [PATCH 10/21] Created UI class for modules in Auto Run. --- extensions/admin_ui/api/handler.rb | 1 + .../javascript/ui/panel/AutoRunModuleForm.js | 65 +++++++++++++++++++ .../javascript/ui/panel/AutoRunRuleForm.js | 26 +++++--- 3 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js diff --git a/extensions/admin_ui/api/handler.rb b/extensions/admin_ui/api/handler.rb index 284d1d16c..cf1dbe0d9 100644 --- a/extensions/admin_ui/api/handler.rb +++ b/extensions/admin_ui/api/handler.rb @@ -92,6 +92,7 @@ module BeEF ui/panel/WelcomeTab.js ui/panel/AutoRunTab.js ui/panel/AutoRunRuleForm.js + ui/panel/AutoRunModuleForm.js ui/panel/ModuleSearching.js ] diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js new file mode 100644 index 000000000..f1d4b76ab --- /dev/null +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js @@ -0,0 +1,65 @@ +/** + * Form that displays fields for a module. + * + * moduleData: The object definition of this moduleData from the Auto Run Engine. + * deleteFn: callback function to delete this moduleData. + * moveUp: moves the module up one spot in the Auto Run execution order. + * moveDown: moves the module down one spot in the Auto Run exection order. + */ +AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown) { + const moduleNameId = `module-name-${moduleData.id}`; + const moduleTextAreaId = `module-name-${moduleData.id}`; + const chainModeComboId = `module-combo-${moduleData.id}`; + + AutoRunModuleForm.superclass.constructor.call(this, { + padding:'10 10 10 10', + items: [{ + xtype: 'textfield', + id: moduleNameId, + value: moduleData.name ? moduleData.name : '', + fieldLabel: 'Name', + },/*{ + xtype: 'combo', + id: chainModeComboId, + fieldLabel: 'Chain Mode', + store: ['sequential', 'nested-forward'], + queryMode: 'local', // Use local data. + triggerAction: 'all', // Show both options instead of just the default. + editable: false, // Disable manual text input. + forceSelection: true, + value: moduleData.chain_mode ? moduleData.chain_mode : 'sequential' + },*/ + { + xtype: 'displayfield', + fieldLabel: 'Author', + value: moduleData.author ? moduleData.author : 'anonymous', + },{ + xtype: 'textarea', + id: moduleTextAreaId, + fieldLabel: 'Module Data', + value: moduleData ? JSON.stringify(moduleData) : '{}', + grow: true, + width: '80%' + }, + { + xtype: 'button', + text: 'Delete', + handler: deleteFn, + },{ + xtype: 'button', + text: 'Move Forward', + handler: moveUp, + disabled: moveUp == undefined + },{ + xtype: 'button', + text: 'Move Back', + handler: moveDown, + disabled: moveDown == undefined + } + ], + border: false, + closable: false + }); +}; + +Ext.extend(AutoRunModuleForm, Ext.Container, {}); \ No newline at end of file diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index 1993265fb..7363ad011 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -25,10 +25,23 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { const self = this; const ruleTextFieldId = `rule-name-${rule.id}`; const chainModeComboId = `rule-chain-mode-${rule.id}`; - const moduleFieldId = `rule-module-field-${rule.id}`; + const modules = JSON.parse(rule['modules']); + const moduleForms = []; + for (let i = 0; i < modules.length; ++i) { + const isFirstModule = i === 0; + const isLastModule = i >= modules.length - 1; + moduleForms.push(new AutoRunModuleForm( + modules[i], + function() {console.log("delete this module")}, + isFirstModule ? undefined : function() {console.log("move up")}, + isLastModule ? undefined : function() {console.log("move down")}, + )); + } + console.log(`Number of modules: ${moduleForms.length}`); function handleUpdateRule() { // TODO: Check if inputs are valid. + // TODO: Get data from modules. const form = self.getForm(); const formValues = form.getValues(); const updatedRule = { @@ -38,7 +51,6 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { execution_order: JSON.parse(rule['execution_order']), name: formValues[ruleTextFieldId], chain_mode: formValues[chainModeComboId], - modules: JSON.parse(formValues[moduleFieldId]), }; console.log(updatedRule); updateFn(updatedRule); @@ -72,7 +84,9 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { xtype: 'displayfield', fieldLabel: 'OS version(s)', value: rule.os_version ? rule.os_version : 'All', - },{ + }, + ...moduleForms, + { xtype: 'combo', id: chainModeComboId, fieldLabel: 'Chain Mode', @@ -82,12 +96,6 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { editable: false, // Disable manual text input. forceSelection: true, value: rule.chain_mode ? rule.chain_mode : 'sequential' - },{ - xtype: 'textarea', - id: moduleFieldId, - fieldLabel: 'modules', - value: rule.modules ? rule.modules : '[]', - grow: true },{ xtype: 'displayfield', fieldLabel: 'Execution Order', From 0fd2d433b2fcc6afd2543b2478846259680dee19 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 20 Feb 2024 20:53:37 -0500 Subject: [PATCH 11/21] Can now reorder modules in the UI. Execution order array is conflicting though. --- .../javascript/ui/panel/AutoRunModuleForm.js | 18 ++----- .../javascript/ui/panel/AutoRunRuleForm.js | 49 +++++++++++++------ 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js index f1d4b76ab..a72272a58 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js @@ -6,19 +6,13 @@ * moveUp: moves the module up one spot in the Auto Run execution order. * moveDown: moves the module down one spot in the Auto Run exection order. */ -AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown) { - const moduleNameId = `module-name-${moduleData.id}`; - const moduleTextAreaId = `module-name-${moduleData.id}`; - const chainModeComboId = `module-combo-${moduleData.id}`; +AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, index) { + const moduleTextAreaId = `rule-${ruleId}-module-textarea-${index}`; + const chainModeComboId = `rule-${ruleId}-module-combo-${index}`; AutoRunModuleForm.superclass.constructor.call(this, { padding:'10 10 10 10', - items: [{ - xtype: 'textfield', - id: moduleNameId, - value: moduleData.name ? moduleData.name : '', - fieldLabel: 'Name', - },/*{ + items: [/*{ xtype: 'combo', id: chainModeComboId, fieldLabel: 'Chain Mode', @@ -30,10 +24,6 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown) { value: moduleData.chain_mode ? moduleData.chain_mode : 'sequential' },*/ { - xtype: 'displayfield', - fieldLabel: 'Author', - value: moduleData.author ? moduleData.author : 'anonymous', - },{ xtype: 'textarea', id: moduleTextAreaId, fieldLabel: 'Module Data', diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index 7363ad011..c67f8ebf7 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -26,27 +26,48 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { const ruleTextFieldId = `rule-name-${rule.id}`; const chainModeComboId = `rule-chain-mode-${rule.id}`; const modules = JSON.parse(rule['modules']); - const moduleForms = []; - for (let i = 0; i < modules.length; ++i) { - const isFirstModule = i === 0; - const isLastModule = i >= modules.length - 1; - moduleForms.push(new AutoRunModuleForm( - modules[i], - function() {console.log("delete this module")}, - isFirstModule ? undefined : function() {console.log("move up")}, - isLastModule ? undefined : function() {console.log("move down")}, - )); + const moduleContainer = new Ext.Container(); + + function reorderModule(index, direction) { + // Rearrange modules into new order. + const currentModule = modules[index]; + const newIndex = direction === 'back' ? index + 1 : index - 1; + modules.splice(index, 1); + modules.splice(newIndex, 0, currentModule); + + // Update DOM. + setupModuleForms(); + moduleContainer.doLayout(); } - console.log(`Number of modules: ${moduleForms.length}`); + + function setupModuleForms() { + + moduleContainer.removeAll(true); + + for (let i = 0; i < modules.length; ++i) { + const isFirstModule = i === 0; + const isLastModule = i >= modules.length - 1; + // TODO: Push them in execution order. + moduleContainer.add(new AutoRunModuleForm( + modules[i], + function() {console.log("delete this module")}, + isFirstModule ? undefined : function() {reorderModule(i, 'forward')}, + isLastModule ? undefined : function() {reorderModule(i, 'back')}, + rule.id, + i + )); + } + } + setupModuleForms(); function handleUpdateRule() { // TODO: Check if inputs are valid. - // TODO: Get data from modules. + // TODO: Need to overwrite module order. const form = self.getForm(); const formValues = form.getValues(); const updatedRule = { ...rule, - modules: JSON.parse(rule['modules']), // need this to prevent type error. + modules: modules, execution_delay: JSON.parse(rule['execution_delay']), execution_order: JSON.parse(rule['execution_order']), name: formValues[ruleTextFieldId], @@ -85,7 +106,7 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { fieldLabel: 'OS version(s)', value: rule.os_version ? rule.os_version : 'All', }, - ...moduleForms, + moduleContainer, { xtype: 'combo', id: chainModeComboId, From 065cd6dec9c6d9441af6a45e4edfe911a7765c08 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 21 Feb 2024 02:40:14 -0500 Subject: [PATCH 12/21] Nicer format for Auto Run rules. --- .../javascript/ui/panel/AutoRunModuleForm.js | 53 ++++++++++-------- .../javascript/ui/panel/AutoRunRuleForm.js | 18 ++++--- .../media/javascript/ui/panel/AutoRunTab.js | 54 +++++++++++++------ 3 files changed, 79 insertions(+), 46 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js index a72272a58..002f0a232 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js @@ -10,9 +10,35 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind const moduleTextAreaId = `rule-${ruleId}-module-textarea-${index}`; const chainModeComboId = `rule-${ruleId}-module-combo-${index}`; + const buttonContainer = new Ext.Container({ + layout: { + type: 'hbox', + pack: 'end', + }, + items: [ + { + xtype: 'button', + text: 'Delete', + handler: deleteFn, + },{ + xtype: 'button', + text: 'Move Forward', + handler: moveUp, + disabled: moveUp == undefined, + },{ + xtype: 'button', + text: 'Move Back', + handler: moveDown, + disabled: moveDown == undefined, + } + ] + }); + AutoRunModuleForm.superclass.constructor.call(this, { - padding:'10 10 10 10', - items: [/*{ + padding: '10 10 10 10', + closable: false, + items: [ + /*{ xtype: 'combo', id: chainModeComboId, fieldLabel: 'Chain Mode', @@ -31,25 +57,8 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind grow: true, width: '80%' }, - { - xtype: 'button', - text: 'Delete', - handler: deleteFn, - },{ - xtype: 'button', - text: 'Move Forward', - handler: moveUp, - disabled: moveUp == undefined - },{ - xtype: 'button', - text: 'Move Back', - handler: moveDown, - disabled: moveDown == undefined - } - ], - border: false, - closable: false - }); + buttonContainer + ]}); }; -Ext.extend(AutoRunModuleForm, Ext.Container, {}); \ No newline at end of file +Ext.extend(AutoRunModuleForm, Ext.Panel, {}); \ No newline at end of file diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index c67f8ebf7..be540d8d7 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -21,12 +21,17 @@ const areNotificationUpdateTest = { * deleteFn: callback function to delete this rule. * updateFn: callback function to update this rule. */ -AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { +AutoRunRuleForm = function(rule, deleteFn, updateFn) { const self = this; const ruleTextFieldId = `rule-name-${rule.id}`; const chainModeComboId = `rule-chain-mode-${rule.id}`; const modules = JSON.parse(rule['modules']); - const moduleContainer = new Ext.Container(); + const execution_order = rule['modules']; + const moduleContainer = new Ext.Container({ + style: { + padding: '10 10 10 10', + } + }); function reorderModule(index, direction) { // Rearrange modules into new order. @@ -41,13 +46,15 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { } function setupModuleForms() { + console.log(execution_order); moduleContainer.removeAll(true); + // I think execution order should always be sequential. + // The actual order comed from the modules array. for (let i = 0; i < modules.length; ++i) { const isFirstModule = i === 0; const isLastModule = i >= modules.length - 1; - // TODO: Push them in execution order. moduleContainer.add(new AutoRunModuleForm( modules[i], function() {console.log("delete this module")}, @@ -79,6 +86,7 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { AutoRunRuleForm.superclass.constructor.call(this, { padding:'10 10 10 10', + title: `Rule ${rule.id}`, items: [{ xtype: 'textfield', id: ruleTextFieldId, @@ -133,10 +141,6 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn, addFn) { }, { text: 'Save', handler: handleUpdateRule - }, - { - text: 'Add New', - handler: addFn }], border: false, closable: false diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index 516967ff0..742486356 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -23,7 +23,6 @@ const areNotification = { * null if there was an error. */ getCurrentRules = async function(token) { - console.log(`token = ${token}`); try { var res = await fetch(`/api/autorun/rules?token=${token}`); @@ -54,13 +53,37 @@ AutoRunTab = function() { // RESTful API token. var token = BeefWUI.get_rest_token(); - // Setup container element. - var container = new Ext.Panel({ - style: { - font: '11px tahoma,arial,helvetica,sans-serif', - width: '500px' - }, + // Heading container to describe general Auto Run state. + var ruleLoadingState = new Ext.Container({ html: "

Loading Auto Run rules...

", + }) + var headingContainer = new Ext.Panel({ + style: { + font: '11px tahoma,arial,helvetica,sans-serif' + }, + padding:'10 10 10 10', + border: false, + items: [{ + xtype: 'container', + html: '\ +
\ +

Auto Run Rules

\ +

These determine what commands run automatically when a browser is hooked.

\ +
' + }, + ruleLoadingState, + { + xtype: 'button', + text: 'Add New Rule', + handler: addRule + }], + listeners: { + afterrender: loadRules + } + }); + // Contains all of the rules and inputs to change each rule. + var ruleContainer = new Ext.Panel({ + border: false, listeners: { afterrender: loadRules } @@ -85,7 +108,6 @@ 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: 'PATCH', headers: {'Content-Type': 'application/json'}, @@ -99,28 +121,26 @@ AutoRunTab = function() { async function loadRules() { const rules = await getCurrentRules(token); if (rules !== null) { - console.log(`

Number of Auto Run rules enabled: ${rules.length}.

`); - container.update(`

Number of Auto Run rules enabled: ${rules.length}.

`); + ruleLoadingState.update(`

Loaded ${rules.length} Auto Run rules.

`); + ruleContainer.removeAll(); for (let i = 0; i < rules.length; i++) { ruleForm = new AutoRunRuleForm( rules[i], function() {deleteRule(rules[i].id)}, - function(newRuleData) {updateRule(rules[i].id, newRuleData)}, - addRule + function(newRuleData) {updateRule(rules[i].id, newRuleData)} ); - container.add(ruleForm); + ruleContainer.add(ruleForm); } - container.doLayout(); + ruleContainer.doLayout(); } else { - container.update("

Failed to load Auto Run rules.

"); + ruleLoadingState.update("

Failed to load Auto Run rules.

"); } } AutoRunTab.superclass.constructor.call(this, { region: 'center', - padding:'10 10 10 10', - items: [container], + items: [headingContainer, ruleContainer], autoScroll: true, border: false, closable: false From c3bc4d2fcdbe0856f617d74e63cae82b20fa9984 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 21 Feb 2024 02:48:50 -0500 Subject: [PATCH 13/21] Update DOM after creating, updating or deleting a rule. --- .../admin_ui/media/javascript/ui/panel/AutoRunTab.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index 742486356..d2f9aef54 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -93,7 +93,10 @@ AutoRunTab = function() { const res = await fetch(`/api/autorun/rule/${id}?token=${token}`, {method: 'DELETE'}); if (!res.ok) { console.error(`Failed when deleting rule with id ${id}. Failed with status ${res.status}.`); + return; } + // Update the entire rules panel. Not very efficient. + loadRules(); } async function addRule() { @@ -104,7 +107,10 @@ AutoRunTab = function() { }); if (!res.ok) { console.error(`Failed when adding a new rule with status ${res.status}.`); + return; } + // Update the entire rules panel. Not very efficient. + loadRules(); } async function updateRule(id, newRuleData) { @@ -115,7 +121,10 @@ AutoRunTab = function() { }); if (!res.ok) { console.error(`Failed when adding a new rule with status ${res.status}.`); + return; } + // Update the entire rules panel. Not very efficient. + loadRules(); } async function loadRules() { From 20089628b33f0cd877534bef45bc2788c112886c Mon Sep 17 00:00:00 2001 From: root Date: Wed, 21 Feb 2024 22:03:20 -0500 Subject: [PATCH 14/21] Can now delete ARE rule modules through UI. --- .../javascript/ui/panel/AutoRunRuleForm.js | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index be540d8d7..0e7eed38e 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -25,8 +25,9 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { const self = this; const ruleTextFieldId = `rule-name-${rule.id}`; const chainModeComboId = `rule-chain-mode-${rule.id}`; - const modules = JSON.parse(rule['modules']); - const execution_order = rule['modules']; + const newRule = JSON.parse(JSON.stringify(rule)); + newRule.modules = JSON.parse(newRule['modules']); + newRule.execution_delay = JSON.parse(newRule['execution_delay']); const moduleContainer = new Ext.Container({ style: { padding: '10 10 10 10', @@ -35,29 +36,37 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { function reorderModule(index, direction) { // Rearrange modules into new order. - const currentModule = modules[index]; + const currentModule = newRule.modules[index]; const newIndex = direction === 'back' ? index + 1 : index - 1; - modules.splice(index, 1); - modules.splice(newIndex, 0, currentModule); + newRule.modules.splice(index, 1); + newRule.modules.splice(newIndex, 0, currentModule); // Update DOM. setupModuleForms(); moduleContainer.doLayout(); } + function removeModule(index) { + console.log("Removing module."); + // Remove element from execution_order and execution_delay arrays. + newRule.modules.splice(index, 1); + newRule.execution_delay.splice(index, 1); + setupModuleForms(); + moduleContainer.doLayout(); + } + function setupModuleForms() { - console.log(execution_order); moduleContainer.removeAll(true); // I think execution order should always be sequential. // The actual order comed from the modules array. - for (let i = 0; i < modules.length; ++i) { + for (let i = 0; i < newRule.modules.length; ++i) { const isFirstModule = i === 0; - const isLastModule = i >= modules.length - 1; + const isLastModule = i >= newRule.modules.length - 1; moduleContainer.add(new AutoRunModuleForm( - modules[i], - function() {console.log("delete this module")}, + newRule.modules[i], + function() {removeModule(i)}, isFirstModule ? undefined : function() {reorderModule(i, 'forward')}, isLastModule ? undefined : function() {reorderModule(i, 'back')}, rule.id, @@ -73,14 +82,13 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { const form = self.getForm(); const formValues = form.getValues(); const updatedRule = { - ...rule, - modules: modules, - execution_delay: JSON.parse(rule['execution_delay']), - execution_order: JSON.parse(rule['execution_order']), + ...newRule, name: formValues[ruleTextFieldId], chain_mode: formValues[chainModeComboId], + execution_order: [...Array(newRule.modules.length).keys()], }; - console.log(updatedRule); + console.log("UPDATED RULE."); + console.log(JSON.stringify(updatedRule, null, 2)); updateFn(updatedRule); } @@ -128,11 +136,15 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { },{ xtype: 'displayfield', fieldLabel: 'Execution Order', - value: rule.execution_order ? rule.execution_order : 'undefined', + value: newRule.execution_order ? + JSON.stringify(newRule.execution_order) + : 'undefined', },{ xtype: 'displayfield', fieldLabel: 'Execution Delay', - value: rule.execution_delay ? rule.execution_delay : 'undefined', + value: newRule.execution_delay ? + JSON.stringify(newRule.execution_delay) + : 'undefined', } ], buttons: [{ From 09cba67c90cd30da7d4bf53359b7b8c90da2b021 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 21 Feb 2024 22:16:24 -0500 Subject: [PATCH 15/21] Add module to a rule now via a button in the UI. --- .../javascript/ui/panel/AutoRunRuleForm.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index 0e7eed38e..c5182ee61 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -51,6 +51,18 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { // Remove element from execution_order and execution_delay arrays. newRule.modules.splice(index, 1); newRule.execution_delay.splice(index, 1); + + // Update DOM. + setupModuleForms(); + moduleContainer.doLayout(); + } + + function addModule() { + // New module is a copy of the last module. + newRule.modules.push(newRule.modules[newRule.modules.length - 1]); + newRule.execution_delay.push(newRule.execution_delay[newRule.execution_delay.length - 1]); + + // Update DOM. setupModuleForms(); moduleContainer.doLayout(); } @@ -92,6 +104,7 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { updateFn(updatedRule); } + AutoRunRuleForm.superclass.constructor.call(this, { padding:'10 10 10 10', title: `Rule ${rule.id}`, @@ -123,6 +136,11 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { value: rule.os_version ? rule.os_version : 'All', }, moduleContainer, + { + xtype: 'button', + text: 'Add Module', + handler: addModule + }, { xtype: 'combo', id: chainModeComboId, From 9ccd8c633ba181b4619b3d6d6c7db90e38c647d1 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 22 Feb 2024 20:55:10 -0500 Subject: [PATCH 16/21] Textfields for ARE module input fields. --- .../javascript/ui/panel/AutoRunModuleForm.js | 66 ++++++++++++----- .../media/javascript/ui/panel/common.js | 74 +++++++++++++++++-- 2 files changed, 113 insertions(+), 27 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js index 002f0a232..f74bbcc75 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js @@ -10,6 +10,49 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind const moduleTextAreaId = `rule-${ruleId}-module-textarea-${index}`; const chainModeComboId = `rule-${ruleId}-module-combo-${index}`; + const moduleOptionsContainer = new Ext.Panel({ + padding: '5 5 5 5', + border: false, + layout: 'form', + items: [/*{ + xtype: 'combo', + id: chainModeComboId, + fieldLabel: 'Chain Mode', + store: ['moduleA', 'moduleB'], // TODO: Update this to the array of commands. + queryMode: 'local', // Use local data. + triggerAction: 'all', // Show both options instead of just the default. + editable: false, // Disable manual text input. + forceSelection: true, + value: moduleData.name, + },*/ + { + xtype: 'displayfield', + fieldLabel: 'Module Name', + value: moduleData.name, + }, + { + xtype: 'displayfield', + fieldLabel: 'Module Author', + value: moduleData.author ? moduleData.author : 'anonymous', + } + ] + }); + + function loadModule() { + // TODO: Need to query module option types so that not everything is given a textfield. + console.log("Module data:"); + console.log(moduleData); + for (key in moduleData.options) { + const value = moduleData.options[key]; + const inputField = new Ext.form.TextField({ + fieldLabel: key, + value: value ? value : '', + }); + moduleOptionsContainer.add(inputField); + }; + } + loadModule(); + const buttonContainer = new Ext.Container({ layout: { type: 'hbox', @@ -34,30 +77,13 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind ] }); + AutoRunModuleForm.superclass.constructor.call(this, { padding: '10 10 10 10', closable: false, items: [ - /*{ - xtype: 'combo', - id: chainModeComboId, - fieldLabel: 'Chain Mode', - store: ['sequential', 'nested-forward'], - queryMode: 'local', // Use local data. - triggerAction: 'all', // Show both options instead of just the default. - editable: false, // Disable manual text input. - forceSelection: true, - value: moduleData.chain_mode ? moduleData.chain_mode : 'sequential' - },*/ - { - xtype: 'textarea', - id: moduleTextAreaId, - fieldLabel: 'Module Data', - value: moduleData ? JSON.stringify(moduleData) : '{}', - grow: true, - width: '80%' - }, - buttonContainer + moduleOptionsContainer, + buttonContainer ]}); }; diff --git a/extensions/admin_ui/media/javascript/ui/panel/common.js b/extensions/admin_ui/media/javascript/ui/panel/common.js index 750bda90e..dd5e21a90 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/common.js +++ b/extensions/admin_ui/media/javascript/ui/panel/common.js @@ -357,13 +357,13 @@ function genNewExploitPanel(panel, command_module_id, command_module_name, zombi fieldLabel: 'Description', fieldClass: 'command-module-panel-description', value: module.Description - }), - new Ext.form.DisplayField({ - name: 'command_module_id_visible', - fieldLabel: 'Id', - fieldClass: 'command-module-panel-description', - value: command_module_id - }) + }), + new Ext.form.DisplayField({ + name: 'command_module_id_visible', + fieldLabel: 'Id', + fieldClass: 'command-module-panel-description', + value: command_module_id + }) ], buttons:[{ @@ -425,3 +425,63 @@ function genNewExploitPanel(panel, command_module_id, command_module_name, zombi }); } }; + + +function genNewAutoRunModulePanel(panel, module, ruleId) { + if(typeof panel != 'object') { + Ext.beef.msg('Bad!', 'Incorrect panel chosen.'); + return; + } + + panel.removeAll(); + if(module.name == 'some special command module') { + //HERE we will develop specific panels for the command modules that require it. + } else { + var form = new Ext.form.FormPanel({ + id: `form-are-module-${ruleId}`, + border: false, + labelWidth: 75, + defaultType: 'textfield', + title: module.name, + bodyStyle: 'padding: 5px;', + + items: [ + new Ext.form.DisplayField({ + name: 'command_module_description', + fieldLabel: 'Description', + fieldClass: 'command-module-panel-description', + value: module.description + }), + ], + + buttons:[{ + text: "Save Module", + handler: function() {console.log("Saved module...")} + }] + }); + + // create the panel and hide it + /* + var payload_panel = new Ext.Panel({ + id: 'payload-panel', // used with Ext.GetCmp('payload-panel') + bodyStyle: 'padding:10px;', // we can assign styles to the main div + layout : 'form', + bodyBorder: false, + height: 200, + hidden: true, + border: false //we can remove the border of the panel + }); + */ + + Ext.each(module.Data, function(input){ + // Fake a zombie "session" property for the sake of getting it working. + generate_form_input_field(form, input, null, false, {session: module.name})} + ); + + //form.add(payload_panel); + panel.add(form); + panel.doLayout(); + // hide the load mask after rendering of the config panel is done + //panel.configLoadMask.hide(); + } +}; \ No newline at end of file From e25529a76b52aba131a2a126c21bc7ca385103f6 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 24 Feb 2024 19:11:33 -0500 Subject: [PATCH 17/21] Autorun UI elements now match command module elements. --- core/main/rest/handlers/modules.rb | 9 +- .../javascript/ui/panel/AutoRunModuleForm.js | 123 ++++++++++++++---- .../javascript/ui/panel/AutoRunRuleForm.js | 9 +- .../media/javascript/ui/panel/AutoRunTab.js | 37 +++++- .../media/javascript/ui/panel/common.js | 68 +--------- 5 files changed, 147 insertions(+), 99 deletions(-) diff --git a/core/main/rest/handlers/modules.rb b/core/main/rest/handlers/modules.rb index 8e07b7c62..5c23e926b 100644 --- a/core/main/rest/handlers/modules.rb +++ b/core/main/rest/handlers/modules.rb @@ -25,21 +25,20 @@ module BeEF get '/' do mods = BeEF::Core::Models::CommandModule.all - mods_hash = {} - i = 0 + mods_array = [] + mods.each do |mod| modk = BeEF::Module.get_key_by_database_id(mod.id) next unless BeEF::Module.is_enabled(modk) - mods_hash[i] = { + mods_array << { '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 end - mods_hash.to_json + mods_array.to_json end get '/search/:mod_name' do diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js index f74bbcc75..ccffd38b3 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js @@ -1,3 +1,38 @@ + +loadModuleInfo = async function(token, moduleName) { + let moduleId = null; + try { + const searchResponse = await fetch(`/api/modules/search/${moduleName}?token=${token}`); + if (!searchResponse.ok) { + throw new Error(`Getting auto run rules failed with status ${searchResponse.status}`); + } + const searchData = await searchResponse.json(); + console.log("Search data:"); + console.log(searchData); + if (typeof searchData.id === 'number') { + moduleId = searchData.id; + } else { + throw new Error("Searching module name failed."); + } + + // DEBUG log + console.log(`Successfully retrieved module id for ${moduleName} = ${moduleId}`); + + const infoResponse = await fetch(`/api/modules/${moduleId}?token=${token}`); + const infoData = await infoResponse.json(); + if (!infoData) { + throw new Error(`Module with name ${moduleName} and ID ${moduleId} couldn't be retrived.`); + } + return infoData; + + } catch(error) { + console.error(error); + console.error("Failed to get module information."); + return null; + } +} + + /** * Form that displays fields for a module. * @@ -9,22 +44,37 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, index) { const moduleTextAreaId = `rule-${ruleId}-module-textarea-${index}`; const chainModeComboId = `rule-${ruleId}-module-combo-${index}`; + const token = BeefWUI.get_rest_token(); + + // TODO: Load in modules from ruby. + /* + const moduleSelect = new Ext.form.ComboBox({ + fieldLabel: 'Command Module', + store: [{name: 'hi', id: 1}], + queryMode: 'local', // Set the queryMode to 'remote' + displayField: 'name', + valueField: 'id', + editable: false, // Disable manual editing of the field + forceSelection: true, // Force selection from the list + //listeners: { + // render: function (combo) { + // combo.setValue(moduleData.name); + // }, + // select: function(combo, newValue, oldValue) { + // if (newValue) { + // console.log("Combo value selected."); + // } + // } + //} + }); + */ const moduleOptionsContainer = new Ext.Panel({ - padding: '5 5 5 5', - border: false, + title: `${moduleData.name}`, + padding: '10 10 10 10', layout: 'form', - items: [/*{ - xtype: 'combo', - id: chainModeComboId, - fieldLabel: 'Chain Mode', - store: ['moduleA', 'moduleB'], // TODO: Update this to the array of commands. - queryMode: 'local', // Use local data. - triggerAction: 'all', // Show both options instead of just the default. - editable: false, // Disable manual text input. - forceSelection: true, - value: moduleData.name, - },*/ + border: false, + items: [ { xtype: 'displayfield', fieldLabel: 'Module Name', @@ -35,23 +85,42 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind fieldLabel: 'Module Author', value: moduleData.author ? moduleData.author : 'anonymous', } - ] + ], + listeners: { + afterrender: loadModule + } }); - function loadModule() { + + async function loadModule() { // TODO: Need to query module option types so that not everything is given a textfield. + const moduleInfo = await loadModuleInfo(token, moduleData.name); + if (!moduleInfo) { + moduleOptionsContainer.update("

Failed to load module information.

"); + return; + } console.log("Module data:"); console.log(moduleData); - for (key in moduleData.options) { - const value = moduleData.options[key]; - const inputField = new Ext.form.TextField({ - fieldLabel: key, - value: value ? value : '', - }); - moduleOptionsContainer.add(inputField); + console.log("Module info:"); + console.log(JSON.stringify(moduleInfo, null, 2)); + console.log("Module options, should be an array:"); + console.log(moduleInfo.options); + + //genNewAutoRunModulePanel(moduleOptionsContainer, moduleInfo, ruleId); + + for (let i = 0; i < moduleInfo.options.length; i++) { + console.log(JSON.stringify(moduleInfo.options[i], null, 2)); + // TODO add real autorun module value by default. + generate_form_input_field( + moduleOptionsContainer, + moduleInfo.options[i], + null, + false, + {session: `${moduleInfo.name}-module-${index}-field-${i}`} + ); }; + moduleOptionsContainer.doLayout(); } - loadModule(); const buttonContainer = new Ext.Container({ layout: { @@ -79,12 +148,12 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind AutoRunModuleForm.superclass.constructor.call(this, { - padding: '10 10 10 10', - closable: false, items: [ + //moduleSelect, moduleOptionsContainer, buttonContainer - ]}); + ] + }); }; -Ext.extend(AutoRunModuleForm, Ext.Panel, {}); \ No newline at end of file +Ext.extend(AutoRunModuleForm, Ext.Container, {}); \ No newline at end of file diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index c5182ee61..ee69c7f8f 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -18,10 +18,11 @@ const areNotificationUpdateTest = { * Form for the user to read, update and delete a specific Auto Run rule. * * rule: The object definition of this rule from the Auto Run Engine. + * modules: The list of all commands/modules that the user can choose from. * deleteFn: callback function to delete this rule. * updateFn: callback function to update this rule. */ -AutoRunRuleForm = function(rule, deleteFn, updateFn) { +AutoRunRuleForm = function(rule, modules, deleteFn, updateFn) { const self = this; const ruleTextFieldId = `rule-name-${rule.id}`; const chainModeComboId = `rule-chain-mode-${rule.id}`; @@ -31,6 +32,9 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { const moduleContainer = new Ext.Container({ style: { padding: '10 10 10 10', + }, + listeners: { + afterrender: setupModuleForms } }); @@ -72,7 +76,7 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { moduleContainer.removeAll(true); // I think execution order should always be sequential. - // The actual order comed from the modules array. + // The actual order comes from the modules array. for (let i = 0; i < newRule.modules.length; ++i) { const isFirstModule = i === 0; const isLastModule = i >= newRule.modules.length - 1; @@ -86,7 +90,6 @@ AutoRunRuleForm = function(rule, deleteFn, updateFn) { )); } } - setupModuleForms(); function handleUpdateRule() { // TODO: Check if inputs are valid. diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index d2f9aef54..4a6b77057 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -30,8 +30,6 @@ getCurrentRules = async function(token) { throw new Error(`Getting auto run rules failed with status ${res.status}`); } const data = await res.json(); - console.log("Successfully retrieved active rules."); - console.log(data); const rules = JSON.parse(data.rules); if (data.success === true && Array.isArray(rules)) { @@ -49,6 +47,27 @@ getCurrentRules = async function(token) { } } +getModules = async function(token) { + try { + var res = await fetch(`/api/modules?token=${token}`); + if (!res.ok) { + throw new Error(`Getting auto run rules failed with status ${res.status}`); + } + const modules = await res.json(); + + // DEBUG log + console.log("Successfully retrieved active modules:"); + console.log(modules); + + return modules; + + } catch(error) { + console.error(error); + console.error("Failed to get auto run rules."); + return null; + } +} + AutoRunTab = function() { // RESTful API token. var token = BeefWUI.get_rest_token(); @@ -128,7 +147,18 @@ AutoRunTab = function() { } async function loadRules() { - const rules = await getCurrentRules(token); + let modules = []; + let rules = []; + try { + modules = await getModules(token); + rules = await getCurrentRules(token); + } catch (error) { + console.error(error); + console.error("Failed to load command modules and/or rules for Auto Run."); + ruleLoadingState.update("

Failed to load Auto Run rules.

"); + return; + } + if (rules !== null) { ruleLoadingState.update(`

Loaded ${rules.length} Auto Run rules.

`); ruleContainer.removeAll(); @@ -136,6 +166,7 @@ AutoRunTab = function() { for (let i = 0; i < rules.length; i++) { ruleForm = new AutoRunRuleForm( rules[i], + modules, function() {deleteRule(rules[i].id)}, function(newRuleData) {updateRule(rules[i].id, newRuleData)} ); diff --git a/extensions/admin_ui/media/javascript/ui/panel/common.js b/extensions/admin_ui/media/javascript/ui/panel/common.js index dd5e21a90..255870faf 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/common.js +++ b/extensions/admin_ui/media/javascript/ui/panel/common.js @@ -20,6 +20,8 @@ var re_execute_command_title = 'Re-execute command' function generate_form_input_field(form, input, value, disabled, zombie) { var input_field = null; var input_def = null; + console.log("Input type: "); + console.log(JSON.stringify(input, null, 2)); if (!input['ui_label']) input['ui_label'] = input['name']; if (!input['type']) input['type'] = 'textfield'; @@ -33,6 +35,8 @@ function generate_form_input_field(form, input, value, disabled, zombie) { allowBlank: false, value: input['value'] }; + console.log("Input Def:"); + console.log(JSON.stringify(input_def, null, 2)); // create the input field object based upon the type supplied switch(input['type'].toLowerCase()) { @@ -100,9 +104,11 @@ function generate_form_input_field(form, input, value, disabled, zombie) { } } + console.log("Before setting values input field."); if(value) input_field.setValue(value); if(disabled) input_field.setDisabled(true); - + + console.log("Before adding input field to form values input field."); form.add(input_field); }; @@ -424,64 +430,4 @@ function genNewExploitPanel(panel, command_module_id, command_module_name, zombi } }); } -}; - - -function genNewAutoRunModulePanel(panel, module, ruleId) { - if(typeof panel != 'object') { - Ext.beef.msg('Bad!', 'Incorrect panel chosen.'); - return; - } - - panel.removeAll(); - if(module.name == 'some special command module') { - //HERE we will develop specific panels for the command modules that require it. - } else { - var form = new Ext.form.FormPanel({ - id: `form-are-module-${ruleId}`, - border: false, - labelWidth: 75, - defaultType: 'textfield', - title: module.name, - bodyStyle: 'padding: 5px;', - - items: [ - new Ext.form.DisplayField({ - name: 'command_module_description', - fieldLabel: 'Description', - fieldClass: 'command-module-panel-description', - value: module.description - }), - ], - - buttons:[{ - text: "Save Module", - handler: function() {console.log("Saved module...")} - }] - }); - - // create the panel and hide it - /* - var payload_panel = new Ext.Panel({ - id: 'payload-panel', // used with Ext.GetCmp('payload-panel') - bodyStyle: 'padding:10px;', // we can assign styles to the main div - layout : 'form', - bodyBorder: false, - height: 200, - hidden: true, - border: false //we can remove the border of the panel - }); - */ - - Ext.each(module.Data, function(input){ - // Fake a zombie "session" property for the sake of getting it working. - generate_form_input_field(form, input, null, false, {session: module.name})} - ); - - //form.add(payload_panel); - panel.add(form); - panel.doLayout(); - // hide the load mask after rendering of the config panel is done - //panel.configLoadMask.hide(); - } }; \ No newline at end of file From a47a35e7bcac63288511b016a707045d3a720bde Mon Sep 17 00:00:00 2001 From: root Date: Mon, 26 Feb 2024 00:35:12 -0500 Subject: [PATCH 18/21] Edit Autorun rules in UI with dynamic input fields for modules. --- .../javascript/ui/panel/AutoRunModuleForm.js | 27 ++++++++----------- .../javascript/ui/panel/AutoRunRuleForm.js | 1 + .../media/javascript/ui/panel/common.js | 2 +- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js index ccffd38b3..ccbd90c18 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js @@ -46,7 +46,7 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind const chainModeComboId = `rule-${ruleId}-module-combo-${index}`; const token = BeefWUI.get_rest_token(); - // TODO: Load in modules from ruby. + // TODO: Change the module. /* const moduleSelect = new Ext.form.ComboBox({ fieldLabel: 'Command Module', @@ -84,40 +84,35 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind xtype: 'displayfield', fieldLabel: 'Module Author', value: moduleData.author ? moduleData.author : 'anonymous', - } + }, ], listeners: { afterrender: loadModule } }); - async function loadModule() { - // TODO: Need to query module option types so that not everything is given a textfield. const moduleInfo = await loadModuleInfo(token, moduleData.name); if (!moduleInfo) { moduleOptionsContainer.update("

Failed to load module information.

"); return; } - console.log("Module data:"); - console.log(moduleData); - console.log("Module info:"); - console.log(JSON.stringify(moduleInfo, null, 2)); - console.log("Module options, should be an array:"); - console.log(moduleInfo.options); - - //genNewAutoRunModulePanel(moduleOptionsContainer, moduleInfo, ruleId); for (let i = 0; i < moduleInfo.options.length; i++) { - console.log(JSON.stringify(moduleInfo.options[i], null, 2)); - // TODO add real autorun module value by default. - generate_form_input_field( + const inputField = generate_form_input_field( moduleOptionsContainer, moduleInfo.options[i], - null, + moduleData.options[moduleInfo.options[i].name], false, {session: `${moduleInfo.name}-module-${index}-field-${i}`} ); + // Ensure any changes to the element are reflected in the newRule object. + // When the user saves the rule the whole newRule object will be saved, + // including any changes made to these input fields. + inputField.on('change', function(_inputF, newValue, oldValue) { + moduleData.options[moduleInfo.options[i].name] = newValue; + }); + }; moduleOptionsContainer.doLayout(); } diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index ee69c7f8f..f5be4569d 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -29,6 +29,7 @@ AutoRunRuleForm = function(rule, modules, deleteFn, updateFn) { const newRule = JSON.parse(JSON.stringify(rule)); newRule.modules = JSON.parse(newRule['modules']); newRule.execution_delay = JSON.parse(newRule['execution_delay']); + newRule.execution_order = JSON.parse(newRule['execution_order']); const moduleContainer = new Ext.Container({ style: { padding: '10 10 10 10', diff --git a/extensions/admin_ui/media/javascript/ui/panel/common.js b/extensions/admin_ui/media/javascript/ui/panel/common.js index 255870faf..79b46bb3e 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/common.js +++ b/extensions/admin_ui/media/javascript/ui/panel/common.js @@ -110,7 +110,7 @@ function generate_form_input_field(form, input, value, disabled, zombie) { console.log("Before adding input field to form values input field."); form.add(input_field); - + return input_field; }; function get_dynamic_payload_details(payload, zombie) { From fd4c4c1af5c7cdadba50aad5d8a7f425beab2714 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 28 Feb 2024 00:38:18 -0500 Subject: [PATCH 19/21] Module combobox provided. change handler not implemented yet. --- .../javascript/ui/panel/AutoRunModuleForm.js | 53 ++++++++++--------- .../javascript/ui/panel/AutoRunRuleForm.js | 4 +- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js index ccbd90c18..09f6b5d81 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js @@ -7,22 +7,19 @@ loadModuleInfo = async function(token, moduleName) { throw new Error(`Getting auto run rules failed with status ${searchResponse.status}`); } const searchData = await searchResponse.json(); - console.log("Search data:"); - console.log(searchData); if (typeof searchData.id === 'number') { moduleId = searchData.id; } else { throw new Error("Searching module name failed."); } - // DEBUG log - console.log(`Successfully retrieved module id for ${moduleName} = ${moduleId}`); - const infoResponse = await fetch(`/api/modules/${moduleId}?token=${token}`); const infoData = await infoResponse.json(); if (!infoData) { throw new Error(`Module with name ${moduleName} and ID ${moduleId} couldn't be retrived.`); } + // Set the module Id incase we need it later. + infoData.id = moduleId; return infoData; } catch(error) { @@ -41,36 +38,40 @@ loadModuleInfo = async function(token, moduleName) { * moveUp: moves the module up one spot in the Auto Run execution order. * moveDown: moves the module down one spot in the Auto Run exection order. */ -AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, index) { +AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, index, moduleList) { const moduleTextAreaId = `rule-${ruleId}-module-textarea-${index}`; const chainModeComboId = `rule-${ruleId}-module-combo-${index}`; const token = BeefWUI.get_rest_token(); - // TODO: Change the module. - /* + const comboStore = new Ext.data.Store({ + data: moduleList, + reader: new Ext.data.JsonReader({ + fields: ['id', 'name'], + }), + proxy: new Ext.data.MemoryProxy(moduleList) + }); + const moduleSelect = new Ext.form.ComboBox({ - fieldLabel: 'Command Module', - store: [{name: 'hi', id: 1}], - queryMode: 'local', // Set the queryMode to 'remote' + fieldLabel: 'Change Module', + store: comboStore, + queryMode: 'local', displayField: 'name', valueField: 'id', editable: false, // Disable manual editing of the field forceSelection: true, // Force selection from the list - //listeners: { - // render: function (combo) { - // combo.setValue(moduleData.name); - // }, - // select: function(combo, newValue, oldValue) { - // if (newValue) { - // console.log("Combo value selected."); - // } - // } - //} + triggerAction: 'all', + typeAhead: true, + listeners: { + select: function(combo, newValue, oldValue) { + if (newValue) { + console.log(`Combo value selected ${newValue.name}.`); + } + } + } }); - */ const moduleOptionsContainer = new Ext.Panel({ - title: `${moduleData.name}`, + title: moduleData.name, padding: '10 10 10 10', layout: 'form', border: false, @@ -98,6 +99,10 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind return; } + // Update the combobox default value to be this module. + // Can't use the moduleData name since it doesn't match the ID. + moduleSelect.setValue(moduleInfo.id); + for (let i = 0; i < moduleInfo.options.length; i++) { const inputField = generate_form_input_field( moduleOptionsContainer, @@ -144,7 +149,7 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind AutoRunModuleForm.superclass.constructor.call(this, { items: [ - //moduleSelect, + moduleSelect, moduleOptionsContainer, buttonContainer ] diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index f5be4569d..6486d842e 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -87,13 +87,13 @@ AutoRunRuleForm = function(rule, modules, deleteFn, updateFn) { isFirstModule ? undefined : function() {reorderModule(i, 'forward')}, isLastModule ? undefined : function() {reorderModule(i, 'back')}, rule.id, - i + i, + modules )); } } function handleUpdateRule() { - // TODO: Check if inputs are valid. // TODO: Need to overwrite module order. const form = self.getForm(); const formValues = form.getValues(); From f7bef44b8fb5e6a10fe620eb121200a1523d4db8 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 1 Mar 2024 05:39:01 -0500 Subject: [PATCH 20/21] Autorun module change and save through UI. --- .../javascript/ui/panel/AutoRunModuleForm.js | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js index 09f6b5d81..f744af951 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunModuleForm.js @@ -1,16 +1,18 @@ -loadModuleInfo = async function(token, moduleName) { - let moduleId = null; +loadModuleInfo = async function(token, moduleId, moduleName) { try { - const searchResponse = await fetch(`/api/modules/search/${moduleName}?token=${token}`); - if (!searchResponse.ok) { - throw new Error(`Getting auto run rules failed with status ${searchResponse.status}`); - } - const searchData = await searchResponse.json(); - if (typeof searchData.id === 'number') { - moduleId = searchData.id; - } else { - throw new Error("Searching module name failed."); + // If all we have is the name then we need to get the ID first. + if (moduleId === undefined) { + const searchResponse = await fetch(`/api/modules/search/${moduleName}?token=${token}`); + if (!searchResponse.ok) { + throw new Error(`Getting auto run rules failed with status ${searchResponse.status}`); + } + const searchData = await searchResponse.json(); + if (typeof searchData.id === 'number') { + moduleId = searchData.id; + } else { + throw new Error("Searching module name failed."); + } } const infoResponse = await fetch(`/api/modules/${moduleId}?token=${token}`); @@ -62,38 +64,39 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind triggerAction: 'all', typeAhead: true, listeners: { - select: function(combo, newValue, oldValue) { - if (newValue) { - console.log(`Combo value selected ${newValue.name}.`); + select: async function(combo) { + const selectedModuleId = combo.getValue() + const moduleInfo = await loadModuleInfo(token, selectedModuleId, undefined); + if (!moduleInfo) { + console.error("Failed to load new module."); + return; } + // Update the module data to reflect the new module. + moduleData.name = moduleInfo.name; + moduleData.condition = moduleInfo.condition ? moduleInfo.condition : null; + moduleData.options = {}; + for (let i = 0; i < moduleInfo.options.length; i++) { + const newOption = moduleInfo.options[i]; + moduleData.options[newOption.name] = newOption.value ? newOption.value : ''; + } + loadModule(moduleInfo); } } }); const moduleOptionsContainer = new Ext.Panel({ - title: moduleData.name, - padding: '10 10 10 10', + title: `Module ${index + 1}`, + tbar: [moduleSelect], layout: 'form', border: false, - items: [ - { - xtype: 'displayfield', - fieldLabel: 'Module Name', - value: moduleData.name, - }, - { - xtype: 'displayfield', - fieldLabel: 'Module Author', - value: moduleData.author ? moduleData.author : 'anonymous', - }, - ], listeners: { - afterrender: loadModule + afterrender: function() {loadModule(undefined)} } }); - async function loadModule() { - const moduleInfo = await loadModuleInfo(token, moduleData.name); + async function loadModule(moduleInfo) { + if (!moduleInfo) + moduleInfo = await loadModuleInfo(token, undefined, moduleData.name); if (!moduleInfo) { moduleOptionsContainer.update("

Failed to load module information.

"); return; @@ -103,6 +106,17 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind // Can't use the moduleData name since it doesn't match the ID. moduleSelect.setValue(moduleInfo.id); + // Setup module form elements. Remove all incase we're switching from a different module. + moduleOptionsContainer.removeAll(); + moduleOptionsContainer.add(new Ext.form.DisplayField({ + fieldLabel: 'Module Name', + value: moduleInfo.name + })) + moduleOptionsContainer.add(new Ext.form.DisplayField({ + fieldLabel: 'Module Author', + value: moduleInfo.author ? moduleInfo.author : 'anonymous' + })) + for (let i = 0; i < moduleInfo.options.length; i++) { const inputField = generate_form_input_field( moduleOptionsContainer, @@ -149,7 +163,6 @@ AutoRunModuleForm = function(moduleData, deleteFn, moveUp, moveDown, ruleId, ind AutoRunModuleForm.superclass.constructor.call(this, { items: [ - moduleSelect, moduleOptionsContainer, buttonContainer ] From bc1b3efa78bcd41a7536d30cbf58626b16fb217d Mon Sep 17 00:00:00 2001 From: root Date: Fri, 1 Mar 2024 07:16:36 -0500 Subject: [PATCH 21/21] Removed debug console logs. --- .../media/javascript/ui/panel/AutoRunRuleForm.js | 3 --- .../admin_ui/media/javascript/ui/panel/AutoRunTab.js | 9 ++------- extensions/admin_ui/media/javascript/ui/panel/common.js | 6 ------ 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js index 6486d842e..19df10537 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunRuleForm.js @@ -52,7 +52,6 @@ AutoRunRuleForm = function(rule, modules, deleteFn, updateFn) { } function removeModule(index) { - console.log("Removing module."); // Remove element from execution_order and execution_delay arrays. newRule.modules.splice(index, 1); newRule.execution_delay.splice(index, 1); @@ -103,8 +102,6 @@ AutoRunRuleForm = function(rule, modules, deleteFn, updateFn) { chain_mode: formValues[chainModeComboId], execution_order: [...Array(newRule.modules.length).keys()], }; - console.log("UPDATED RULE."); - console.log(JSON.stringify(updatedRule, null, 2)); updateFn(updatedRule); } diff --git a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js index 4a6b77057..9a4948374 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js +++ b/extensions/admin_ui/media/javascript/ui/panel/AutoRunTab.js @@ -1,4 +1,4 @@ -const areNotification = { +const defaultRule = { "name": "Display an alert", "author": "mgeeky", "modules": [ @@ -33,7 +33,6 @@ getCurrentRules = async function(token) { const rules = JSON.parse(data.rules); if (data.success === true && Array.isArray(rules)) { - console.log(rules); return rules; } @@ -55,10 +54,6 @@ getModules = async function(token) { } const modules = await res.json(); - // DEBUG log - console.log("Successfully retrieved active modules:"); - console.log(modules); - return modules; } catch(error) { @@ -122,7 +117,7 @@ AutoRunTab = function() { const res = await fetch(`/api/autorun/rule/add?token=${token}`, { method: 'POST', headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(areNotification) + body: JSON.stringify(defaultRule) }); if (!res.ok) { console.error(`Failed when adding a new rule with status ${res.status}.`); diff --git a/extensions/admin_ui/media/javascript/ui/panel/common.js b/extensions/admin_ui/media/javascript/ui/panel/common.js index 79b46bb3e..48c1f7047 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/common.js +++ b/extensions/admin_ui/media/javascript/ui/panel/common.js @@ -20,8 +20,6 @@ var re_execute_command_title = 'Re-execute command' function generate_form_input_field(form, input, value, disabled, zombie) { var input_field = null; var input_def = null; - console.log("Input type: "); - console.log(JSON.stringify(input, null, 2)); if (!input['ui_label']) input['ui_label'] = input['name']; if (!input['type']) input['type'] = 'textfield'; @@ -35,8 +33,6 @@ function generate_form_input_field(form, input, value, disabled, zombie) { allowBlank: false, value: input['value'] }; - console.log("Input Def:"); - console.log(JSON.stringify(input_def, null, 2)); // create the input field object based upon the type supplied switch(input['type'].toLowerCase()) { @@ -104,11 +100,9 @@ function generate_form_input_field(form, input, value, disabled, zombie) { } } - console.log("Before setting values input field."); if(value) input_field.setValue(value); if(disabled) input_field.setDisabled(true); - console.log("Before adding input field to form values input field."); form.add(input_field); return input_field; };