diff --git a/monkey_island/cc/resources/edge.py b/monkey_island/cc/resources/edge.py index 8aee77638..2ef0c15bc 100644 --- a/monkey_island/cc/resources/edge.py +++ b/monkey_island/cc/resources/edge.py @@ -1,8 +1,6 @@ -from bson import ObjectId from flask import request import flask_restful -from cc.database import mongo from cc.services.edge import EdgeService __author__ = 'Barak' @@ -11,7 +9,6 @@ __author__ = 'Barak' class Edge(flask_restful.Resource): def get(self): edge_id = request.args.get('id') - if edge_id: return {"edge": EdgeService.get_displayed_edge_by_id(edge_id)} diff --git a/monkey_island/cc/resources/node.py b/monkey_island/cc/resources/node.py index d3c61f76f..5a6c52e1b 100644 --- a/monkey_island/cc/resources/node.py +++ b/monkey_island/cc/resources/node.py @@ -1,9 +1,6 @@ -from bson import ObjectId from flask import request import flask_restful -from cc.database import mongo -from cc.services.edge import EdgeService from cc.services.node import NodeService __author__ = 'Barak' diff --git a/monkey_island/cc/services/edge.py b/monkey_island/cc/services/edge.py index edadb1322..7f93205f4 100644 --- a/monkey_island/cc/services/edge.py +++ b/monkey_island/cc/services/edge.py @@ -169,11 +169,11 @@ class EdgeService: @staticmethod def get_edge_group(edge): - if edge["exploited"]: + if edge.get("exploited"): return "exploited" - if edge["tunnel"]: + if edge.get("tunnel"): return "tunnel" - if (len(edge["scans"]) > 0) or (len(edge["exploits"]) > 0): + if (len(edge.get("scans", [])) > 0) or (len(edge.get("exploits", [])) > 0): return "scan" return "empty" diff --git a/monkey_island/cc/services/node.py b/monkey_island/cc/services/node.py index ed626eb5c..ca2288974 100644 --- a/monkey_island/cc/services/node.py +++ b/monkey_island/cc/services/node.py @@ -101,10 +101,7 @@ class NodeService: @staticmethod def get_node_group(node): - if node["exploited"]: - return "exploited" - else: - return "clean" + return "exploited" if node.get("exploited") else "clean" @staticmethod def monkey_to_net_node(monkey): diff --git a/monkey_island/cc/ui/package.json b/monkey_island/cc/ui/package.json index a7375ab3a..73d84fa6b 100644 --- a/monkey_island/cc/ui/package.json +++ b/monkey_island/cc/ui/package.json @@ -70,8 +70,6 @@ "react-bootstrap": "^0.31.2", "react-copy-to-clipboard": "^5.0.0", "react-data-components": "^1.1.1", - "react-data-grid": "^2.0.58", - "react-data-grid-addons": "^2.0.58", "react-dom": "^15.6.1", "react-fa": "^4.2.0", "react-graph-vis": "^0.1.3", diff --git a/monkey_island/cc/ui/src/components/Main.js b/monkey_island/cc/ui/src/components/Main.js index 11c7f1115..598f0e1a6 100644 --- a/monkey_island/cc/ui/src/components/Main.js +++ b/monkey_island/cc/ui/src/components/Main.js @@ -14,6 +14,7 @@ require('react-data-components/css/table-twbs.css'); require('styles/App.css'); let logoImage = require('../images/monkey-logo.png'); +let guardicoreLogoImage = require('../images/guardicore-logo.png'); class AppComponent extends React.Component { render() { @@ -24,52 +25,56 @@ class AppComponent extends React.Component {
Infection Monkey - by GuardiCore

+ +
+
+ Powered by + + GuardiCore + +
+ diff --git a/monkey_island/cc/ui/src/components/pages/ConfigurePage.js b/monkey_island/cc/ui/src/components/pages/ConfigurePage.js index 94359d967..16b5ef4a3 100644 --- a/monkey_island/cc/ui/src/components/pages/ConfigurePage.js +++ b/monkey_island/cc/ui/src/components/pages/ConfigurePage.js @@ -1,6 +1,6 @@ import React from 'react'; import Form from 'react-jsonschema-form'; -import {Col} from 'react-bootstrap'; +import {Col, Nav, NavItem} from 'react-bootstrap'; class ConfigurePageComponent extends React.Component { constructor(props) { @@ -47,9 +47,9 @@ class ConfigurePageComponent extends React.Component { }); }; - setSelectedSection = (event) => { + setSelectedSection = (key) => { this.setState({ - selectedSection: event.target.value + selectedSection: key }); }; @@ -57,17 +57,14 @@ class ConfigurePageComponent extends React.Component { return (

Monkey Configuration

-
- - This configuration will only apply on new infections. -
- + { this.state.selectedSection ?
: ''} +
+ + This configuration will only apply to new infections. +
+ { this.state.saved ?

Configuration saved successfully.

: ''} diff --git a/monkey_island/cc/ui/src/components/pages/FullLogsPage.js b/monkey_island/cc/ui/src/components/pages/FullLogsPage.js index 837e12c1f..dbdc618ac 100644 --- a/monkey_island/cc/ui/src/components/pages/FullLogsPage.js +++ b/monkey_island/cc/ui/src/components/pages/FullLogsPage.js @@ -3,13 +3,13 @@ import {Col} from 'react-bootstrap'; import JSONTree from 'react-json-tree' import {DataTable} from 'react-data-components'; -const renderJson = (val, row) => ; -const renderTime = (val, row) => val.split('.')[0]; +const renderJson = (val) => ; +const renderTime = (val) => val.split('.')[0]; const columns = [ - { title: 'Type', prop: 'telem_type' }, - { title: 'Monkey ID', prop: 'monkey_guid' }, { title: 'Time', prop: 'timestamp', render: renderTime}, + { title: 'Monkey ID', prop: 'monkey_guid' }, + { title: 'Type', prop: 'telem_type' }, { title: 'More Info', prop: 'data', render: renderJson, width: '40%' } ]; diff --git a/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey_island/cc/ui/src/components/pages/MapPage.js index fb236d857..57d88eba7 100644 --- a/monkey_island/cc/ui/src/components/pages/MapPage.js +++ b/monkey_island/cc/ui/src/components/pages/MapPage.js @@ -1,8 +1,9 @@ import React from 'react'; import {Col} from 'react-bootstrap'; import Graph from 'react-graph-vis'; -import {Icon} from 'react-fa' - +import PreviewPane from 'components/preview-pane/PreviewPane'; +import {Link} from 'react-router-dom'; +import {Icon} from 'react-fa'; let options = { layout: { @@ -44,7 +45,9 @@ class MapPageComponent extends React.Component { constructor(props) { super(props); this.state = { - graph: {nodes: [], edges: []} + graph: {nodes: [], edges: []}, + selected: null, + selectedType: null }; } @@ -60,13 +63,19 @@ class MapPageComponent extends React.Component { selectionChanged(event) { if (event.nodes.length === 1) { - console.log('selected node:', event.nodes[0]); + console.log('selected node:', event.nodes[0]); // eslint-disable-line no-console + fetch('/api/netmap/node?id='+event.nodes[0]) + .then(res => res.json()) + .then(res => this.setState({selected: res, selectedType: 'node'})); } else if (event.edges.length === 1) { - console.log('selected edge:', event.edges[0]); + fetch('/api/netmap/edge?id='+event.edges[0]) + .then(res => res.json()) + .then(res => this.setState({selected: res.edge, selectedType: 'edge'})); } else { - console.log('no preview.'); + console.log('selection cleared.'); // eslint-disable-line no-console + this.setState({selected: null, selectedType: null}); } } @@ -77,44 +86,21 @@ class MapPageComponent extends React.Component {

Infection Map

-
- -
-
-
-

- - vm4 - Infected Asset -

-
+ -
-

Machine Info

-

...

+ -

Exploit Method

-

...

- -

Timeline

-
    -
  • -
    - failed attempt1 -
  • -
  • -
    - failed attempt2 -
  • -
  • -
    - Infection! -
  • -
-
+
+ Monkey Telemetry +
diff --git a/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js b/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js index 46bbdc115..cc708afeb 100644 --- a/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js +++ b/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js @@ -2,6 +2,7 @@ import React from 'react'; import {Button, Col, Well} from 'react-bootstrap'; import CopyToClipboard from 'react-copy-to-clipboard'; import {Icon} from 'react-fa'; +import {Link} from "react-router-dom"; class RunMonkeyPageComponent extends React.Component { constructor(props) { @@ -9,7 +10,8 @@ class RunMonkeyPageComponent extends React.Component { this.state = { ips: [], selectedIp: '0.0.0.0', - isRunning: true + isRunningOnIsland: false, + isRunningLocally: false }; } @@ -17,14 +19,13 @@ class RunMonkeyPageComponent extends React.Component { fetch('/api') .then(res => res.json()) .then(res => this.setState({ - ips: res['ip_addresses'], - selectedIp: res['ip_addresses'][0] + ips: res['ip_addresses'] })); fetch('/api/local-monkey') .then(res => res.json()) .then(res => this.setState({ - isRunning: res['is_running'] + isRunningOnIsland: res['is_running'] })); } @@ -37,50 +38,64 @@ class RunMonkeyPageComponent extends React.Component { { method: 'POST', headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({action: 'run', ip: this.state.selectedIp}) + body: JSON.stringify({action: 'run'}) }) .then(res => res.json()) .then(res => { this.setState({ - isRunning: res['is_running'] + isRunningOnIsland: res['is_running'] }); }); }; - setSelectedIp = (event) => { - this.setState({selectedIp: event.target.value}); - }; - render() { return (

Run the Monkey

-

- Select one of the server's IP addresses: - -
That address will be used as the monkey's C&C address. +

+ You can run the monkey on the C&C server, on your local machine and basically everywhere. + The more the merrier 😄

-

Run this snippet on a host for manually infecting it with a Monkey:

- - - - - {this.generateCmd(this.state.selectedIp)} - -

- Or simply click here to Run on {this.state.selectedIp} - { this.state.isRunning ? - Running... - : ''} +

+ + + Download and run locally + { !this.state.isRunningLocally ? + + : ''} + +

+
+

+ Run one of those snippets on a host for infecting it with a Monkey: +
+ (The IP address is used as the monkey's C&C address) +

+ + {this.state.ips.map(ip => +
+ + + + {this.generateCmd(ip)} +
+ )} +
+
+

+ Go ahead and monitor the ongoing infection in the Infection Map view.

); diff --git a/monkey_island/cc/ui/src/components/pages/RunServerPage.js b/monkey_island/cc/ui/src/components/pages/RunServerPage.js index c94f78b9c..1c8f6580e 100644 --- a/monkey_island/cc/ui/src/components/pages/RunServerPage.js +++ b/monkey_island/cc/ui/src/components/pages/RunServerPage.js @@ -21,7 +21,10 @@ class RunServerPageComponent extends React.Component {

Your Monkey Island server is up and running on {this.state.ip} 👏 👏

- Now configure the monkey (or just stick with the default configuration) and run the monkey. + Go ahead and run the monkey. +

+

+ (You can make further adjustments by configuring the monkey)

diff --git a/monkey_island/cc/ui/src/components/preview-pane/PreviewPane.js b/monkey_island/cc/ui/src/components/preview-pane/PreviewPane.js new file mode 100644 index 000000000..2f49f6257 --- /dev/null +++ b/monkey_island/cc/ui/src/components/preview-pane/PreviewPane.js @@ -0,0 +1,100 @@ +import React from 'react'; +import {Icon} from "react-fa"; + +class PreviewPaneComponent extends React.Component { + assetInfo(asset) { + return ( +
+ + + + + + + + + + + + + + + + + + + +
Operating System{asset.os}
IP Addresses{asset.ip_addresses.map(val =>
{val}
)}
Services{asset.services.map(val =>
{val}
)}
Accessible From{asset.accessible_from_nodes.map(val =>
{val.id}
)}
+
+ ); + } + + infectedAssetInfo(asset) { + return ( +
+ {this.assetInfo(asset)} + +

Timeline

+
    + { asset.exploits.map(exploit => +
  • +
    +
    {exploit.start_timestamp}
    +
    {exploit.origin}
    +
    {exploit.exploiter}
    +
  • + )} +
+
+ ); + } + + infectionInfo(edge) { + return ( +
+ +
+ ); + } + + scanInfo(edge) { + return ( +
+ +
+ ); + } + + render() { + let info = null; + switch (this.props.type) { + case 'edge': + info = this.props.item.exploits.length ? + this.infectionInfo(this.props.item) : this.scanInfo(this.props.item); + case 'node': + info = this.props.item.exploits.some(exploit => exploit.result) ? + this.infectedAssetInfo(this.props.item) : this.assetInfo(this.props.item); + } + return ( +
+ { !this.props.item ? + + + Select an item on the map for a preview + + : +
+

+ {this.props.item.label} +

+ +
+ {info} +
+ } +
+ ); + } +} + +export default PreviewPaneComponent; diff --git a/monkey_island/cc/ui/src/images/guardicore-logo.png b/monkey_island/cc/ui/src/images/guardicore-logo.png new file mode 100644 index 000000000..d5f02d006 Binary files /dev/null and b/monkey_island/cc/ui/src/images/guardicore-logo.png differ diff --git a/monkey_island/cc/ui/src/styles/App.css b/monkey_island/cc/ui/src/styles/App.css index 01dc1a8b0..72b3dbf76 100644 --- a/monkey_island/cc/ui/src/styles/App.css +++ b/monkey_island/cc/ui/src/styles/App.css @@ -91,6 +91,22 @@ body { li .checkmark { font-size: 1.3em; + margin-right: -15px; + } + + hr { + border-top-color: #ccc !important; + } + + .guardicore-link span { + color: #999; + vertical-align: middle; + } + + .guardicore-link img { + height: 24px; + margin-left: 8px; + vertical-align: middle; } } @@ -162,6 +178,10 @@ body { margin-left: 1em; } +.run-monkey-snippets .well { + margin-bottom: 0; +} + /* * Map Preview Pane */ @@ -172,10 +192,16 @@ body { border-bottom: 3px solid #e8e8e8; padding: 1.5em 1em; border-radius: 8px; + margin-bottom: 2em; +} + +.preview-pane hr { + margin: 10px 0; } .preview-pane h3 { margin: 0; + font-size: 20px; } .preview-pane h3 small { @@ -191,6 +217,15 @@ body { font-size: 1em; margin-top: 0; } + +.preview-pane .table tr:first-child th , .preview-pane .table tr:first-child td { + border-top: 0; +} + +.preview-pane .table th { + text-align: left; +} + .preview-pane p, .preview-pane .timeline { margin-left: 1em; } @@ -220,6 +255,7 @@ body { .timeline .bullet { width: 16px; + height: 16px; background: #ccc; position: absolute; right: 100%; @@ -247,8 +283,16 @@ body { padding: 15px 8px; } -#search-field , #page-menu { +.data-table-container > .container > .row:first-child > div:first-child > div { + display: inline-block; +} + +.data-table-container > .container > .row:first-child > div:first-child > div:last-child { margin-left: 1em; +} + +#search-field , #page-menu { + margin-left: 0.5em; margin-bottom: 1em; height: 34px; padding: 6px 12px; @@ -265,3 +309,11 @@ body { -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } + +#search-field { + width: 100px; +} + +.data-table-container .pagination { + margin: 0 !important; +}