From e25529a76b52aba131a2a126c21bc7ca385103f6 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 24 Feb 2024 19:11:33 -0500 Subject: [PATCH] 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