diff --git a/monkey/infection_monkey/master/control_channel.py b/monkey/infection_monkey/master/control_channel.py index cdf660842..ccff3e783 100644 --- a/monkey/infection_monkey/master/control_channel.py +++ b/monkey/infection_monkey/master/control_channel.py @@ -84,7 +84,7 @@ class ControlChannel(IControlChannel): ) response.raise_for_status() - return [Credentials.from_mapping(credentials) for credentials in response.json] + return [Credentials.from_mapping(credentials) for credentials in response.json()] except ( requests.exceptions.JSONDecodeError, requests.exceptions.ConnectionError, diff --git a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py index 5d58d510a..ee8beb13c 100644 --- a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py @@ -14,14 +14,14 @@ class MongoCredentialsRepository(ICredentialsRepository): """ def __init__(self, mongo: MongoClient, repository_encryptor: ILockableEncryptor): - self._mongo = mongo + self._database = mongo.monkeyisland self._repository_encryptor = repository_encryptor def get_configured_credentials(self) -> Sequence[Credentials]: - return self._get_credentials_from_collection(self._mongo.db.configured_credentials) + return self._get_credentials_from_collection(self._database.configured_credentials) def get_stolen_credentials(self) -> Sequence[Credentials]: - return self._get_credentials_from_collection(self._mongo.db.stolen_credentials) + return self._get_credentials_from_collection(self._database.stolen_credentials) def get_all_credentials(self) -> Sequence[Credentials]: configured_credentials = self.get_configured_credentials() @@ -31,20 +31,16 @@ class MongoCredentialsRepository(ICredentialsRepository): def save_configured_credentials(self, credentials: Sequence[Credentials]): # TODO: Fix deduplication of Credentials in mongo - self._save_credentials_to_collection(credentials, self._mongo.db.configured_credentials) + self._save_credentials_to_collection(credentials, self._database.configured_credentials) def save_stolen_credentials(self, credentials: Sequence[Credentials]): - self._save_credentials_to_collection(credentials, self._mongo.db.stolen_credentials) + self._save_credentials_to_collection(credentials, self._database.stolen_credentials) def remove_configured_credentials(self): - MongoCredentialsRepository._remove_credentials_fom_collection( - self._mongo.db.configured_credentials - ) + self._remove_credentials_fom_collection(self._database.configured_credentials) def remove_stolen_credentials(self): - MongoCredentialsRepository._remove_credentials_fom_collection( - self._mongo.db.stolen_credentials - ) + self._remove_credentials_fom_collection(self._database.stolen_credentials) def remove_all_credentials(self): self.remove_configured_credentials() diff --git a/monkey/monkey_island/cc/resources/propagation_credentials.py b/monkey/monkey_island/cc/resources/propagation_credentials.py index c5dfc42fe..2342d0f37 100644 --- a/monkey/monkey_island/cc/resources/propagation_credentials.py +++ b/monkey/monkey_island/cc/resources/propagation_credentials.py @@ -42,6 +42,22 @@ class PropagationCredentials(AbstractResource): return {}, HTTPStatus.NO_CONTENT + def put(self, collection=None): + credentials = [Credentials.from_mapping(c) for c in request.json] + + if collection == _configured_collection: + self._credentials_repository.remove_configured_credentials() + self._credentials_repository.save_configured_credentials(credentials) + elif collection == _stolen_collection: + self._credentials_repository.remove_stolen_credentials() + self._credentials_repository.save_stolen_credentials(credentials) + elif collection is None: + return {}, HTTPStatus.METHOD_NOT_ALLOWED + else: + return {}, HTTPStatus.NOT_FOUND + + return {}, HTTPStatus.NO_CONTENT + def delete(self, collection=None): if collection == _configured_collection: self._credentials_repository.remove_configured_credentials() diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/CredentialsConfig.tsx b/monkey/monkey_island/cc/ui/src/components/configuration-components/CredentialsConfig.tsx new file mode 100644 index 000000000..4e40f980b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/CredentialsConfig.tsx @@ -0,0 +1,26 @@ +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 (<> +
{onChange(formData.formData)}} + customFormats={customFormats} + className={className} + liveValidate + children={true}/> + ) +} diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ExportConfigModal.tsx b/monkey/monkey_island/cc/ui/src/components/configuration-components/ExportConfigModal.tsx index ef11dd67f..c56ccb4f1 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/ExportConfigModal.tsx +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ExportConfigModal.tsx @@ -10,6 +10,7 @@ import {reformatConfig} from './ReformatHook'; type Props = { show: boolean, configuration: object, + credentials: object, onHide: () => void } @@ -22,17 +23,17 @@ const ConfigExportModal = (props: Props) => { } function onSubmit() { - let config = reformatConfig(props.configuration, true); - let config_export = {'metadata': {}, 'contents': null}; + let configuration = reformatConfig(props.configuration, true); + let credentials = props.credentials; + let metadata = {'encrypted': false}; if (radioValue === 'password') { - config_export.contents = encryptText(JSON.stringify(config), pass); - config_export.metadata = {'encrypted': true}; - } else { - config_export.contents = config; - config_export.metadata = {'encrypted': false}; + configuration = encryptText(JSON.stringify(configuration), pass); + credentials = encryptText(JSON.stringify(credentials), pass); + metadata = {'encrypted': true}; } + let config_export = {'metadata': metadata, 'configuration': configuration, 'credentials': credentials}; let export_json = JSON.stringify(config_export, null, 2); let export_blob = new Blob( [export_json], diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ImportConfigModal.tsx b/monkey/monkey_island/cc/ui/src/components/configuration-components/ImportConfigModal.tsx index 0b9623b1f..c36f58911 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/ImportConfigModal.tsx +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ImportConfigModal.tsx @@ -10,7 +10,10 @@ import UnsafeConfigOptionsConfirmationModal import UploadStatusIcon, {UploadStatuses} from '../ui-components/UploadStatusIcon'; import isUnsafeOptionSelected from '../utils/SafeOptionValidator.js'; import {decryptText} from '../utils/PasswordBasedEncryptor'; - +import { + reformatConfig, + formatCredentialsForIsland +} from '../configuration-components/ReformatHook'; type Props = { show: boolean, @@ -21,9 +24,11 @@ type Props = { const ConfigImportModal = (props: Props) => { const configImportEndpoint = '/api/agent-configuration'; + const credentialsEndpoint = '/api/propagation-credentials/configured-credentials'; const [uploadStatus, setUploadStatus] = useState(UploadStatuses.clean); const [configContents, setConfigContents] = useState(null); + const [configCredentials, setConfigCredentials] = useState(null); const [password, setPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); const [configEncrypted, setConfigEncrypted] = useState(false); @@ -36,10 +41,10 @@ const ConfigImportModal = (props: Props) => { const authComponent = new AuthComponent({}); useEffect(() => { - if (configContents !== null) { + if (configContents !== null && configCredentials !== null) { tryImport(); } - }, [configContents, unsafeOptionsVerified]) + }, [configContents, configCredentials, unsafeOptionsVerified]) function tryImport() { if (configEncrypted && !showPassword){ @@ -47,8 +52,10 @@ const ConfigImportModal = (props: Props) => { } else if (configEncrypted && showPassword) { try { let decryptedConfig = JSON.parse(decryptText(configContents, password)); + let decryptedConfigCredentials = JSON.parse(decryptText(configCredentials, password)); setConfigEncrypted(false); setConfigContents(decryptedConfig); + setConfigCredentials(decryptedConfigCredentials); } catch (e) { setUploadStatus(UploadStatuses.error); setErrorMessage('Decryption failed: Password is wrong or the file is corrupted'); @@ -61,16 +68,38 @@ const ConfigImportModal = (props: Props) => { } } else { sendConfigToServer(); + sendConfigCredentialsToServer(); setUploadStatus(UploadStatuses.success); } } + function sendConfigCredentialsToServer() { + let credentials = formatCredentialsForIsland(configCredentials); + authComponent.authFetch(credentialsEndpoint, + { + method: 'PUT', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(credentials) + } + ).then(res => { + if (res.ok) { + resetState(); + props.onClose(true); + } else { + setUploadStatus(UploadStatuses.error); + setErrorMessage("Configuration file is corrupt or in an outdated format."); + } + }) + } + function sendConfigToServer() { + let config = reformatConfig(configContents, true); + delete config['advanced']; authComponent.authFetch(configImportEndpoint, { method: 'POST', headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(configContents) + body: JSON.stringify(config) } ).then(res => { if (res.ok) { @@ -92,6 +121,7 @@ const ConfigImportModal = (props: Props) => { setUploadStatus(UploadStatuses.clean); setPassword(''); setConfigContents(null); + setConfigCredentials(null); setErrorMessage(''); setShowPassword(false); setShowUnsafeOptionsConfirmation(false); @@ -113,7 +143,8 @@ const ConfigImportModal = (props: Props) => { return } setConfigEncrypted(importContents['metadata']['encrypted']); - setConfigContents(importContents['contents']); + setConfigContents(importContents['configuration']); + setConfigCredentials(importContents['credentials']); }; reader.readAsText(event.target.files[0]); } diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/PropagationConfig.tsx b/monkey/monkey_island/cc/ui/src/components/configuration-components/PropagationConfig.tsx index b00e4653d..3527bb3e8 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/PropagationConfig.tsx +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/PropagationConfig.tsx @@ -1,7 +1,8 @@ import Form from 'react-jsonschema-form-bs4'; -import React, {useState, useEffect} from 'react'; +import React, {useState} from 'react'; import {Nav} from 'react-bootstrap'; import _ from 'lodash'; +import CredentialsConfig from './CredentialsConfig'; const sectionOrder = [ 'exploitation', @@ -19,40 +20,24 @@ 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]); - useEffect(() => { - setLocalFormData(formData[selectedSection]); - setDisplayedSchema(getSchemaByKey(schema, selectedSection)); - setDisplayedSchemaUi(getUiSchemaByKey(uiSchema, selectedSection)); - setLocalFormData(formData[selectedSection]); - }, [selectedSection]) + const onFormDataChange = (formData) => { + let formDataClone = _.clone(formData.formData); + let configurationClone = _.clone(configuration); - useEffect(() => { - setLocalFormData(formData[selectedSection]); - }, [formData]) - - const onInnerDataChange = (innerData) => { - let innerDataClone = _.clone(innerData); - let formDataClone = _.clone(formData); - - formDataClone[selectedSection] = innerDataClone.formData; - onChange({formData: formDataClone}); - } - - const setSection = (sectionKey) => { - setSelectedSection(sectionKey); + configurationClone[selectedSection] = formDataClone; + onChange(configurationClone); } const renderNav = () => { - return () } + const getForm = () => { + let displayedSchema = getSchemaByKey(schema, selectedSection); + let displayedUiSchema = getUiSchemaByKey(uiSchema, selectedSection); + if (selectedSection === 'credentials') { + return + } else { + let selectedSectionData = configuration[selectedSection]; + return + } + } return (
{renderNav()} - - - + {getForm()}
) } @@ -91,8 +94,5 @@ function getNavTitle(schema, key) { if (key === 'maximum_depth') { return 'General'; } - if (key === 'credentials') { - return 'Credentials'; - } return schema['properties'][key].title; } diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/ReformatHook.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/ReformatHook.js index 322882194..b5f538347 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/ReformatHook.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/ReformatHook.js @@ -1,8 +1,101 @@ +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']}] + let formattedConfig = _.clone(config); + + if (reverse) { + if(formattedConfig['payloads'].length === 1){ + // Second click on Export + formattedConfig['payloads'] = [{'name': 'ransomware', 'options': formattedConfig['payloads'][0]['options']}]; } else { - config['payloads'] = config['payloads'][0]['options']; + formattedConfig['payloads'] = [{'name': 'ransomware', 'options': formattedConfig['payloads']}]; } - return config; + formattedConfig['keep_tunnel_open_time'] = formattedConfig['advanced']['keep_tunnel_open_time']; + } else { + formattedConfig['payloads'] = formattedConfig['payloads'][0]['options']; + formattedConfig['advanced'] = {}; + formattedConfig['advanced']['keep_tunnel_open_time'] = formattedConfig['keep_tunnel_open_time']; } + return formattedConfig; +} + +export function formatCredentialsForForm(credentials) { + let formattedCredentials = _.clone(defaultCredentials); + for (let i = 0; i < credentials.length; i++) { + let identity = credentials[i]['identity']; + if(identity !== null) { + formattedCredentials['exploit_user_list'].push(identity.username) + } + + let secret = credentials[i]['secret']; + if(secret !== null){ + 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) + } + } + } + + formattedCredentials['exploit_user_list'] = [...new Set(formattedCredentials['exploit_user_list'])]; + formattedCredentials['exploit_password_list'] = [...new Set(formattedCredentials['exploit_password_list'])]; + formattedCredentials['exploit_ntlm_hash_list'] = [...new Set(formattedCredentials['exploit_ntlm_hash_list'])]; + formattedCredentials['exploit_lm_hash_list'] = [...new Set(formattedCredentials['exploit_lm_hash_list'])]; + + return formattedCredentials; +} + +export function formatCredentialsForIsland(credentials) { + let formattedCredentials = []; + let usernames = credentials['exploit_user_list']; + for (let i = 0; i < usernames.length; i++) { + formattedCredentials.push({ + 'identity': {'username': usernames[i], 'credential_type': 'USERNAME'}, + 'secret': null + }) + } + + let passwords = credentials['exploit_password_list']; + for (let i = 0; i < passwords.length; i++) { + formattedCredentials.push({ + 'identity': null, + 'secret': {'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({ + 'identity': null, + 'secret': {'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({ + 'identity': null, + 'secret': {'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({ + 'identity': null, + 'secret': {'credential_type': 'SSH_KEYPAIR', 'private_key': ssh_keys[i]['private_key'], + 'public_key': ssh_keys[i]['public_key']} + }) + } + + return formattedCredentials; +} diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js index ac9b22ead..7fd4cebfd 100644 --- a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -3,6 +3,7 @@ import InfoBox from './InfoBox'; import TextBox from './TextBox.js'; import PbaInput from './PbaInput'; import {API_PBA_LINUX, API_PBA_WINDOWS} from '../pages/ConfigurePage'; +import SensitiveTextInput from '../ui-components/SensitiveTextInput'; export default function UiSchema(props) { const UiSchema = { @@ -25,7 +26,6 @@ export default function UiSchema(props) { options: { http_ports: { items: { - classNames: 'config-template-no-header' } } } @@ -38,6 +38,24 @@ export default function UiSchema(props) { private_key: { } } + }, + exploit_password_list: { + items: { + classNames: 'config-template-no-header', + 'ui:widget': SensitiveTextInput + } + }, + exploit_lm_hash_list:{ + items: { + classNames: 'config-template-no-header', + 'ui:widget': SensitiveTextInput + } + }, + exploit_ntlm_hash_list:{ + items: { + classNames: 'config-template-no-header', + 'ui:widget': SensitiveTextInput + } } }, network_scan: { diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js index c0423d8af..bb1d1a281 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js @@ -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(CONFIGURED_PROPAGATION_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,26 +162,23 @@ class ConfigurePageComponent extends AuthComponent { } configSubmit() { - return this.sendConfig() - .then(res => res.json()) - .then(() => { - this.setState({ - lastAction: configSaveAction - }); - this.setInitialConfig(this.state.configuration); - this.props.onStatusChange(); - }).catch(error => { - console.log('Bad configuration: ' + error.toString()); - this.setState({lastAction: 'invalid_configuration'}); - }); + this.sendCredentials().then(res => { + if(res.ok) { + this.sendConfig(); + } + }); } - onChange = ({formData}) => { + onChange = (formData) => { let configuration = this.state.configuration; configuration[this.state.selectedSection] = formData; this.setState({currentFormData: formData, configuration: configuration}); }; + onCredentialChange = (credentials) => { + this.setState({credentials: credentials}); + } + updateConfigSection = () => { let newConfig = this.state.configuration; @@ -176,6 +191,7 @@ class ConfigurePageComponent extends AuthComponent { renderConfigExportModal = () => { return ( { this.setState({showConfigExportModal: false}); }}/>); @@ -255,6 +271,7 @@ class ConfigurePageComponent extends AuthComponent { sendConfig() { let config = JSON.parse(JSON.stringify(this.state.configuration)) config = reformatConfig(config, true); + delete config['advanced']; return ( this.authFetch(CONFIG_URL, @@ -265,13 +282,36 @@ class ConfigurePageComponent extends AuthComponent { }) .then(res => { if (!res.ok) { - throw Error() + console.log(`bad configuration submited ${res.status}`); + this.setState({lastAction: 'invalid_configuration'}); + } else { + this.setState({ + lastAction: configSaveAction + }); + this.setInitialConfig(this.state.configuration); + this.props.onStatusChange(); } return res; - }).catch((error) => { - console.log(`bad configuration ${error}`); - this.setState({lastAction: 'invalid_configuration'}); - })); + })); + } + + sendCredentials() { + return ( + this.authFetch(CONFIGURED_PROPAGATION_CREDENTIALS_URL, + { + method: 'PUT', + 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'}); + })); } renderConfigContent = (displayedSchema) => { @@ -285,21 +325,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 () + delete Object.assign(formProperties, {'configuration': formProperties.formData}).formData; + return () } else { + formProperties['onChange'] = (formData) => { + this.onChange(formData.formData) + }; return (
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js b/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js index ea95a88d8..c1eb57198 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js @@ -8,7 +8,10 @@ export function getAllUsernames(stolen, configured){ export function getCredentialsUsernames(credentials) { let usernames = []; for(let i = 0; i < credentials.length; i++){ - usernames.push(credentials[i]['identity']['username']); + let username = credentials[i]['identity']; + if(username !== null) { + usernames.push(username['username']); + } } return usernames; } @@ -16,10 +19,16 @@ export function getCredentialsUsernames(credentials) { export function getAllSecrets(stolen, configured){ let secrets = []; for(let i = 0; i < stolen.length; i++){ - secrets.push(getSecretsFromCredential(stolen[i]['secret'])); + let secret = stolen[i]['secret']; + if(secret !== null){ + secrets.push(getSecretsFromCredential(secret)); + } } for(let i = 0; i < configured.length; i++){ - secrets.push(getSecretsFromCredential(configured[i]['secret'])); + let secret = configured[i]['secret']; + if(secret !== null){ + secrets.push(getSecretsFromCredential(secret)); + } } return secrets; } diff --git a/monkey/monkey_island/cc/ui/src/services/configuration/propagation/credentials.js b/monkey/monkey_island/cc/ui/src/services/configuration/propagation/credentials.js index f2ba5c222..8c9fe2405 100644 --- a/monkey/monkey_island/cc/ui/src/services/configuration/propagation/credentials.js +++ b/monkey/monkey_island/cc/ui/src/services/configuration/propagation/credentials.js @@ -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; diff --git a/monkey/monkey_island/cc/ui/src/services/configuration/propagation/exploitation.js b/monkey/monkey_island/cc/ui/src/services/configuration/propagation/exploitation.js index 2b24b91f3..495eb4ed6 100644 --- a/monkey/monkey_island/cc/ui/src/services/configuration/propagation/exploitation.js +++ b/monkey/monkey_island/cc/ui/src/services/configuration/propagation/exploitation.js @@ -15,9 +15,9 @@ const EXPLOITATION_CONFIGURATION_SCHEMA = { 'title': 'Vulnerability Exploiters', 'type': 'array', 'uniqueItems': true - } + }, + 'options': EXPLOITATION_OPTIONS_CONFIGURATION_SCHEMA }, - 'options': EXPLOITATION_OPTIONS_CONFIGURATION_SCHEMA, 'type': 'object' }; diff --git a/monkey/monkey_island/cc/ui/src/services/configuration/propagation/fingerprinterClasses.js b/monkey/monkey_island/cc/ui/src/services/configuration/propagation/fingerprinterClasses.js index 51cba1708..4d68abf02 100644 --- a/monkey/monkey_island/cc/ui/src/services/configuration/propagation/fingerprinterClasses.js +++ b/monkey/monkey_island/cc/ui/src/services/configuration/propagation/fingerprinterClasses.js @@ -4,11 +4,11 @@ const FINGERPRINTER_CLASSES = { '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':''} + 'smb' : {'name':'smb', 'options':{}}, + 'ssh' : {'name':'ssh', 'options':{}}, + 'http' : {'name':'http', 'options':{}}, + 'mssql' : {'name':'mssql', 'options':{}}, + 'elastic' : {'name':'elastic', 'options':{}} }, 'anyOf': [ {