diff --git a/monkey_island/cc/ui/src/components/map/MapOptions.js b/monkey_island/cc/ui/src/components/map/MapOptions.js
index 701adcf29..f6946ea31 100644
--- a/monkey_island/cc/ui/src/components/map/MapOptions.js
+++ b/monkey_island/cc/ui/src/components/map/MapOptions.js
@@ -1,4 +1,4 @@
-let groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island',
+const groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island',
'island_monkey_linux', 'island_monkey_linux_running', 'island_monkey_windows', 'island_monkey_windows_running',
'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
@@ -16,7 +16,22 @@ let getGroupsOptions = () => {
return groupOptions;
};
-export const options = {
+const groupNamesPth = ['normal', 'critical'];
+
+let getGroupsOptionsPth = () => {
+ let groupOptions = {};
+ for (let groupName of groupNamesPth) {
+ groupOptions[groupName] =
+ {
+ shape: 'image',
+ size: 50,
+ image: require('../../images/nodes/pth/' + groupName + '.png')
+ };
+ }
+ return groupOptions;
+};
+
+export const basic_options = {
autoResize: true,
layout: {
improvedLayout: false
@@ -33,10 +48,22 @@ export const options = {
avoidOverlap: 0.5
},
minVelocity: 0.75
- },
- groups: getGroupsOptions()
+ }
};
+export const options = (() => {
+ let opts = JSON.parse(JSON.stringify(basic_options)); /* Deep copy */
+ opts.groups = getGroupsOptions();
+ return opts;
+})();
+
+export const optionsPth = (() => {
+ let opts = JSON.parse(JSON.stringify(basic_options)); /* Deep copy */
+ opts.groups = getGroupsOptionsPth();
+ opts.physics.barnesHut.gravitationalConstant = -20000;
+ return opts;
+})();
+
export function edgeGroupToColor(group) {
switch (group) {
case 'exploited':
diff --git a/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js b/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js
new file mode 100644
index 000000000..e06043c20
--- /dev/null
+++ b/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js
@@ -0,0 +1,247 @@
+import React from 'react';
+import {Icon} from 'react-fa';
+import Toggle from 'react-toggle';
+import {OverlayTrigger, Tooltip} from 'react-bootstrap';
+import download from 'downloadjs'
+import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane';
+
+class InfMapPreviewPaneComponent extends PreviewPaneComponent {
+
+ osRow(asset) {
+ return (
+
+ Operating System |
+ {asset.os.charAt(0).toUpperCase() + asset.os.slice(1)} |
+
+ );
+ }
+
+ ipsRow(asset) {
+ return (
+
+ IP Addresses |
+ {asset.ip_addresses.map(val => {val} )} |
+
+ );
+ }
+
+ servicesRow(asset) {
+ return (
+
+ Services |
+ {asset.services.map(val => {val} )} |
+
+ );
+ }
+
+ accessibleRow(asset) {
+ return (
+
+
+ Accessible From
+ {this.generateToolTip('List of machine which can access this one using a network protocol')}
+ |
+ {asset.accessible_from_nodes.map(val => {val} )} |
+
+ );
+ }
+
+ statusRow(asset) {
+ return (
+
+ Status |
+ {(asset.dead) ? 'Dead' : 'Alive'} |
+
+ );
+ }
+
+ forceKill(event, asset) {
+ let newConfig = asset.config;
+ newConfig['alive'] = !event.target.checked;
+ this.authFetch('/api/monkey/' + asset.guid,
+ {
+ method: 'PATCH',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({config: newConfig})
+ });
+ }
+
+ forceKillRow(asset) {
+ return (
+
+
+ Force Kill
+ {this.generateToolTip('If this is on, monkey will die next time it communicates')}
+ |
+
+ this.forceKill(e, asset)}/>
+
+ |
+
+ );
+ }
+
+ unescapeLog(st) {
+ return st.substr(1, st.length - 2) // remove quotation marks on beginning and end of string.
+ .replace(/\\n/g, "\n")
+ .replace(/\\r/g, "\r")
+ .replace(/\\t/g, "\t")
+ .replace(/\\b/g, "\b")
+ .replace(/\\f/g, "\f")
+ .replace(/\\"/g, '\"')
+ .replace(/\\'/g, "\'")
+ .replace(/\\&/g, "\&");
+ }
+
+ downloadLog(asset) {
+ this.authFetch('/api/log?id=' + asset.id)
+ .then(res => res.json())
+ .then(res => {
+ let timestamp = res['timestamp'];
+ timestamp = timestamp.substr(0, timestamp.indexOf('.'));
+ let filename = res['monkey_label'].split(':').join('-') + ' - ' + timestamp + '.log';
+ let logContent = this.unescapeLog(res['log']);
+ download(logContent, filename, 'text/plain');
+ });
+
+ }
+
+ downloadLogRow(asset) {
+ return (
+
+
+ Download Log
+ |
+
+ this.downloadLog(asset)}>Download
+ |
+
+ );
+ }
+
+ exploitsTimeline(asset) {
+ if (asset.exploits.length === 0) {
+ return ();
+ }
+
+ return (
+
+
+ Exploit Timeline
+ {this.generateToolTip('Timeline of exploit attempts. Red is successful. Gray is unsuccessful')}
+
+
+
+ )
+ }
+
+ assetInfo(asset) {
+ return (
+
+
+
+ {this.osRow(asset)}
+ {this.ipsRow(asset)}
+ {this.servicesRow(asset)}
+ {this.accessibleRow(asset)}
+
+
+ {this.exploitsTimeline(asset)}
+
+ );
+ }
+
+ infectedAssetInfo(asset) {
+ return (
+
+
+
+ {this.osRow(asset)}
+ {this.statusRow(asset)}
+ {this.ipsRow(asset)}
+ {this.servicesRow(asset)}
+ {this.accessibleRow(asset)}
+ {this.forceKillRow(asset)}
+ {this.downloadLogRow(asset)}
+
+
+ {this.exploitsTimeline(asset)}
+
+ );
+ }
+
+ scanInfo(edge) {
+ return (
+
+
+
+
+ Operating System |
+ {edge.os.type} |
+
+
+ IP Address |
+ {edge.ip_address} |
+
+
+ Services |
+ {edge.services.map(val => {val} )} |
+
+
+
+ {
+ (edge.exploits.length === 0) ?
+ '' :
+
+ }
+
+ );
+ }
+
+ islandEdgeInfo() {
+ return (
+
+
+ );
+ }
+
+ getInfoByProps() {
+ switch (this.props.type) {
+ case 'edge':
+ return this.scanInfo(this.props.item);
+ case 'node':
+ return this.props.item.group.includes('monkey', 'manual') ?
+ this.infectedAssetInfo(this.props.item) : this.assetInfo(this.props.item);
+ case 'island_edge':
+ return this.islandEdgeInfo();
+ }
+
+ return null;
+ }
+}
+
+export default InfMapPreviewPaneComponent;
diff --git a/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js b/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js
index 64b228332..c38907eea 100644
--- a/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js
+++ b/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js
@@ -15,251 +15,25 @@ class PreviewPaneComponent extends AuthComponent {
);
}
- osRow(asset) {
- return (
-
- Operating System |
- {asset.os.charAt(0).toUpperCase() + asset.os.slice(1)} |
-
- );
+ // This should be overridden
+ getInfoByProps() {
+ return null;
}
- ipsRow(asset) {
- return (
-
- IP Addresses |
- {asset.ip_addresses.map(val => {val} )} |
-
- );
- }
-
- servicesRow(asset) {
- return (
-
- Services |
- {asset.services.map(val => {val} )} |
-
- );
- }
-
- accessibleRow(asset) {
- return (
-
-
- Accessible From
- {this.generateToolTip('List of machine which can access this one using a network protocol')}
- |
- {asset.accessible_from_nodes.map(val => {val} )} |
-
- );
- }
-
- statusRow(asset) {
- return (
-
- Status |
- {(asset.dead) ? 'Dead' : 'Alive'} |
-
- );
- }
-
- forceKill(event, asset) {
- let newConfig = asset.config;
- newConfig['alive'] = !event.target.checked;
- this.authFetch('/api/monkey/' + asset.guid,
- {
- method: 'PATCH',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({config: newConfig})
- });
- }
-
- forceKillRow(asset) {
- return (
-
-
- Force Kill
- {this.generateToolTip('If this is on, monkey will die next time it communicates')}
- |
-
- this.forceKill(e, asset)}/>
-
- |
-
- );
- }
-
- unescapeLog(st) {
- return st.substr(1, st.length - 2) // remove quotation marks on beginning and end of string.
- .replace(/\\n/g, "\n")
- .replace(/\\r/g, "\r")
- .replace(/\\t/g, "\t")
- .replace(/\\b/g, "\b")
- .replace(/\\f/g, "\f")
- .replace(/\\"/g, '\"')
- .replace(/\\'/g, "\'")
- .replace(/\\&/g, "\&");
- }
-
- downloadLog(asset) {
- this.authFetch('/api/log?id=' + asset.id)
- .then(res => res.json())
- .then(res => {
- let timestamp = res['timestamp'];
- timestamp = timestamp.substr(0, timestamp.indexOf('.'));
- let filename = res['monkey_label'].split(':').join('-') + ' - ' + timestamp + '.log';
- let logContent = this.unescapeLog(res['log']);
- download(logContent, filename, 'text/plain');
- });
-
- }
-
- downloadLogRow(asset) {
- return (
-
-
- Download Log
- |
-
- this.downloadLog(asset)}>Download
- |
-
- );
- }
-
- exploitsTimeline(asset) {
- if (asset.exploits.length === 0) {
- return ();
+ getLabelByProps() {
+ if (!this.props.item) {
+ return '';
+ } else if (this.props.item.hasOwnProperty('label')) {
+ return this.props.item['label'];
+ } else if (this.props.item.hasOwnProperty('_label')) {
+ return this.props.item['_label'];
}
-
- return (
-
-
- Exploit Timeline
- {this.generateToolTip('Timeline of exploit attempts. Red is successful. Gray is unsuccessful')}
-
-
-
- )
- }
-
- assetInfo(asset) {
- return (
-
-
-
- {this.osRow(asset)}
- {this.ipsRow(asset)}
- {this.servicesRow(asset)}
- {this.accessibleRow(asset)}
-
-
- {this.exploitsTimeline(asset)}
-
- );
- }
-
- infectedAssetInfo(asset) {
- return (
-
-
-
- {this.osRow(asset)}
- {this.statusRow(asset)}
- {this.ipsRow(asset)}
- {this.servicesRow(asset)}
- {this.accessibleRow(asset)}
- {this.forceKillRow(asset)}
- {this.downloadLogRow(asset)}
-
-
- {this.exploitsTimeline(asset)}
-
- );
- }
-
- scanInfo(edge) {
- return (
-
-
-
-
- Operating System |
- {edge.os.type} |
-
-
- IP Address |
- {edge.ip_address} |
-
-
- Services |
- {edge.services.map(val => {val} )} |
-
-
-
- {
- (edge.exploits.length === 0) ?
- '' :
-
- }
-
- );
- }
-
- islandEdgeInfo() {
- return (
-
-
- );
+ return '';
}
render() {
- let info = null;
- switch (this.props.type) {
- case 'edge':
- info = this.scanInfo(this.props.item);
- break;
- case 'node':
- info = this.props.item.group.includes('monkey', 'manual') ?
- this.infectedAssetInfo(this.props.item) : this.assetInfo(this.props.item);
- break;
- case 'island_edge':
- info = this.islandEdgeInfo();
- break;
- }
-
- let label = '';
- if (!this.props.item) {
- label = '';
- } else if (this.props.item.hasOwnProperty('label')) {
- label = this.props.item['label'];
- } else if (this.props.item.hasOwnProperty('_label')) {
- label = this.props.item['_label'];
- }
+ let info = this.getInfoByProps();
+ let label = this.getLabelByProps();
return (
diff --git a/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js b/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js
new file mode 100644
index 000000000..f9a5ae1bb
--- /dev/null
+++ b/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import {Icon} from 'react-fa';
+import Toggle from 'react-toggle';
+import {OverlayTrigger, Tooltip} from 'react-bootstrap';
+import download from 'downloadjs'
+import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane';
+
+class PthPreviewPaneComponent extends PreviewPaneComponent {
+ nodeInfo(asset) {
+ return (
+
+
+
+
+ Hostname |
+ {asset.hostname} |
+
+
+ IP Addresses |
+ {asset.ips.map(val => {val} )} |
+
+
+ Services |
+ {asset.services.map(val => {val} )} |
+
+
+ Compromised Users |
+ {asset.users.map(val => {val} )} |
+
+
+
+
+ );
+ }
+
+ edgeInfo(edge) {
+ return (
+
+
+
+
+ Compromised Users |
+ {edge.users.map(val => {val} )} |
+
+
+
+
+ );
+ }
+
+ getInfoByProps() {
+ switch (this.props.type) {
+ case 'edge':
+ return this.edgeInfo(this.props.item);
+ case 'node':
+ return this.nodeInfo(this.props.item);
+ }
+
+ return null;
+ }
+}
+
+export default PthPreviewPaneComponent;
diff --git a/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey_island/cc/ui/src/components/pages/MapPage.js
index 4a54aeb8c..00c0cba3c 100644
--- a/monkey_island/cc/ui/src/components/pages/MapPage.js
+++ b/monkey_island/cc/ui/src/components/pages/MapPage.js
@@ -2,7 +2,7 @@ import React from 'react';
import {Col} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import {Icon} from 'react-fa';
-import PreviewPane from 'components/map/preview-pane/PreviewPane';
+import InfMapPreviewPaneComponent from 'components/map/preview-pane/InfMapPreviewPane';
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {ModalContainer, ModalDialog} from 'react-modal-dialog';
import {options, edgeGroupToColor} from 'components/map/MapOptions';
@@ -190,7 +190,7 @@ class MapPageComponent extends AuthComponent {
: ''}
-
+
);
diff --git a/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js b/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js
index 2ac43f094..8c7ded49b 100644
--- a/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js
+++ b/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js
@@ -1,83 +1,57 @@
import React from 'react';
-import {Col} from 'react-bootstrap';
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import AuthComponent from '../AuthComponent';
-import Graph from 'react-graph-vis';
-
-const options = {
- autoResize: true,
- layout: {
- improvedLayout: false
- },
- edges: {
- width: 2,
- smooth: {
- type: 'curvedCW'
- }
- },
- physics: {
- barnesHut: {
- gravitationalConstant: -120000,
- avoidOverlap: 0.5
- },
- minVelocity: 0.75
- }
-};
+import {optionsPth, edgeGroupToColorPth, options} from '../map/MapOptions';
+import PreviewPane from "../map/preview-pane/PreviewPane";
+import {Col} from "react-bootstrap";
+import {Link} from 'react-router-dom';
+import {Icon} from 'react-fa';
+import PthPreviewPaneComponent from "../map/preview-pane/PthPreviewPane";
class PassTheHashMapPageComponent extends AuthComponent {
constructor(props) {
super(props);
this.state = {
- graph: {nodes: [], edges: []},
- report: "",
+ graph: props.graph,
selected: null,
- selectedType: null,
- killPressed: false,
- showKillDialog: false,
- telemetry: [],
- telemetryLastTimestamp: null
+ selectedType: null
};
}
- componentDidMount() {
- this.updateMapFromServer();
- this.interval = setInterval(this.timedEvents, 1000);
- }
-
- componentWillUnmount() {
- clearInterval(this.interval);
- }
-
- timedEvents = () => {
- this.updateMapFromServer();
+ events = {
+ select: event => this.selectionChanged(event)
};
- updateMapFromServer = () => {
- this.authFetch('/api/pthmap')
- .then(res => res.json())
- .then(res => {
- this.setState({graph: res});
- this.props.onStatusChange();
- });
- this.authFetch('/api/pthreport')
- .then(res => res.json())
- .then(res => {
- this.setState({report: res.html});
- this.props.onStatusChange();
- });
- };
+ selectionChanged(event) {
+ if (event.nodes.length === 1) {
+ let displayedNode = this.state.graph.nodes.find(
+ function (node) {
+ return node['id'] === event.nodes[0];
+ });
+ this.setState({selected: displayedNode, selectedType: 'node'})
+ }
+ else if (event.edges.length === 1) {
+ let displayedEdge = this.state.graph.edges.find(
+ function (edge) {
+ return edge['id'] === event.edges[0];
+ });
+ this.setState({selected: displayedEdge, selectedType: 'edge'});
+ }
+ else {
+ this.setState({selected: null, selectedType: null});
+ }
+ }
render() {
return (
-
-
Pass The Hash Map
+
+
+
+
-
-
-
-
+
);
diff --git a/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey_island/cc/ui/src/components/pages/ReportPage.js
index bec4f3625..7590dbcae 100644
--- a/monkey_island/cc/ui/src/components/pages/ReportPage.js
+++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js
@@ -7,7 +7,11 @@ import {edgeGroupToColor, options} from 'components/map/MapOptions';
import StolenPasswords from 'components/report-components/StolenPasswords';
import CollapsibleWellComponent from 'components/report-components/CollapsibleWell';
import {Line} from 'rc-progress';
-import AuthComponent from '../AuthComponent';
+import AuthComponent from 'components/AuthComponent';
+import PassTheHashMapPageComponent from "./PassTheHashMapPage";
+import SharedCreds from "components/report-components/SharedCreds";
+import StrongUsers from "components/report-components/StrongUsers";
+import SharedAdmins from "components/report-components/SharedAdmins";
let guardicoreLogoImage = require('../../images/guardicore-logo.png');
let monkeyLogoImage = require('../../images/monkey-icon.svg');
@@ -413,9 +417,70 @@ class ReportPageComponent extends AuthComponent {
-
+ {this.generateReportPthMap()}
+
+
+ { /* TODO: use dynamic data */}
+
+
+
+ { /* TODO: use dynamic data */}
+
+
+
+ { /* TODO: use dynamic data */}
+
+
+
+ );
+ }
+
+ generateReportPthMap() {
+ // TODO: remove this and use updateMapFromSerever to get actual map data.
+ const my_map = {
+ nodes: [
+ {id: '1', label: 'MYPC-1', group: 'normal', users: ['MYPC-2\\user1', 'Dom\\user2'], ips: ['192.168.0.1'], services: ["DC", "SQL"], 'hostname': 'aaa1'},
+ {id: 2, label: 'MYPC-2', group: 'critical', users: ['MYPC-2\\user1', 'Dom\\user2'], ips: ['192.168.0.1'], services: ["DC", "SQL"], 'hostname': 'aaa2'},
+ {id: 3, label: 'MYPC-3', group: 'normal', users: ['MYPC-3\\user1', 'Dom\\user3'], ips: ['192.168.0.2'], services: ["DC", "SQL"], 'hostname': 'aaa3'},
+ {id: 4, label: 'MYPC-4', group: 'critical', users: ['MYPC-4\\user1', 'Dom\\user4'], ips: ['192.168.0.3', '192.168.0.4'], services: ["DC", "SQL"], 'hostname': 'aaa4'},
+ {id: 5, label: 'MYPC-5', group: 'normal', users: ['MYPC-5\\user1', 'Dom\\user5'], ips: ['192.168.0.1'], services: [], 'hostname': 'aaa5'},
+ {id: 6, label: 'MYPC-6', group: 'critical', users: ['MYPC-6\\user1', 'Dom\\user6'], ips: ['192.168.0.1'], services: ["DC"], 'hostname': 'aaa6'},
+ {id: 7, label: 'MYPC-7', group: 'critical', users: ['MYPC-7\\user1', 'Dom\\user7'], ips: ['192.168.0.1'], services: ["DC", "SQL"], 'hostname': 'aaa7'}
+ ],
+ edges: [
+ {id: 10, from: '1', to: 2, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla0'},
+ {id: 11, from: '1', to: 3, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla1'},
+ {id: 12, from: '1', to: 4, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla2'},
+ {id: 13, from: 5, to: 6, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla3'},
+ {id: 14, from: 6, to: 7, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla4'},
+ {id: 15, from: 6, to: 5, users: ['MYPC-3\\user1', 'Dom\\user3'], _label: 'bla5'},
+ ]
+
+ };
+ return (
+
);
}
diff --git a/monkey_island/cc/ui/src/components/report-components/BreachedServers.js b/monkey_island/cc/ui/src/components/report-components/BreachedServers.js
index d8c91f5ca..d23a14c38 100644
--- a/monkey_island/cc/ui/src/components/report-components/BreachedServers.js
+++ b/monkey_island/cc/ui/src/components/report-components/BreachedServers.js
@@ -2,10 +2,7 @@ import React from 'react';
import ReactTable from 'react-table'
let renderArray = function(val) {
- if (val.length === 0) {
- return '';
- }
- return val.reduce((total, new_str) => total + ', ' + new_str);
+ return ;
};
const columns = [
diff --git a/monkey_island/cc/ui/src/components/report-components/ScannedServers.js b/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
index b598ab537..9b62bbdc5 100644
--- a/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
+++ b/monkey_island/cc/ui/src/components/report-components/ScannedServers.js
@@ -2,10 +2,7 @@ import React from 'react';
import ReactTable from 'react-table'
let renderArray = function(val) {
- if (val.length === 0) {
- return '';
- }
- return val.reduce((total, new_str) => total + ', ' + new_str);
+ return ;
};
const columns = [
diff --git a/monkey_island/cc/ui/src/components/report-components/SharedAdmins.js b/monkey_island/cc/ui/src/components/report-components/SharedAdmins.js
new file mode 100644
index 000000000..bf57065d5
--- /dev/null
+++ b/monkey_island/cc/ui/src/components/report-components/SharedAdmins.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import ReactTable from 'react-table'
+
+let renderArray = function(val) {
+ return ;
+};
+
+const columns = [
+ {
+ Header: 'Shared Admins Between Machines',
+ columns: [
+ { Header: 'Username', accessor: 'username'},
+ { Header: 'Domain', accessor: 'domain'},
+ { Header: 'Machines', id: 'machines', accessor: x => renderArray(x.machines)},
+ ]
+ }
+];
+
+const pageSize = 10;
+
+class SharedAdminsComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
+ let showPagination = this.props.data.length > pageSize;
+ return (
+
+
+
+ );
+ }
+}
+
+export default SharedAdminsComponent;
diff --git a/monkey_island/cc/ui/src/components/report-components/SharedCreds.js b/monkey_island/cc/ui/src/components/report-components/SharedCreds.js
new file mode 100644
index 000000000..f42494167
--- /dev/null
+++ b/monkey_island/cc/ui/src/components/report-components/SharedCreds.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import ReactTable from 'react-table'
+
+let renderArray = function(val) {
+ console.log(val);
+ return ;
+};
+
+const columns = [
+ {
+ Header: 'Shared Credentials',
+ columns: [
+ {Header: 'Credential Group', id: 'cred_group', accessor: x => renderArray(x.cred_group) }
+ ]
+ }
+];
+
+const pageSize = 10;
+
+class SharedCredsComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
+ let showPagination = this.props.data.length > pageSize;
+ return (
+
+
+
+ );
+ }
+}
+
+export default SharedCredsComponent;
diff --git a/monkey_island/cc/ui/src/components/report-components/StrongUsers.js b/monkey_island/cc/ui/src/components/report-components/StrongUsers.js
new file mode 100644
index 000000000..bfb933ec1
--- /dev/null
+++ b/monkey_island/cc/ui/src/components/report-components/StrongUsers.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import ReactTable from 'react-table'
+
+let renderArray = function(val) {
+ return ;
+};
+
+const columns = [
+ {
+ Header: 'Powerful Users',
+ columns: [
+ { Header: 'Username', accessor: 'username'},
+ { Header: 'Domain', accessor: 'domain'},
+ { Header: 'Machines', id: 'machines', accessor: x => renderArray(x.machines)},
+ { Header: 'Services', id: 'services', accessor: x => renderArray(x.services)}
+ ]
+ }
+];
+
+const pageSize = 10;
+
+class StrongUsersComponent extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
+ let showPagination = this.props.data.length > pageSize;
+ return (
+
+
+
+ );
+ }
+}
+
+export default StrongUsersComponent;
diff --git a/monkey_island/cc/ui/src/images/nodes/pth/critical.png b/monkey_island/cc/ui/src/images/nodes/pth/critical.png
new file mode 100644
index 000000000..0348a7f5d
Binary files /dev/null and b/monkey_island/cc/ui/src/images/nodes/pth/critical.png differ
diff --git a/monkey_island/cc/ui/src/images/nodes/pth/normal.png b/monkey_island/cc/ui/src/images/nodes/pth/normal.png
new file mode 100644
index 000000000..3b1e9b638
Binary files /dev/null and b/monkey_island/cc/ui/src/images/nodes/pth/normal.png differ