diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
index 17dcc9be7..48a11f008 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js
@@ -1,16 +1,22 @@
import React from 'react';
import {css} from '@emotion/core';
-import {Button, Col, Well, Nav, NavItem, Collapse} from 'react-bootstrap';
+import {Button, Col, Card, Nav, Collapse, Row} from 'react-bootstrap';
import CopyToClipboard from 'react-copy-to-clipboard';
import GridLoader from 'react-spinners/GridLoader';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faClipboard, faCheck, faSync } from '@fortawesome/free-solid-svg-icons';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import {faClipboard} from '@fortawesome/free-solid-svg-icons/faClipboard';
+import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
+import {faSync} from '@fortawesome/free-solid-svg-icons/faSync';
+import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
+import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
import {Link} from 'react-router-dom';
import AuthComponent from '../AuthComponent';
import AwsRunTable from '../run-monkey/AwsRunTable';
+import MissingBinariesModal from '../ui-components/MissingBinariesModal';
+
const loading_css_override = css`
display: block;
margin-right: auto;
@@ -36,8 +42,12 @@ class RunMonkeyPageComponent extends AuthComponent {
awsMachines: [],
isLoadingAws: true,
isErrorWhileCollectingAwsMachines: false,
- awsMachineCollectionErrorMsg: ''
+ awsMachineCollectionErrorMsg: '',
+ showModal: false,
+ errorDetails: ''
};
+
+ this.closeModal = this.closeModal.bind(this);
}
componentDidMount() {
@@ -126,6 +136,14 @@ class RunMonkeyPageComponent extends AuthComponent {
runningOnIslandState: 'installing'
});
} else {
+ /* If Monkey binaries are missing, change the state accordingly */
+ if (res['error_text'].startsWith('Copy file failed')) {
+ this.setState({
+ showModal: true,
+ errorDetails: res['error_text']
+ }
+ );
+ }
this.setState({
runningOnIslandState: 'not_running'
});
@@ -145,7 +163,7 @@ class RunMonkeyPageComponent extends AuthComponent {
cmdText = RunMonkeyPageComponent.generateWindowsCmd(this.state.selectedIp, is32Bit);
}
return (
-
+
{cmdText}
-
+
)
}
@@ -249,17 +267,17 @@ class RunMonkeyPageComponent extends AuthComponent {
-
+
Not sure what this is? Not seeing your AWS EC2 instances? Read the documentation!
+ rel="noopener noreferrer" target="_blank">Read the documentation!
{
this.state.ips.length > 1 ?
-
:
}
@@ -269,34 +287,48 @@ class RunMonkeyPageComponent extends AuthComponent {
ref={r => (this.awsTable = r)}
/>
-
+ {this.state.awsClicked ?
+ : null}
+
)
}
+ closeModal = () => {
+ this.setState({
+ showModal: false
+ })
+ };
+
render() {
return (
-
-
2. Run the Monkey
+
+
1. Run Monkey
Go ahead and run the monkey!
(Or configure the monkey to fine tune its behavior)
-
-
+
{
// TODO: implement button functionality
/*
@@ -313,29 +345,67 @@ class RunMonkeyPageComponent extends AuthComponent {
OR
-
-
+
+
Run on a machine of your choice
-
+
- Choose the operating system where you want to run the monkey
- {this.state.ips.length > 1 ? ', and the interface to communicate with.' : '.'}
+ Choose the operating system where you want to run the monkey:
-
+
+
+
+
+
+
{this.state.ips.length > 1 ?
-
+
+
+
+
+ Choose the interface to communicate with:
+
+
+
+
+
+
+
+
+
:
}
@@ -346,7 +416,7 @@ class RunMonkeyPageComponent extends AuthComponent {
{
this.state.isLoadingAws ?
-
+
: null
}
{
@@ -369,11 +439,13 @@ class RunMonkeyPageComponent extends AuthComponent {
}
{
this.state.isOnAws ?
-
-
+
+
Run on AWS machine of your choice
-
+
:
null
@@ -382,8 +454,8 @@ class RunMonkeyPageComponent extends AuthComponent {
{
this.state.isErrorWhileCollectingAwsMachines ?
-
-
+
+
Error while collecting AWS machine data. Error
message: {this.state.awsMachineCollectionErrorMsg}
Are you sure you've set the correct role on your Island AWS machine?
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunServerPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RunServerPage.js
index 1ad890845..b70d0b9f7 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/RunServerPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunServerPage.js
@@ -1,6 +1,10 @@
import React from 'react';
-import {Col} from 'react-bootstrap';
+import {Col, Row} from 'react-bootstrap';
import {Link} from 'react-router-dom';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import {faPlayCircle} from '@fortawesome/free-regular-svg-icons';
+import {faBookOpen, faCogs} from '@fortawesome/free-solid-svg-icons';
+import '../../styles/pages/RunServerPage.scss';
class RunServerPageComponent extends React.Component {
constructor(props) {
@@ -9,24 +13,18 @@ class RunServerPageComponent extends React.Component {
render() {
return (
-
-
1. Monkey Island Server
+
+
Welcome to the Monkey Island Server
-
Congrats! You have successfully set up the Monkey Island
- server. 👏 👏
-
- The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter
- breaches and internal server infections.
- The Monkey uses various methods to propagate across a data
- center and reports to this Monkey Island Command and Control server.
-
-
- To read more about the Monkey, visit infectionmonkey.com
-
-
- Go ahead and run the monkey.
+
+ Congratulations! You have successfully set up the Monkey Island server. 👏 👏
+
+
+
+
);
@@ -34,3 +32,43 @@ class RunServerPageComponent extends React.Component {
}
export default RunServerPageComponent;
+
+function HomepageCallToActions() {
+ return (
+
+
+
+
+
+
Run Monkey
+
Run the Monkey with the current configuration.
+
+
+
+
+
Configure Monkey
+
Edit targets, add credentials, choose exploits and more.
+
+
+
+
+
+
+ );
+}
+
+function MonkeyInfo() {
+ return (
+ <>
+
What is Infection Monkey?
+
Infection Monkey is an open-source security tool for testing a data center's resiliency to perimeter
+ breaches and internal server infections. The Monkey uses various methods to propagate across a data center
+ and reports to this Monkey Island Command and Control server.
+ >
+ );
+}
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js b/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js
index a57c81ca5..c536146bf 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js
@@ -1,9 +1,12 @@
import React from 'react';
-import {Col} from 'react-bootstrap';
+import {Col, Button} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import AuthComponent from '../AuthComponent';
import StartOverModal from '../ui-components/StartOverModal';
-import '../../styles/StartOverPage.scss';
+import '../../styles/pages/StartOverPage.scss';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
+import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
class StartOverPageComponent extends AuthComponent {
constructor(props) {
@@ -32,7 +35,9 @@ class StartOverPageComponent extends AuthComponent {
render() {
return (
-
+
-
-
+ {
this.setState({showCleanDialog: true});
this.updateMonkeysRunning();
}
}>
Reset the Environment
-
+
-
+
You don't have to reset the environment to keep running monkeys.
You can continue and Run More Monkeys as you wish,
and see the results on the Infection Map without deleting anything.
{this.state.cleaned ?
-
+
Environment was reset successfully
: ''}
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js
index d259d1cba..faa9ad138 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js
@@ -1,9 +1,13 @@
import React from 'react';
import {Button, Col} from 'react-bootstrap';
-import JSONTree from 'react-json-tree'
+import JSONTree from 'react-json-tree';
import {DataTable} from 'react-data-components';
import AuthComponent from '../AuthComponent';
-import download from 'downloadjs'
+import download from 'downloadjs';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+
+import '../../styles/pages/TelemetryPage.scss';
+import {faDownload} from '@fortawesome/free-solid-svg-icons/faDownload';
const renderJson = (val) => ;
const renderTime = (val) => val.split('.')[0];
@@ -41,10 +45,11 @@ class TelemetryPageComponent extends AuthComponent {
render() {
return (
-
+
-
Monkey Island Logs
Download Monkey Island internal log file
{
this.downloadIslandLog();
}}>
- Download
+
Download
-
-
+
);
}
}
diff --git a/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js b/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js
index c175becd3..3199385ba 100644
--- a/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js
+++ b/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js
@@ -3,10 +3,18 @@ import Graph from 'react-graph-vis';
import Dimensions from 'react-dimensions'
class GraphWrapper extends React.Component {
+
+ constructor(props) {
+ super(props);
+ }
+
render() {
- let newOptions = this.props.options;
- newOptions.height = this.props.containerHeight.toString() + 'px';
- newOptions.width = this.props.containerWidth.toString() + 'px';
+ let newOptions = null;
+ if(this.props.options !== undefined){
+ newOptions = this.props.options;
+ newOptions.height = this.props.containerHeight.toString() + 'px';
+ newOptions.width = this.props.containerWidth.toString() + 'px';
+ }
return ()
}
}
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
index 39886fee6..97f3c1a18 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js
@@ -1,9 +1,13 @@
import React from 'react';
import {Col, Button} from 'react-bootstrap';
-import '../../styles/Collapse.scss';
-import '../../styles/report/AttackReport.scss';
+import '../../styles/components/Collapse.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import {faCircle, faEye, faEyeSlash, faRadiation} from '@fortawesome/free-solid-svg-icons';
+import {faCircle} from '@fortawesome/free-solid-svg-icons/faCircle';
+import {faRadiation} from '@fortawesome/free-solid-svg-icons/faRadiation';
+import {faEye} from '@fortawesome/free-solid-svg-icons/faEye';
+import {faEyeSlash} from '@fortawesome/free-solid-svg-icons/faEyeSlash';
+import {faToggleOff} from '@fortawesome/free-solid-svg-icons/faToggleOff';
+import marked from 'marked';
import ReportHeader, {ReportTypes} from './common/ReportHeader';
import {ScanStatus} from '../attack/techniques/Helpers';
@@ -35,18 +39,18 @@ class AttackReport extends React.Component {
};
if (typeof this.props.report.schema !== 'undefined' && typeof this.props.report.techniques !== 'undefined'){
this.state['schema'] = this.props.report['schema'];
- this.state['techniques'] = AttackReport.addLinksToTechniques(this.props.report['schema'], this.props.report['techniques']);
+ this.state['techniques'] = AttackReport.modifyTechniqueData(this.props.report['schema'], this.props.report['techniques']);
}
}
componentDidUpdate(prevProps) {
if (this.props.report !== prevProps.report) {
this.setState({schema: this.props.report['schema'],
- techniques: AttackReport.addLinksToTechniques(this.props.report['schema'], this.props.report['techniques'])})
+ techniques: AttackReport.modifyTechniqueData(this.props.report['schema'], this.props.report['techniques'])})
}
}
- onTechniqueSelect = (technique, value) => {
+ onTechniqueSelect = (technique, _) => {
let selectedTechnique = this.getTechniqueByTitle(technique);
if (selectedTechnique === false){
return;
@@ -57,9 +61,11 @@ class AttackReport extends React.Component {
static getComponentClass(tech_id, techniques) {
switch (techniques[tech_id].status) {
case ScanStatus.SCANNED:
- return 'collapse-info';
+ return 'collapse-warning';
case ScanStatus.USED:
return 'collapse-danger';
+ case ScanStatus.DISABLED:
+ return 'collapse-disabled';
default:
return 'collapse-default';
}
@@ -71,24 +77,30 @@ class AttackReport extends React.Component {
return ;
case ScanStatus.USED:
return ;
+ case ScanStatus.DISABLED:
+ return ;
default:
return ;
- }
+ }
}
renderLegend() {
return ()
}
@@ -98,9 +110,9 @@ class AttackReport extends React.Component {
This report shows information about
-
Mitre ATT&CKâą
@@ -132,7 +144,8 @@ class AttackReport extends React.Component {
return false;
}
- static addLinksToTechniques(schema, techniques){
+ static modifyTechniqueData(schema, techniques){
+ // add links to techniques
schema = schema.properties;
for(const type in schema){
if (! schema.hasOwnProperty(type)) {return false;}
@@ -144,6 +157,11 @@ class AttackReport extends React.Component {
}
}
}
+ // modify techniques' messages
+ for (const tech_id in techniques){
+ techniques[tech_id]['message'] = ;
+ }
+
return techniques
}
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js
index b44f367e1..067069fab 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js
@@ -3,12 +3,11 @@ import BreachedServers from 'components/report-components/security/BreachedServe
import ScannedServers from 'components/report-components/security/ScannedServers';
import PostBreach from 'components/report-components/security/PostBreach';
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
-import {edgeGroupToColor, options} from 'components/map/MapOptions';
+import {edgeGroupToColor, getOptions} from 'components/map/MapOptions';
import StolenPasswords from 'components/report-components/security/StolenPasswords';
import CollapsibleWellComponent from 'components/report-components/security/CollapsibleWell';
import {Line} from 'rc-progress';
import AuthComponent from '../AuthComponent';
-import PassTheHashMapPageComponent from '../pages/PassTheHashMapPage';
import StrongUsers from 'components/report-components/security/StrongUsers';
import ReportHeader, {ReportTypes} from './common/ReportHeader';
import ReportLoader from './common/ReportLoader';
@@ -16,9 +15,10 @@ import SecurityIssuesGlance from './common/SecurityIssuesGlance';
import PrintReportButton from './common/PrintReportButton';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faMinus } from '@fortawesome/free-solid-svg-icons';
-
-let guardicoreLogoImage = require('../../images/guardicore-logo.png');
+import { faMinus } from '@fortawesome/free-solid-svg-icons/faMinus';
+import guardicoreLogoImage from '../../images/guardicore-logo.png'
+import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons';
+import '../../styles/App.css';
class ReportPageComponent extends AuthComponent {
@@ -54,14 +54,24 @@ class ReportPageComponent extends AuthComponent {
super(props);
this.state = {
report: props.report,
- graph: {nodes: [], edges: []}
+ graph: {nodes: [], edges: []},
+ nodeStateList: []
};
}
componentDidMount() {
+ this.getNodeStateListFromServer();
this.updateMapFromServer();
}
+ getNodeStateListFromServer = () => {
+ this.authFetch('/api/netmap/nodeStates')
+ .then(res => res.json())
+ .then(res => {
+ this.setState({nodeStateList: res.node_states});
+ });
+ };
+
componentWillUnmount() {
clearInterval(this.interval);
}
@@ -137,7 +147,7 @@ class ReportPageComponent extends AuthComponent {
''
:
-
+
To improve the monkey's detection rates, try adding users and passwords and enable the "Local
network
scan" config value under Basic - Network.
@@ -145,31 +155,35 @@ class ReportPageComponent extends AuthComponent {
}
The first monkey run was started on {this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished
+ className="badge badge-info">{this.state.report.overview.monkey_start_time}. After {this.state.report.overview.monkey_duration}, all monkeys finished
propagation attempts.
The monkey started propagating from the following machines where it was manually installed:
-
- {this.state.report.overview.manual_monkeys.map(x => - {x}
)}
-
+
+ {this.state.report.overview.manual_monkeys.map(x => - {x}
)}
+
The monkeys were run with the following configuration:
{
this.state.report.overview.config_users.length > 0 ?
-
- Usernames used for brute-forcing:
+ <>
+
+ Usernames used for brute-forcing:
+
- {this.state.report.overview.config_users.map(x => - {x}
)}
+ {this.state.report.overview.config_users.map(x => - {x}
)}
- Passwords used for brute-forcing:
+
+ Passwords used for brute-forcing:
+
- {this.state.report.overview.config_passwords.map(x => - {x.substr(0, 3) + '******'}
)}
+ {this.state.report.overview.config_passwords.map(x => - {x.substr(0, 3) + '******'}
)}
-
+ >
:
Brute forcing uses stolen credentials only. No credentials were supplied during Monkeyâs
@@ -185,7 +199,7 @@ class ReportPageComponent extends AuthComponent {
The Monkey uses the following exploit methods:
- {this.state.report.overview.config_exploits.map(x => - {x}
)}
+ {this.state.report.overview.config_exploits.map(x => - {x}
)}
)
@@ -199,7 +213,7 @@ class ReportPageComponent extends AuthComponent {
The Monkey scans the following IPs:
- {this.state.report.overview.config_ips.map(x => - {x}
)}
+ {this.state.report.overview.config_ips.map(x => - {x}
)}
:
@@ -233,7 +247,7 @@ class ReportPageComponent extends AuthComponent {
}).length > 0 ?
During this simulated attack the Monkey uncovered
+ className="badge badge-warning">
{this.state.report.overview.issues.filter(function (x) {
return x === true;
}).length} threats:
@@ -287,7 +301,7 @@ class ReportPageComponent extends AuthComponent {
:
During this simulated attack the Monkey uncovered 0 threats.
+ className="badge badge-success">0 threats.
}
@@ -303,15 +317,15 @@ class ReportPageComponent extends AuthComponent {
The Monkey uncovered the following possible set of issues:
{this.state.report.overview.warnings[this.Warning.CROSS_SEGMENT] ?
- - Weak segmentation - Machines from different segments are able to
+
- Weak segmentation - Machines from different segments are able to
communicate.
: null}
{this.state.report.overview.warnings[this.Warning.TUNNEL] ?
- - Weak segmentation - Machines were able to communicate over unused ports.
: null}
+ - Weak segmentation - Machines were able to communicate over unused ports.
: null}
{this.state.report.overview.warnings[this.Warning.SHARED_LOCAL_ADMIN] ?
- - Shared local administrator account - Different machines have the same account as a local
+
- Shared local administrator account - Different machines have the same account as a local
administrator.
: null}
{this.state.report.overview.warnings[this.Warning.SHARED_PASSWORDS] ?
- - Multiple users have the same password
: null}
+ - Multiple users have the same password
: null}
:
@@ -371,9 +385,9 @@ class ReportPageComponent extends AuthComponent {
The Monkey discovered {this.state.report.glance.scanned.length} machines and
+ className="badge badge-warning">{this.state.report.glance.scanned.length} machines and
successfully breached {this.state.report.glance.exploited.length} of them.
+ className="badge badge-danger">{this.state.report.glance.exploited.length} of them.
Island Communication
-
-
-
-
-
-
+
-
- {this.generateReportPthMap()}
+
+
+
+
+
+
@@ -420,29 +435,6 @@ class ReportPageComponent extends AuthComponent {
);
}
- generateReportPthMap() {
- return (
-
-
- Credentials Map
-
-
- This map visualizes possible attack paths through the network using credential compromise. Paths represent lateral
- movement opportunities by attackers.
-
-
- Legend:
- Access credentials |
-
-
-
-
- );
- }
-
generateReportFooter() {
return (