Merge pull request #2051 from guardicore/2003-define-new-json-schema

2003 define new json schema
This commit is contained in:
ilija-lazoroski 2022-07-05 13:34:20 +02:00 committed by GitHub
commit 8873ef891b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 920 additions and 214 deletions

View File

@ -1,23 +1,25 @@
const CONFIGURATION_TABS = {
BASIC: 'basic',
BASIC_NETWORK: 'basic_network',
RANSOMWARE: 'ransomware',
MONKEY: 'monkey',
INTERNAL: 'internal'
PROPAGATION: 'propagation',
PAYLOADS: 'payloads',
PBA: 'post_breach_actions',
CUSTOM_PBA: 'custom_pbas',
CREDENTIALS_COLLECTORS: 'credential_collectors',
ADVANCED: 'advanced'
};
const advancedModeConfigTabs = [
CONFIGURATION_TABS.BASIC,
CONFIGURATION_TABS.BASIC_NETWORK,
CONFIGURATION_TABS.RANSOMWARE,
CONFIGURATION_TABS.MONKEY,
CONFIGURATION_TABS.INTERNAL
CONFIGURATION_TABS.PROPAGATION,
CONFIGURATION_TABS.PAYLOADS,
CONFIGURATION_TABS.PBA,
CONFIGURATION_TABS.CUSTOM_PBA,
CONFIGURATION_TABS.CREDENTIALS_COLLECTORS,
CONFIGURATION_TABS.ADVANCED
];
const ransomwareModeConfigTabs = [
CONFIGURATION_TABS.BASIC,
CONFIGURATION_TABS.BASIC_NETWORK,
CONFIGURATION_TABS.RANSOMWARE
CONFIGURATION_TABS.PROPAGATION,
CONFIGURATION_TABS.PAYLOADS,
CONFIGURATION_TABS.ADVANCED
];
const CONFIGURATION_TABS_PER_MODE = {

View File

@ -12,7 +12,7 @@ function applyUiSchemaManipulators(selectedSection,
function ransomwareDirManipulator(selectedSection,
formData,
uiSchema) {
if (selectedSection === 'ransomware'){
if (selectedSection === 'payloads'){
uiSchema.encryption.directories =
{'ui:disabled': !formData['encryption']['enabled']};
}

View File

@ -1,139 +1,140 @@
import AdvancedMultiSelect from '../ui-components/AdvancedMultiSelect';
import InfoBox from './InfoBox';
import TextBox from './TextBox.js';
import PbaInput from './PbaInput';
import {API_PBA_LINUX, API_PBA_WINDOWS} from '../pages/ConfigurePage';
import InfoBox from './InfoBox';
import TextBox from './TextBox';
import SensitiveTextInput from '../ui-components/SensitiveTextInput';
export default function UiSchema(props) {
const UiSchema = {
basic: {
'ui:order': ['exploiters', 'credentials'],
exploiters: {
exploiter_classes: {
propagation: {
exploitation: {
brute_force: {
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect
}
},
credentials: {
exploit_user_list: {
items: {
'ui:widget': AdvancedMultiSelect,
brute_force_classes: {
classNames: 'config-template-no-header'
}
},
exploit_password_list: {
items: {
classNames: 'config-template-no-header',
'ui:widget': SensitiveTextInput
}
}
}
},
basic_network: {
'ui:order': ['scope', 'network_analysis'],
scope: {
info_box: {
'ui:field': InfoBox
},
blocked_ips: {
items: {
vulnerability: {
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect,
vulnerability_classes: {
classNames: 'config-template-no-header'
}
},
subnet_scan_list: {
format: 'ip-list',
items: {
classNames: 'config-template-no-header'
options: {
http_ports: {
items: {
classNames: 'config-template-no-header'
}
}
}
},
network_analysis: {
inaccessible_subnets: {
items: {
network_scan: {
targets: {
info_box: {
'ui:field': InfoBox
},
blocked_ips: {
items: {
classNames: 'config-template-no-header'
}
},
inaccessible_subnets: {
items: {
classNames: 'config-template-no-header'
}
},
subnets: {
items: {
classNames: 'config-template-no-header'
}
}
},
tcp: {
ports: {
items: {
classNames: 'config-template-no-header'
}
}
},
fingerprinters:{
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect,
fingerprinter_classes: {
classNames: 'config-template-no-header'
}
}
}
},
monkey: {
post_breach: {
post_breach_actions: {
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect
},
custom_PBA_linux_cmd: {
'ui:widget': 'textarea',
'ui:emptyValue': ''
},
PBA_linux_file: {
'ui:widget': PbaInput,
'ui:options': {
filename: props.PBA_linux_filename,
apiEndpoint: API_PBA_LINUX,
setPbaFilename: props.setPbaFilenameLinux
}
},
custom_PBA_windows_cmd: {
'ui:widget': 'textarea',
'ui:emptyValue': ''
},
PBA_windows_file: {
'ui:widget': PbaInput,
'ui:options': {
filename: props.PBA_windows_filename,
apiEndpoint: API_PBA_WINDOWS,
setPbaFilename: props.setPbaFilenameWindows
}
},
PBA_linux_filename: {
classNames: 'linux-pba-file-info',
'ui:emptyValue': ''
},
PBA_windows_filename: {
classNames: 'windows-pba-file-info',
'ui:emptyValue': ''
}
},
credential_collectors: {
credential_collectors: {
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect
}
}
},
ransomware: {
payloads: {
classNames: 'config-template-no-header',
encryption: {
info_box: {
info_box : {
'ui:field': InfoBox
},
directories: {
// Directory inputs are dynamically hidden
// Directory inputs are dynamically hidden
},
text_box: {
'ui:field': TextBox
},
enabled: {'ui:widget': 'hidden'}
enabled: {
'ui:widget': 'hidden'
}
},
other_behaviors : {'ui:widget': 'hidden'}
other_behaviors : {
'ui:widget': 'hidden'
}
},
internal: {
classes: {
finger_classes: {
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect
custom_pbas: {
classNames: 'config-template-no-header',
linux_command: {
'ui:widget': 'textarea',
'ui:emptyValue': ''
},
linux_file: {
'ui:widget': PbaInput,
'ui:options': {
filename: props.linux_filename,
apiEndpoint: API_PBA_LINUX,
setPbaFilename: props.setPbaFilenameLinux
}
},
exploits: {
exploit_lm_hash_list:{
items: {
'ui:widget': SensitiveTextInput
}
},
exploit_ntlm_hash_list: {
items: {
'ui:widget': SensitiveTextInput
}
windows_command: {
'ui:widget': 'textarea',
'ui:emptyValue': ''
},
windows_file: {
'ui:widget': PbaInput,
'ui:options': {
filename: props.windows_filename,
apiEndpoint: API_PBA_WINDOWS,
setPbaFilename: props.setPbaFilenameWindows
}
},
linux_filename: {
classNames: 'linux-pba-file-info',
'ui:emptyValue': ''
},
windows_filename: {
classNames: 'windows-pba-file-info',
'ui:emptyValue': ''
}
},
post_breach_actions: {
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect,
post_breach_actions: {
classNames: 'config-template-no-header'
}
},
credential_collectors: {
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect,
credential_collectors_classes: {
classNames: 'config-template-no-header'
}
}
};

View File

@ -8,7 +8,6 @@ import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
import {faExclamationCircle} from '@fortawesome/free-solid-svg-icons/faExclamationCircle';
import {formValidationFormats} from '../configuration-components/ValidationFormats';
import transformErrors from '../configuration-components/ValidationErrorMessages';
import InternalConfig from '../configuration-components/InternalConfig';
import UnsafeConfigOptionsConfirmationModal
from '../configuration-components/UnsafeConfigOptionsConfirmationModal.js';
import UnsafeOptionsWarningModal from '../configuration-components/UnsafeOptionsWarningModal.js';
@ -18,6 +17,7 @@ import ConfigImportModal from '../configuration-components/ImportConfigModal';
import applyUiSchemaManipulators from '../configuration-components/UISchemaManipulators.tsx';
import HtmlFieldDescription from '../configuration-components/HtmlFieldDescription.js';
import CONFIGURATION_TABS_PER_MODE from '../configuration-components/ConfigurationTabs.js';
import {SCHEMA} from '../../services/configuration/config_schema.js';
const CONFIG_URL = '/api/configuration/island';
export const API_PBA_LINUX = '/api/file-upload/PBAlinux';
@ -68,24 +68,28 @@ class ConfigurePageComponent extends AuthComponent {
}
componentDidMount = () => {
let urls = [CONFIG_URL];
let urls = ['/api/agent-configuration'];
// ??? Why fetch config here and not in `render()`?
Promise.all(urls.map(url => this.authFetch(url).then(res => res.json())))
.then(data => {
let sections = [];
let monkeyConfig = data[0];
this.setInitialConfig(monkeyConfig.configuration);
// TODO: Fix when we add plugins
monkeyConfig['payloads'] = monkeyConfig['payloads'][0]['options'];
this.setInitialConfig(monkeyConfig);
for (let sectionKey of this.getSectionsOrder()) {
sections.push({
key: sectionKey,
title: monkeyConfig.schema.properties[sectionKey].title
title: SCHEMA.properties[sectionKey].title
});
}
this.setState({
schema: monkeyConfig.schema,
configuration: monkeyConfig.configuration,
schema: SCHEMA,
configuration: monkeyConfig,
sections: sections,
currentFormData: monkeyConfig.configuration[this.state.selectedSection]
currentFormData: monkeyConfig[this.state.selectedSection]
})
});
};
@ -266,9 +270,11 @@ class ConfigurePageComponent extends AuthComponent {
this.updateConfigSection();
this.currentSection = key;
let selectedSectionData = this.state.configuration[key];
this.setState({
selectedSection: key,
currentFormData: this.state.configuration[key]
currentFormData: selectedSectionData
});
};
@ -337,11 +343,11 @@ class ConfigurePageComponent extends AuthComponent {
let formProperties = {};
formProperties['schema'] = displayedSchema
formProperties['uiSchema'] = UiSchema({
PBA_linux_filename: this.state.configuration.monkey.post_breach.PBA_linux_filename,
PBA_windows_filename: this.state.configuration.monkey.post_breach.PBA_windows_filename,
selectedSection: this.state.selectedSection,
linux_filename: this.state.configuration.custom_pbas.linux_filename,
windows_filename: this.state.configuration.custom_pbas.windows_filename,
setPbaFilenameWindows: this.setPbaFilenameWindows,
setPbaFilenameLinux: this.setPbaFilenameLinux,
selectedSection: this.state.selectedSection
setPbaFilenameLinux: this.setPbaFilenameLinux
})
formProperties['fields'] = {DescriptionField: HtmlFieldDescription};
formProperties['formData'] = this.state.currentFormData;
@ -355,22 +361,18 @@ class ConfigurePageComponent extends AuthComponent {
formProperties['formData'],
formProperties['uiSchema']);
if (this.state.selectedSection === 'internal') {
return (<InternalConfig {...formProperties}/>)
} else {
return (
<div>
<Form {...formProperties}>
<button type='submit' className={'hidden'}>Submit</button>
</Form>
</div>
)
}
return (
<div>
<Form {...formProperties} key={displayedSchema.title}>
<button type='submit' className={'hidden'}>Submit</button>
</Form>
</div>
)
};
setPbaFilenameWindows = (filename) => {
let config = this.state.configuration
config.monkey.post_breach.PBA_windows_filename = filename
config.custom_pbas.windows_filename = filename
this.setState({
configuration: config
})
@ -378,7 +380,7 @@ class ConfigurePageComponent extends AuthComponent {
setPbaFilenameLinux = (filename) => {
let config = this.state.configuration
config.monkey.post_breach.PBA_linux_filename = filename
config.custom_pbas.linux_filename = filename
this.setState({
configuration: config
})
@ -391,7 +393,7 @@ class ConfigurePageComponent extends AuthComponent {
style={{'marginBottom': '2em'}}
className={'config-nav'}>
{this.state.sections.map(section => {
let classProp = section.key.startsWith('basic') ? 'tab-primary' : '';
let classProp = section.key.startsWith('propagation') ? 'tab-primary' : '';
return (
<Nav.Item key={section.key}>
<Nav.Link className={classProp} eventKey={section.key}>{section.title}</Nav.Link>

View File

@ -6,7 +6,7 @@ import {cloneDeep} from 'lodash';
import {getDefaultPaneParams, InfoPane, WarningType} from './InfoPane';
import {MasterCheckbox, MasterCheckboxState} from './MasterCheckbox';
import ChildCheckboxContainer from './ChildCheckbox';
import {getFullDefinitionByKey} from './JsonSchemaHelpers';
import {getFullDefinitionByKey, getObjectFromRegistryByRef} from './JsonSchemaHelpers';
function AdvancedMultiSelectHeader(props) {
const {
@ -17,12 +17,13 @@ function AdvancedMultiSelectHeader(props) {
onResetClick
} = props;
return (
<Card.Header className="d-flex justify-content-between">
<MasterCheckbox title={title} onClick={onCheckboxClick} checkboxState={checkboxState}/>
<Button className={'reset-safe-defaults'} type={'reset'} variant={'warning'}
hidden={hideReset} onClick={onResetClick}>
Reset to safe defaults
hidden={hideReset} onClick={onResetClick}>
Reset to safe options
</Button>
</Card.Header>
);
@ -32,22 +33,47 @@ class AdvancedMultiSelect extends React.Component {
constructor(props) {
super(props);
this.defaultValues = props.schema.default;
this.infoPaneRefString = props.schema.items.$ref;
this.registry = props.registry;
this.enumOptions = props.options.enumOptions.sort(this.compareOptions);
let selectedPluginNames = this.props.value.map(v => v.name);
let allPluginNames = this.props.options.enumOptions.map(v => v.value);
this.state = {
masterCheckboxState: this.getMasterCheckboxState(props.value),
hideReset: this.getHideResetState(props.value),
infoPaneParams: getDefaultPaneParams(
this.infoPaneRefString,
this.registry,
this.isUnsafeOptionSelected(props.value)
)
this.props.schema.items.$ref,
this.props.registry,
this.isUnsafeOptionSelected(selectedPluginNames)
),
allPluginNames: allPluginNames,
masterCheckboxState: this.getMasterCheckboxState(selectedPluginNames),
pluginDefinitions: getObjectFromRegistryByRef(this.props.schema.items.$ref,
this.props.registry).pluginDefs,
selectedPluginNames: selectedPluginNames
};
}
getOptionList = () => {
return this.props.options.enumOptions.sort(this.compareOptions);
}
onChange = (strValues) => {
let newValues = [];
for (let j = 0; j < strValues.length; j++) {
let found = false;
for (let i = 0; i < this.state.allPluginNames.length; i++) {
if (strValues[j] === this.state.allPluginNames[i]['name']) {
newValues.push(JSON.parse(JSON.stringify(this.props.value[i])))
found = true;
break;
}
}
if (!found) {
newValues.push(this.state.pluginDefinitions[strValues[j]]);
}
}
newValues = JSON.parse(JSON.stringify(newValues));
this.props.onChange(newValues)
this.setState({selectedPluginNames: newValues.map(v => v.name)});
}
// Sort options alphabetically. "Unsafe" options float to the top so that they
// do not get selected and hidden at the bottom of the list.
compareOptions = (a, b) => {
@ -62,28 +88,23 @@ class AdvancedMultiSelect extends React.Component {
}
onMasterCheckboxClick = () => {
if (this.state.masterCheckboxState === MasterCheckboxState.ALL) {
let checkboxState = this.getMasterCheckboxState(this.state.selectedPluginNames);
if (checkboxState === MasterCheckboxState.ALL) {
var newValues = [];
} else {
newValues = this.enumOptions.map(({value}) => value);
newValues = this.getOptionList().map(({value}) => value);
}
this.props.onChange(newValues);
this.setMasterCheckboxState(newValues);
this.setHideResetState(newValues);
this.setPaneInfoToDefault(this.isUnsafeOptionSelected(newValues));
this.onChange(newValues);
}
onChildCheckboxClick = (value) => {
let selectValues = this.getSelectValuesAfterClick(value);
this.props.onChange(selectValues);
this.setMasterCheckboxState(selectValues);
this.setHideResetState(selectValues);
this.onChange(selectValues);
}
getSelectValuesAfterClick(clickedValue) {
const valueArray = cloneDeep(this.props.value);
const valueArray = cloneDeep(this.state.selectedPluginNames);
if (valueArray.includes(clickedValue)) {
return valueArray.filter(e => e !== clickedValue);
@ -95,8 +116,7 @@ class AdvancedMultiSelect extends React.Component {
setMasterCheckboxState(selectValues) {
let newState = this.getMasterCheckboxState(selectValues);
if (newState != this.state.masterCheckboxState) {
if (newState !== this.state.masterCheckboxState) {
this.setState({masterCheckboxState: newState});
}
}
@ -106,7 +126,7 @@ class AdvancedMultiSelect extends React.Component {
return MasterCheckboxState.NONE;
}
if (selectValues.length !== this.enumOptions.length) {
if (selectValues.length !== this.getOptionList().length) {
return MasterCheckboxState.MIXED;
}
@ -114,16 +134,7 @@ class AdvancedMultiSelect extends React.Component {
}
onResetClick = () => {
this.props.onChange(this.defaultValues);
this.setHideResetState(this.defaultValues);
this.setMasterCheckboxState(this.defaultValues);
this.setPaneInfoToDefault(this.isUnsafeOptionSelected(this.defaultValues));
}
setHideResetState(selectValues) {
this.setState(() => ({
hideReset: this.getHideResetState(selectValues)
}));
this.setPaneInfoToSafe();
}
getHideResetState(selectValues) {
@ -135,11 +146,14 @@ class AdvancedMultiSelect extends React.Component {
}
isSafe = (itemKey) => {
return getFullDefinitionByKey(this.infoPaneRefString, this.registry, itemKey).safe;
let fullDef = getFullDefinitionByKey(this.props.schema.items.$ref,
this.props.registry, itemKey);
return fullDef.safe;
}
setPaneInfo = (itemKey) => {
let definitionObj = getFullDefinitionByKey(this.infoPaneRefString, this.registry, itemKey);
setPaneInfo = (itemKey) => {
let definitionObj = getFullDefinitionByKey(this.props.schema.items.$ref,
this.props.registry, itemKey);
this.setState(
{
infoPaneParams: {
@ -152,14 +166,10 @@ class AdvancedMultiSelect extends React.Component {
);
}
setPaneInfoToDefault(isUnsafeOptionSelected) {
this.setState(() => ({
infoPaneParams: getDefaultPaneParams(
this.props.schema.items.$ref,
this.props.registry,
isUnsafeOptionSelected
)
}));
setPaneInfoToSafe() {
let safePluginNames = this.state.allPluginNames.filter(pluginName => this.isSafe(pluginName));
this.setState({selectedPluginNames: safePluginNames});
this.onChange(safePluginNames);
}
render() {
@ -168,33 +178,33 @@ class AdvancedMultiSelect extends React.Component {
id,
multiple,
required,
schema,
value
schema
} = this.props;
return (
<div className={'advanced-multi-select'}>
<AdvancedMultiSelectHeader title={schema.title}
onCheckboxClick={this.onMasterCheckboxClick}
checkboxState={this.state.masterCheckboxState}
hideReset={this.state.hideReset} onResetClick={this.onResetClick}/>
onCheckboxClick={this.onMasterCheckboxClick}
checkboxState={this.getMasterCheckboxState(
this.state.selectedPluginNames)}
hideReset={this.getHideResetState(
this.state.selectedPluginNames)}
onResetClick={this.onResetClick}/>
<ChildCheckboxContainer id={id} multiple={multiple} required={required}
autoFocus={autofocus} isSafe={this.isSafe}
onPaneClick={this.setPaneInfo} onCheckboxClick={this.onChildCheckboxClick}
selectedValues={value} enumOptions={this.enumOptions}/>
autoFocus={autofocus} isSafe={this.isSafe}
onPaneClick={this.setPaneInfo}
onCheckboxClick={this.onChildCheckboxClick}
selectedValues={this.state.selectedPluginNames}
enumOptions={this.getOptionList()}/>
<InfoPane title={this.state.infoPaneParams.title}
body={this.state.infoPaneParams.content}
link={this.state.infoPaneParams.link}
warningType={this.state.infoPaneParams.warningType}/>
body={this.state.infoPaneParams.content}
link={this.state.infoPaneParams.link}
warningType={this.state.infoPaneParams.warningType}/>
</div>
);
}
componentDidUpdate(_prevProps) {
this.setMasterCheckboxState(this.props.value);
}
}
export default AdvancedMultiSelect;

View File

@ -1,24 +1,29 @@
function getPluginDescriptors(schema, config) {
return ([
{
name: 'Exploiters',
allPlugins: schema.definitions.exploiter_classes.anyOf,
selectedPlugins: config.basic.exploiters.exploiter_classes
name: 'Brute force exploiters',
allPlugins: schema.definitions.brute_force_classes.anyOf,
selectedPlugins: config.propagation.exploitation.brute_force
},
{
name: 'Vulnerability exploiters',
allPlugins: schema.definitions.vulnerability_classes.anyOf,
selectedPlugins: config.propagation.exploitation.vulnerability
},
{
name: 'Fingerprinters',
allPlugins: schema.definitions.finger_classes.anyOf,
selectedPlugins: config.internal.classes.finger_classes
allPlugins: schema.definitions.fingerprinter_classes.anyOf,
selectedPlugins: config.propagation.network_scan.fingerprinters
},
{
name: 'PostBreachActions',
allPlugins: schema.definitions.post_breach_actions.anyOf,
selectedPlugins: config.monkey.post_breach.post_breach_actions
selectedPlugins: config.post_breach_actions
},
{
name: 'CredentialCollectors',
allPlugins: schema.definitions.credential_collectors.anyOf,
selectedPlugins: config.monkey.credential_collectors.credential_collectors
allPlugins: schema.definitions.credential_collectors_classes.anyOf,
selectedPlugins: config.credential_collectors
}
]);
}
@ -40,7 +45,7 @@ function isUnsafePluginSelected(pluginDescriptor) {
pluginDescriptor.allPlugins.forEach(i => pluginSafety[i.enum[0]] = i.safe);
for (let selected of pluginDescriptor.selectedPlugins) {
if (!pluginSafety[selected]) {
if (!pluginSafety[selected.name]) {
return true;
}
}

View File

@ -0,0 +1,58 @@
import {customPBAConfigurationSchema} from './definitions/custom_pbas.js';
import {ransomwareSchema} from './definitions/ransomware.js';
import {propagationConfigurationSchema} from './definitions/propagation.js';
import {bruteForceExploiters, vulnerabilityExploiters} from './definitions/exploiter_classes.js';
import {credentialCollectors} from './definitions/credential_collectors.js';
import {postBreachActions} from './definitions/post_breach_actions.js';
import {fingerprinterClasses} from './definitions/fingerprinter_classes.js'
export const SCHEMA = {
'title': 'Monkey',
'type': 'object',
'definitions': {
'brute_force_classes': bruteForceExploiters,
'vulnerability_classes': vulnerabilityExploiters,
'credential_collectors_classes': credentialCollectors,
'post_breach_actions': postBreachActions,
'fingerprinter_classes': fingerprinterClasses
},
'properties': {
'propagation': propagationConfigurationSchema,
'post_breach_actions': {
'title': 'Post-breach actions',
'type': 'array',
'uniqueItems': true,
'items': {
'$ref': '#/definitions/post_breach_actions'
}
},
'custom_pbas': customPBAConfigurationSchema,
'payloads': ransomwareSchema,
'credential_collectors': {
'title': 'Credential collectors',
'type': 'array',
'uniqueItems': true,
'items': {
'$ref': '#/definitions/credential_collectors_classes'
},
'default': [
'MimikatzCollector',
'SSHCollector'
]
},
'advanced': {
'title': 'Advanced',
'type': 'object',
'properties':{
'keep_tunnel_open_time': {
'title': 'Keep tunnel open time',
'format': 'float',
'type': 'number',
'default': 30,
'description': 'Time to keep tunnel open before going down after last exploit (in seconds)'
}
}
}
},
'options': {'collapsed': true}
}

View File

@ -0,0 +1,25 @@
export const credentialCollectors = {
'title': 'Credential Collectors',
'description': 'Click on a credential collector to find out what it collects.',
'type': 'string',
'pluginDefs': {
'MimikatzCollector':{'name': 'MimikatzCollector', 'options': {}},
'SSHCollector':{'name': 'SSHCollector', 'options': {}}
},
'anyOf': [
{
'type': 'string',
'enum': ['MimikatzCollector'],
'title': 'Mimikatz Credentials Collector',
'safe': true,
'info': 'Collects credentials from Windows credential manager.'
},
{
'type': 'string',
'enum': ['SSHCollector'],
'title': 'SSH Credentials Collector',
'safe': true,
'info': 'Searches users\' home directories and collects SSH keypairs.'
}
]
}

View File

@ -0,0 +1,51 @@
export const customPBAConfigurationSchema = {
'title': 'Custom PBA',
'properties': {
'linux_command': {
'title': 'Linux post-breach command',
'type': 'string',
'default': '',
'description': 'Command to be executed after breaching. ' +
'Use this field to run custom commands or execute the uploaded ' +
'file on exploited machines.\nExample: ' +
'"chmod +x ./my_script.sh; ./my_script.sh ; rm ./my_script.sh"'
},
'linux_file': {
'title': 'Linux post-breach file',
'type': 'string',
'format': 'data-url',
'description': 'File to be uploaded after breaching. ' +
'Use the "Linux post-breach command" field to ' +
'change permissions, run, or delete the file. ' +
'Reference your file by filename.'
},
'linux_filename': {
'title': 'Linux PBA filename',
'type': 'string',
'default': ''
},
'windows_command': {
'title': 'Windows post-breach command',
'type': 'string',
'default': '',
'description': 'Command to be executed after breaching. ' +
'Use this field to run custom commands or execute the uploaded ' +
'file on exploited machine.\nExample: ' +
'"my_script.bat & del my_script.bat"'
},
'windows_file':{
'title': 'Windows post-breach file',
'type': 'string',
'format': 'data-url',
'description': 'File to be uploaded after breaching. ' +
'Use the "Windows post-breach command" field to ' +
'change permissions, run or delete the file. ' +
'Reference your file by filename.'
},
'windows_filename': {
'title': 'Windows PBA filename',
'type': 'string',
'default': ''
}
}
}

View File

@ -0,0 +1,26 @@
import {exploitationOptionsConfigurationSchema} from './exploitation_options.js';
export const exploitationConfigurationSchema = {
'title': 'Exploiters',
'type': 'object',
'description': 'Choose which exploiters the Monkey will attempt.',
'properties': {
'brute_force': {
'title': 'Brute force exploiters',
'type': 'array',
'uniqueItems': true,
'items': {
'$ref': '#/definitions/brute_force_classes'
}
},
'vulnerability': {
'title': 'Vulnerability Exploiters',
'type': 'array',
'uniqueItems': true,
'items': {
'$ref': '#/definitions/vulnerability_classes'
}
},
'options': exploitationOptionsConfigurationSchema
}
};

View File

@ -0,0 +1,15 @@
export const exploitationOptionsConfigurationSchema = {
'title': 'Exploiters Options',
'type': 'object',
'properties': {
'http_ports': {
'title': 'HTTP Ports',
'type': 'array',
'items': {
'type': 'integer'
},
'default': [80, 8080, 443, 8008, 7001, 9200, 8983, 9600],
'description': 'List of ports the monkey will check if are being used for HTTP'
}
}
}

View File

@ -0,0 +1,121 @@
export const bruteForceExploiters = {
'title': 'Brute force exploiters',
'description': 'Click on exploiter to get more information about it.'
+ '\u26A0'
+ ' Note that using unsafe exploits may cause crashes of the exploited ' +
'machine/service.',
'type': 'string',
'pluginDefs': {
'SmbExploiter': {'name': 'SmbExploiter', 'options': {}},
'PowerShellExploiter': {'name': 'PowerShellExploiter', 'options': {}},
'WmiExploiter': {'name': 'WmiExploiter', 'options': {}},
'MSSQLExploiter': {'name': 'MSSQLExploiter', 'options': {}},
'SSHExploiter': {'name': 'SSHExploiter', 'options': {}}
},
'anyOf': [
{
'type': 'string',
'enum': ['SmbExploiter'],
'title': 'SMB Exploiter',
'safe': true,
'info': 'Brute forces using credentials provided by user and' +
' hashes gathered by mimikatz.',
'link': 'https://www.guardicore.com/infectionmonkey/docs/reference' +
'/exploiters/smbexec/'
},
{
'type': 'string',
'enum': ['PowerShellExploiter'],
'title': 'PowerShell Remoting Exploiter',
'info': 'Exploits PowerShell remote execution setups. PowerShell Remoting uses Windows ' +
'Remote Management (WinRM) to allow users to run PowerShell commands on remote ' +
'computers.',
'safe': true,
'link': 'https://www.guardicore.com/infectionmonkey' +
'/docs/reference/exploiters/powershell'
},
{
'type': 'string',
'enum': ['WmiExploiter'],
'title': 'WMI Exploiter',
'safe': true,
'info': 'Brute forces WMI (Windows Management Instrumentation) ' +
'using credentials provided by user and hashes gathered by ' +
'mimikatz.',
'link': 'https://www.guardicore.com/infectionmonkey/docs/reference' +
'/exploiters/wmiexec/'
},
{
'type': 'string',
'enum': ['MSSQLExploiter'],
'title': 'MSSQL Exploiter',
'safe': true,
'info': 'Tries to brute force into MsSQL server and uses insecure ' +
'configuration to execute commands on server.',
'link': 'https://www.guardicore.com/infectionmonkey/docs/reference' +
'/exploiters/mssql/'
},
{
'type': 'string',
'enum': ['SSHExploiter'],
'title': 'SSH Exploiter',
'safe': true,
'info': 'Brute forces using credentials provided by user and SSH keys ' +
'gathered from systems.',
'link': 'https://www.guardicore.com/infectionmonkey/docs/reference' +
'/exploiters/sshexec/'
}
]
}
export const vulnerabilityExploiters = {
'title': 'Vulnerability exploiters',
'description': 'Click on exploiter to get more information about it.' +
'\u26A0 Note that using unsafe exploits may cause craches of the exploited ' +
'machine/service.',
'type': 'string',
'pluginDefs': {
'ZerologonExploiter': {'name': 'ZerologonExploiter', 'options':{}},
'Log4ShellExploiter': {'name': 'Log4ShellExploiter', 'options': {}},
'HadoopExploiter': {'name': 'HadoopExploiter', 'options': {}}
},
'anyOf': [
{
'type': 'string',
'enum': ['ZerologonExploiter'],
'title': 'Zerologon Exploiter',
'safe': false,
'info': 'Exploits a privilege escalation vulnerability (CVE-2020-1472) in a Windows ' +
'server domain controller (DC) by using the Netlogon Remote Protocol (MS-NRPC). ' +
'This exploiter changes the password of a Windows server DC account, steals ' +
'credentials, and then attempts to restore the original DC password. The victim DC ' +
'will be unable to communicate with other DCs until the original ' +
'password has been restored. If Infection Monkey fails to restore the ' +
'password automatically, you\'ll have to do it manually. For more ' +
'information, see the documentation.',
'link': 'https://www.guardicore.com/infectionmonkey' +
'/docs/reference/exploiters/zerologon/'
},
{
'type': 'string',
'enum': ['Log4ShellExploiter'],
'title': 'Log4Shell Exploiter',
'safe': true,
'info': 'Exploits a software vulnerability (CVE-2021-44228) in Apache Log4j, a Java ' +
'logging framework. Exploitation is attempted on the following services — ' +
'Apache Solr, Apache Tomcat, Logstash.',
'link': 'https://www.guardicore.com/infectionmonkey/docs/reference' +
'/exploiters/log4shell/'
},
{
'type': 'string',
'enum': ['HadoopExploiter'],
'title': 'Hadoop/Yarn Exploiter',
'safe': true,
'info': 'Remote code execution on HADOOP server with YARN and default settings. ' +
'Logic based on ' +
'https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn.',
'link': 'https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/hadoop/'
}
]
}

View File

@ -0,0 +1,52 @@
export const fingerprinterClasses = {
'title': 'Fingerprinters',
'description': 'Fingerprint modules collect info about external services ' +
'Infection Monkey scans.',
'type': 'string',
'pluginDefs': {
'smb' : {'name':'smb', 'options':''},
'ssh' : {'name':'ssh', 'options':''},
'http' : {'name':'http', 'options':''},
'mssql' : {'name':'mssql', 'options':''},
'elastic' : {'name':'elastic', 'options':''}
},
'anyOf': [
{
'type': 'string',
'enum': ['smb'],
'title': 'SMB Fingerprinter',
'safe': true,
'info': 'Figures out if SMB is running and what\'s the version of it.'
},
{
'type': 'string',
'enum': ['ssh'],
'title': 'SSH Fingerprinter',
'safe': true,
'info': 'Figures out if SSH is running.'
},
{
'type': 'string',
'enum': ['http'],
'title': 'HTTP Fingerprinter',
'safe': true,
'info': 'Checks if host has HTTP/HTTPS ports open.'
},
{
'type': 'string',
'enum': ['mssql'],
'title': 'MSSQL Fingerprinter',
'safe': true,
'info': 'Checks if Microsoft SQL service is running and tries to gather ' +
'information about it.'
},
{
'type': 'string',
'enum': ['elastic'],
'title': 'Elastic Fingerprinter',
'safe': true,
'info': 'Checks if ElasticSearch is running and attempts to find it\'s version.'
}
]
}

View File

@ -0,0 +1,12 @@
export const icmpScanConfigurationSchema = {
'title': 'Ping scanner',
'type': 'object',
'properties': {
'timeout': {
'format': 'float',
'title': 'Ping scan timeout',
'type': 'number',
'description': 'Maximum time to wait for ping response'
}
}
}

View File

@ -0,0 +1,21 @@
import {icmpScanConfigurationSchema} from './icmp_scan.js';
import {scanTargetConfigurationSchema} from './scan_target.js';
import {tcpScanConfigurationSchema} from './tcp_scan.js';
export const networkScanConfigurationSchema = {
'title': 'Network analysis',
'type': 'object',
'properties': {
'fingerprinters': {
'title': 'Fingerprinters',
'type': 'array',
'uniqueItems': true,
'items': {
'$ref': '#/definitions/fingerprinter_classes'
}
},
'icmp': icmpScanConfigurationSchema,
'targets': scanTargetConfigurationSchema,
'tcp': tcpScanConfigurationSchema
}
}

View File

@ -0,0 +1,15 @@
export const pluginConfigurationSchema = {
'type': 'object',
'properties': {
'name': {
'title': 'Name',
'type': 'string'
},
'safe': {
'type': 'boolean'
},
'options': {
'type': 'object'
}
}
}

View File

@ -0,0 +1,111 @@
export const postBreachActions = {
'title': 'Post-Breach Actions',
'description': 'Runs scripts/commands on infected machines. These actions safely simulate what ' +
'an adversary might do after breaching a new machine. Used in ATT&CK and Zero trust reports.',
'type': 'string',
'pluginDefs': {
'CommunicateAsBackdoorUser':{'name': 'CommunicateAsBackdoorUser', 'options':{}},
'ModifyShellStartupFiles':{'name': 'ModifyShellStartupFiles', 'options':{}},
'HiddenFiles':{'name': 'HiddenFiles', 'options':{}},
'TrapCommand':{'name': 'TrapCommand', 'options':{}},
'ChangeSetuidSetgid':{'name': 'ChangeSetuidSetgid', 'options':{}},
'ScheduleJobs':{'name': 'ScheduleJobs', 'options':{}},
'Timestomping':{'name': 'Timestomping', 'options':{}},
'SignedScriptProxyExecution':{'name': 'SignedScriptProxyExecution', 'options':{}},
'AccountDiscovery':{'name': 'AccountDiscovery', 'options':{}},
'ClearCommandHistory':{'name': 'ClearCommandHistory', 'options':{}},
'ProcessListCollection':{'name': 'ProcessListCollection', 'options':{}}
},
'anyOf': [
{
'type': 'string',
'enum': ['CommunicateAsBackdoorUser'],
'title': 'Communicate as Backdoor User',
'safe': true,
'info': 'Attempts to create a new user, create HTTPS requests as that ' +
'user and delete the user ' +
'afterwards.'
},
{
'type': 'string',
'enum': ['ModifyShellStartupFiles'],
'title': 'Modify Shell Startup Files',
'safe': true,
'info': 'Attempts to modify shell startup files, like ~/.profile, ' +
'~/.bashrc, ~/.bash_profile ' +
'in linux, and profile.ps1 in windows. Reverts modifications done' +
' afterwards.'
},
{
'type': 'string',
'enum': ['HiddenFiles'],
'title': 'Hidden Files and Directories',
'safe': true,
'info': 'Attempts to create a hidden file and remove it afterward.'
},
{
'type': 'string',
'enum': ['TrapCommand'],
'title': 'Trap Command',
'safe': true,
'info': 'On Linux systems, attempts to trap a terminate signal in order ' +
'to execute a command upon receiving that signal. Removes the trap afterwards.'
},
{
'type': 'string',
'enum': ['ChangeSetuidSetgid'],
'title': 'Setuid and Setgid',
'safe': true,
'info': 'On Linux systems, attempts to set the setuid and setgid bits of ' +
'a new file. ' +
'Removes the file afterwards.',
'attack_techniques': ['T1166']
},
{
'type': 'string',
'enum': ['ScheduleJobs'],
'title': 'Job Scheduling',
'safe': true,
'info': 'Attempts to create a scheduled job on the system and remove it.'
},
{
'type': 'string',
'enum': ['Timestomping'],
'title': 'Timestomping',
'safe': true,
'info': 'Creates a temporary file and attempts to modify its time ' +
'attributes. Removes the file afterwards.'
},
{
'type': 'string',
'enum': ['SignedScriptProxyExecution'],
'title': 'Signed Script Proxy Execution',
'safe': false,
'info': 'On Windows systems, attempts to execute an arbitrary file ' +
'with the help of a pre-existing signed script.'
},
{
'type': 'string',
'enum': ['AccountDiscovery'],
'title': 'Account Discovery',
'safe': true,
'info': 'Attempts to get a listing of user accounts on the system.'
},
{
'type': 'string',
'enum': ['ClearCommandHistory'],
'title': 'Clear Command History',
'safe': false,
'info': 'Attempts to clear the command history.'
},
{
'type': 'string',
'enum': ['ProcessListCollection'],
'title': 'Process List Collector',
'safe': true,
'info': 'Collects a list of running processes on the machine.'
}
]
}

View File

@ -0,0 +1,23 @@
import {exploitationConfigurationSchema} from './exploitation.js';
import {networkScanConfigurationSchema} from './network_scan.js';
export const propagationConfigurationSchema = {
'title': 'Propagation',
'type': 'object',
'properties': {
'exploitation': exploitationConfigurationSchema,
'maximum_depth': {
'title': 'Maximum scan depth',
'type': 'integer',
'minimum': 1,
'default': 2,
'description': 'Amount of hops allowed for the monkey to spread from the ' +
'Island server. \n' +
' \u26A0' +
' Note that setting this value too high may result in the ' +
'Monkey propagating too far, '+
'if the "Local network scan" is enabled'
},
'network_scan': networkScanConfigurationSchema
}
}

View File

@ -0,0 +1,65 @@
export const ransomwareSchema = {
'title': 'Payloads',
'properties': {
'encryption': {
'title': 'Ransomware simulation',
'type': 'object',
'description': 'To simulate ransomware encryption, you\'ll need to provide Infection ' +
'Monkey with files that it can safely encrypt. On each machine where you would like ' +
'the ransomware simulation to run, create a directory and put some files in it.' +
'\n\nProvide the path to the directory that was created on each machine.',
'properties': {
'enabled': {
'title': 'Encrypt files',
'type': 'boolean',
'default': true,
'description': 'Ransomware encryption will be simulated by flipping every bit ' +
'in the files contained within the target directories.'
},
'info_box': {
'info': 'No files will be encrypted if a directory is not specified or doesn\'t ' +
'exist on a victim machine.'
},
'directories': {
'title': 'Directories to encrypt',
'type': 'object',
'properties': {
'linux_target_dir': {
'title': 'Linux target directory',
'type': 'string',
'format': 'valid-ransomware-target-path-linux',
'default': '',
'description': 'A path to a directory on Linux systems that contains ' +
'files that you will allow Infection Monkey to encrypt. If no ' +
'directory is specified, no files will be encrypted.'
},
'windows_target_dir': {
'title': 'Windows target directory',
'type': 'string',
'format': 'valid-ransomware-target-path-windows',
'default': '',
'description': 'A path to a directory on Windows systems that contains ' +
'files that you will allow Infection Monkey to encrypt. If no ' +
'directory is specified, no files will be encrypted.'
}
}
},
'text_box': {
'text': 'Note: A README.txt will be left in the specified target directory.'
}
}
},
'other_behaviors': {
'title': 'Other ransomware behavior',
'type': 'object',
'properties': {
'readme': {
'title': 'Create a README.txt file',
'type': 'boolean',
'default': true,
'description': 'Creates a README.txt ransomware note on infected systems.'
}
}
}
}
}

View File

@ -0,0 +1,70 @@
export const scanTargetConfigurationSchema = {
'title': 'Network',
'type': 'object',
'properties': {
'info_box': {
'info': 'The Monkey scans its subnet if "Local network scan" is checked. '+
'Additionally, the Monkey scans machines according to "Scan target list". '
},
'blocked_ips': {
'title': 'Blocked IPs',
'type': 'array',
'uniqueItems': true,
'items': {
'type': 'string',
'format': 'ip'
},
'default': [],
'description': 'List of IPs that the monkey will not scan.'
},
'inaccessible_subnets': {
'title': 'Network segmentation testing',
'type': 'array',
'uniqueItems': true,
'items': {
'type': 'string',
'format': 'ip-range'
},
'default': [],
'description': 'Test for network segmentation by providing a list of network segments that should NOT be accessible to each other.\n\n ' +
'For example, if you configured the following three segments: ' +
'"10.0.0.0/24", "11.0.0.2/32" and "12.2.3.0/24",' +
'a Monkey running on 10.0.0.5 will try to access machines in ' +
'the following subnets: ' +
'11.0.0.2/32, 12.2.3.0/24. An alert on successful cross-segment connections ' +
'will be shown in the reports. \n\n' +
'Network segments can be IPs, subnets or hosts. Examples:\n' +
'\tDefine a single-IP segment: "192.168.0.1"\n' +
'\tDefine a segment using a network range: ' +
'"192.168.0.5-192.168.0.20"\n' +
'\tDefine a segment using an subnet IP mask: "192.168.0.5/24"\n' +
'\tDefine a single-host segment: "printer.example"'
},
'local_network_scan': {
'title': 'Local network scan',
'type': 'boolean',
'default': true,
'description': 'Determines whether the Monkey will scan the local subnets of machines it runs on, ' +
'in addition to the IPs that are configured manually in the "Scan target list"'
},
'subnets': {
'title': 'Scan target list',
'type': 'array',
'uniqueItems': true,
'items': {
'type': 'string',
'format': 'ip-range'
},
'default': [],
'description': 'List of targets the Monkey will try to scan. Targets can be ' +
'IPs, subnets or hosts. ' +
'Examples:\n' +
'\tTarget a specific IP: "192.168.0.1"\n' +
'\tTarget a subnet using a network range: ' +
'"192.168.0.5-192.168.0.20"\n'+
'\tTarget a subnet using an IP mask: "192.168.0.5/24"\n' +
'\tTarget a specific host: "printer.example"'
}
}
}

View File

@ -0,0 +1,21 @@
export const tcpScanConfigurationSchema = {
'title': 'TCP scanner',
'type': 'object',
'properties': {
'ports': {
'title': 'TCP target ports',
'type': 'array',
'items': {
'type': 'integer'
},
'default': [22,2222,445,135,389,80,8080,443,8008,3306,7001,8088,5885,5986],
'description': 'List of TCP ports the monkey will check whether they\'re open'
},
'timeout': {
'title': 'TCP scan timeout',
'format': 'float',
'type': 'number',
'description': 'Maximum time to wait for TCP response.'
}
}
}