forked from p15670423/monkey
UI: Separate credential configuration and config
This commit is contained in:
parent
47a87e14e6
commit
0cee5ac00d
|
@ -0,0 +1,27 @@
|
|||
import Form from 'react-jsonschema-form-bs4';
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default function CredentialsConfig(props) {
|
||||
const {
|
||||
schema,
|
||||
uiSchema,
|
||||
credentials,
|
||||
onChange,
|
||||
customFormats,
|
||||
className
|
||||
} = props;
|
||||
|
||||
let credentialsCopy = _.clone(credentials);
|
||||
|
||||
return (<>
|
||||
<Form schema={schema}
|
||||
uiSchema={uiSchema}
|
||||
formData={credentialsCopy}
|
||||
onChange={(formData) => {onChange(formData.formData)}}
|
||||
customFormats={customFormats}
|
||||
className={className}
|
||||
liveValidate
|
||||
children={true}/>
|
||||
</>)
|
||||
}
|
|
@ -2,6 +2,7 @@ import Form from 'react-jsonschema-form-bs4';
|
|||
import React, {useState, useEffect} from 'react';
|
||||
import {Nav} from 'react-bootstrap';
|
||||
import _ from 'lodash';
|
||||
import CredentialsConfig from './CredentialsConfig';
|
||||
|
||||
const sectionOrder = [
|
||||
'exploitation',
|
||||
|
@ -19,30 +20,27 @@ export default function PropagationConfig(props) {
|
|||
onChange,
|
||||
customFormats,
|
||||
className,
|
||||
formData
|
||||
configuration,
|
||||
credentials,
|
||||
onCredentialChange
|
||||
} = props;
|
||||
const [selectedSection, setSelectedSection] = useState(initialSection);
|
||||
const [displayedSchema, setDisplayedSchema] = useState(getSchemaByKey(schema, initialSection));
|
||||
const [displayedSchemaUi, setDisplayedSchemaUi] = useState(getUiSchemaByKey(uiSchema, initialSection));
|
||||
const [localFormData, setLocalFormData] = useState(formData[initialSection]);
|
||||
const [localFormData, setLocalFormData] = useState(configuration[initialSection]);
|
||||
|
||||
useEffect(() => {
|
||||
setLocalFormData(formData[selectedSection]);
|
||||
setLocalFormData(configuration[selectedSection]);
|
||||
setDisplayedSchema(getSchemaByKey(schema, selectedSection));
|
||||
setDisplayedSchemaUi(getUiSchemaByKey(uiSchema, selectedSection));
|
||||
setLocalFormData(formData[selectedSection]);
|
||||
}, [selectedSection])
|
||||
|
||||
useEffect(() => {
|
||||
setLocalFormData(formData[selectedSection]);
|
||||
}, [formData])
|
||||
const onFormDataChange = (formData) => {
|
||||
let formDataClone = _.clone(formData.formData);
|
||||
let configurationClone = _.clone(configuration);
|
||||
|
||||
const onInnerDataChange = (innerData) => {
|
||||
let innerDataClone = _.clone(innerData);
|
||||
let formDataClone = _.clone(formData);
|
||||
|
||||
formDataClone[selectedSection] = innerDataClone.formData;
|
||||
onChange({formData: formDataClone});
|
||||
configurationClone[selectedSection] = formDataClone;
|
||||
onChange(configurationClone);
|
||||
}
|
||||
|
||||
const setSection = (sectionKey) => {
|
||||
|
@ -50,7 +48,7 @@ export default function PropagationConfig(props) {
|
|||
}
|
||||
|
||||
const renderNav = () => {
|
||||
return (<Nav variant='tabs'
|
||||
return (<Nav variant="tabs"
|
||||
fill
|
||||
activeKey={selectedSection} onSelect={setSection}
|
||||
style={{'marginBottom': '2em'}}
|
||||
|
@ -64,18 +62,30 @@ export default function PropagationConfig(props) {
|
|||
</Nav>)
|
||||
}
|
||||
|
||||
const getForm = () => {
|
||||
if (selectedSection === 'credentials') {
|
||||
return <CredentialsConfig schema={displayedSchema}
|
||||
uiSchema={displayedSchemaUi}
|
||||
credentials={credentials}
|
||||
onChange={onCredentialChange}
|
||||
customFormats={customFormats}
|
||||
className={className}/>
|
||||
} else {
|
||||
return <Form schema={displayedSchema}
|
||||
uiSchema={displayedSchemaUi}
|
||||
formData={localFormData}
|
||||
onChange={onFormDataChange}
|
||||
customFormats={customFormats}
|
||||
className={className}
|
||||
liveValidate
|
||||
// children={true} hides the submit button
|
||||
children={true}/>
|
||||
}
|
||||
}
|
||||
|
||||
return (<div>
|
||||
{renderNav()}
|
||||
<Form schema={displayedSchema}
|
||||
uiSchema={displayedSchemaUi}
|
||||
formData={localFormData}
|
||||
onChange={onInnerDataChange}
|
||||
customFormats={customFormats}
|
||||
className={className}
|
||||
liveValidate>
|
||||
<button type='submit' className={'hidden'}>Submit</button>
|
||||
</Form>
|
||||
{getForm()}
|
||||
</div>)
|
||||
}
|
||||
|
||||
|
@ -91,8 +101,5 @@ function getNavTitle(schema, key) {
|
|||
if (key === 'maximum_depth') {
|
||||
return 'General';
|
||||
}
|
||||
if (key === 'credentials') {
|
||||
return 'Credentials';
|
||||
}
|
||||
return schema['properties'][key].title;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,85 @@
|
|||
import {defaultCredentials} from '../../services/configuration/propagation/credentials';
|
||||
import _ from 'lodash';
|
||||
|
||||
export function reformatConfig(config, reverse = false) {
|
||||
if (reverse) {
|
||||
config['payloads'] = [{'name': 'ransomware', 'options': config['payloads']}]
|
||||
} else {
|
||||
config['payloads'] = config['payloads'][0]['options'];
|
||||
}
|
||||
return config;
|
||||
if (reverse) {
|
||||
config['payloads'] = [{'name': 'ransomware', 'options': config['payloads']}]
|
||||
} else {
|
||||
config['payloads'] = config['payloads'][0]['options'];
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
export function formatCredentialsForForm(credentials) {
|
||||
console.log(credentials);
|
||||
let formattedCredentials = _.clone(defaultCredentials);
|
||||
for (let i = 0; i < credentials.length; i++) {
|
||||
for (let j = 0; j < credentials[i].identities.length; j++) {
|
||||
formattedCredentials['exploit_user_list'].push(credentials[i]['identities'][j].username)
|
||||
}
|
||||
for (let j = 0; j < credentials[i].secrets.length; j++) {
|
||||
console.log(credentials[i])
|
||||
let secret = credentials[i]['secrets'][j];
|
||||
if (secret['credential_type'] === 'PASSWORD') {
|
||||
formattedCredentials['exploit_password_list'].push(secret['password'])
|
||||
}
|
||||
if (secret['credential_type'] === 'NT_HASH') {
|
||||
formattedCredentials['exploit_ntlm_hash_list'].push(secret['nt_hash'])
|
||||
}
|
||||
if (secret['credential_type'] === 'LM_HASH') {
|
||||
formattedCredentials['exploit_lm_hash_list'].push(secret['lm_hash'])
|
||||
}
|
||||
if (secret['credential_type'] === 'SSH_KEY') {
|
||||
let keypair = {'public_key': secret['public_key'], 'private_key': secret['private_key']}
|
||||
formattedCredentials['exploit_ssh_keys'].push(keypair)
|
||||
}
|
||||
}
|
||||
}
|
||||
return formattedCredentials;
|
||||
}
|
||||
|
||||
export function formatCredentialsForIsland(credentials) {
|
||||
let formattedCredentials = [];
|
||||
let usernames = credentials['exploit_user_list'];
|
||||
for (let i = 0; i < usernames.length; i++) {
|
||||
formattedCredentials.push({
|
||||
'identities': [{'username': usernames[i], 'credential_type': 'USERNAME'}],
|
||||
'secrets': []
|
||||
})
|
||||
}
|
||||
|
||||
let passwords = credentials['exploit_password_list'];
|
||||
for (let i = 0; i < passwords.length; i++) {
|
||||
formattedCredentials.push({
|
||||
'identities': [],
|
||||
'secrets': [{'credential_type': 'PASSWORD', 'password': passwords[i]}]
|
||||
})
|
||||
}
|
||||
|
||||
let nt_hashes = credentials['exploit_ntlm_hash_list'];
|
||||
for (let i = 0; i < nt_hashes.length; i++) {
|
||||
formattedCredentials.push({
|
||||
'identities': [],
|
||||
'secrets': [{'credential_type': 'NT_HASH', 'nt_hash': nt_hashes[i]}]
|
||||
})
|
||||
}
|
||||
|
||||
let lm_hashes = credentials['exploit_lm_hash_list'];
|
||||
for (let i = 0; i < lm_hashes.length; i++) {
|
||||
formattedCredentials.push({
|
||||
'identities': [],
|
||||
'secrets': [{'credential_type': 'LM_HASH', 'lm_hash': lm_hashes[i]}]
|
||||
})
|
||||
}
|
||||
|
||||
let ssh_keys = credentials['exploit_ssh_keys'];
|
||||
for (let i = 0; i < ssh_keys.length; i++) {
|
||||
formattedCredentials.push({
|
||||
'identities': [],
|
||||
'secrets': [{'credential_type': 'SSH_KEYPAIR', 'private_key': ssh_keys[i]['private_key'],
|
||||
'public_key': ssh_keys[i]['public_key']}]
|
||||
})
|
||||
}
|
||||
|
||||
return formattedCredentials;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,11 @@ import applyUiSchemaManipulators from '../configuration-components/UISchemaManip
|
|||
import HtmlFieldDescription from '../configuration-components/HtmlFieldDescription.js';
|
||||
import CONFIGURATION_TABS_PER_MODE from '../configuration-components/ConfigurationTabs.js';
|
||||
import {SCHEMA} from '../../services/configuration/configSchema.js';
|
||||
import {reformatConfig} from '../configuration-components/ReformatHook';
|
||||
import {
|
||||
reformatConfig,
|
||||
formatCredentialsForForm,
|
||||
formatCredentialsForIsland
|
||||
} from '../configuration-components/ReformatHook';
|
||||
|
||||
const CONFIG_URL = '/api/agent-configuration';
|
||||
const RESET_URL = '/api/reset-agent-configuration';
|
||||
|
@ -39,6 +43,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
|
||||
this.state = {
|
||||
configuration: {},
|
||||
credentials: {},
|
||||
currentFormData: {},
|
||||
importCandidateConfig: null,
|
||||
lastAction: 'none',
|
||||
|
@ -94,6 +99,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
currentFormData: monkeyConfig[this.state.selectedSection]
|
||||
})
|
||||
});
|
||||
this.updateCredentials();
|
||||
};
|
||||
|
||||
onUnsafeConfirmationCancelClick = () => {
|
||||
|
@ -110,7 +116,19 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
}
|
||||
}
|
||||
|
||||
updateCredentials = () => {
|
||||
this.authFetch(CREDENTIALS_URL)
|
||||
.then(res => res.json())
|
||||
.then(credentials => {
|
||||
credentials = formatCredentialsForForm(credentials);
|
||||
this.setState({
|
||||
credentials: credentials
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateConfig = () => {
|
||||
this.updateCredentials();
|
||||
this.authFetch(CONFIG_URL)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
@ -120,8 +138,8 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
configuration: data,
|
||||
currentFormData: data[this.state.selectedSection]
|
||||
});
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit = () => {
|
||||
this.setState({lastAction: configSubmitAction}, this.attemptConfigSubmit)
|
||||
|
@ -144,8 +162,7 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
}
|
||||
|
||||
configSubmit() {
|
||||
return this.sendConfig()
|
||||
.then(res => res.json())
|
||||
this.sendConfig()
|
||||
.then(() => {
|
||||
this.setState({
|
||||
lastAction: configSaveAction
|
||||
|
@ -158,12 +175,17 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
});
|
||||
}
|
||||
|
||||
onChange = ({formData}) => {
|
||||
onChange = (formData) => {
|
||||
let data = formData;
|
||||
let configuration = this.state.configuration;
|
||||
configuration[this.state.selectedSection] = formData;
|
||||
this.setState({currentFormData: formData, configuration: configuration});
|
||||
configuration[this.state.selectedSection] = data;
|
||||
this.setState({currentFormData: data, configuration: configuration});
|
||||
};
|
||||
|
||||
onCredentialChange = (credentials) => {
|
||||
this.setState({credentials: credentials});
|
||||
}
|
||||
|
||||
updateConfigSection = () => {
|
||||
let newConfig = this.state.configuration;
|
||||
|
||||
|
@ -256,6 +278,22 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
let config = JSON.parse(JSON.stringify(this.state.configuration))
|
||||
config = reformatConfig(config, true);
|
||||
|
||||
this.authFetch(CREDENTIALS_URL,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(formatCredentialsForIsland(this.state.credentials))
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw Error()
|
||||
}
|
||||
return res;
|
||||
}).catch((error) => {
|
||||
console.log(`bad configuration ${error}`);
|
||||
this.setState({lastAction: 'invalid_configuration'});
|
||||
});
|
||||
|
||||
return (
|
||||
this.authFetch(CONFIG_URL,
|
||||
{
|
||||
|
@ -285,21 +323,27 @@ class ConfigurePageComponent extends AuthComponent {
|
|||
setPbaFilenameLinux: this.setPbaFilenameLinux
|
||||
})
|
||||
formProperties['fields'] = {DescriptionField: HtmlFieldDescription};
|
||||
formProperties['formData'] = this.state.currentFormData;
|
||||
formProperties['onChange'] = this.onChange;
|
||||
formProperties['onFocus'] = this.resetLastAction;
|
||||
formProperties['customFormats'] = formValidationFormats;
|
||||
formProperties['transformErrors'] = transformErrors;
|
||||
formProperties['className'] = 'config-form';
|
||||
formProperties['liveValidate'] = true;
|
||||
formProperties['formData'] = this.state.currentFormData;
|
||||
|
||||
applyUiSchemaManipulators(this.state.selectedSection,
|
||||
formProperties['formData'],
|
||||
formProperties['uiSchema']);
|
||||
|
||||
if (this.state.selectedSection === 'propagation') {
|
||||
return (<PropagationConfig {...formProperties}/>)
|
||||
delete Object.assign(formProperties, {'configuration': formProperties.formData}).formData;
|
||||
return (<PropagationConfig {...formProperties}
|
||||
credentials={this.state.credentials}
|
||||
onCredentialChange={this.onCredentialChange}/>)
|
||||
} else {
|
||||
formProperties['onChange'] = (formData) => {
|
||||
this.onChange(formData.formData)
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Form {...formProperties} key={displayedSchema.title}>
|
||||
|
|
|
@ -59,4 +59,12 @@ const CREDENTIALS = {
|
|||
}
|
||||
}
|
||||
|
||||
export const defaultCredentials = {
|
||||
'exploit_user_list': [],
|
||||
'exploit_password_list': [],
|
||||
'exploit_lm_hash_list': [],
|
||||
'exploit_ntlm_hash_list': [],
|
||||
'exploit_ssh_keys': []
|
||||
}
|
||||
|
||||
export default CREDENTIALS;
|
||||
|
|
Loading…
Reference in New Issue