From 800e337f6f0e4fc3f9d25c5dff417ae3dc736a33 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Thu, 19 Jul 2018 18:35:37 +0300 Subject: [PATCH 1/2] Add credential map to report. currently uses fake static data --- .../cc/ui/src/components/map/MapOptions.js | 35 ++- .../map/preview-pane/InfMapPreviewPane.js | 247 +++++++++++++++++ .../map/preview-pane/PreviewPane.js | 252 +----------------- .../map/preview-pane/PthPreviewPane.js | 63 +++++ .../cc/ui/src/components/pages/MapPage.js | 4 +- .../components/pages/PassTheHashMapPage.js | 94 +++---- .../cc/ui/src/components/pages/ReportPage.js | 46 ++++ .../cc/ui/src/images/nodes/pth/critical.png | Bin 0 -> 20067 bytes .../cc/ui/src/images/nodes/pth/normal.png | Bin 0 -> 19466 bytes 9 files changed, 436 insertions(+), 305 deletions(-) create mode 100644 monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js create mode 100644 monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js create mode 100644 monkey_island/cc/ui/src/images/nodes/pth/critical.png create mode 100644 monkey_island/cc/ui/src/images/nodes/pth/normal.png 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')} +

+
    + {asset.exploits.map(exploit => +
  • +
    +
    {new Date(exploit.timestamp).toLocaleString()}
    +
    {exploit.origin}
    +
    {exploit.exploiter}
    +
  • + )} +
+
+ ) + } + + 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) ? + '' : +
+

Timeline

+
    + {edge.exploits.map(exploit => +
  • +
    +
    {new Date(exploit.timestamp).toLocaleString()}
    +
    {exploit.origin}
    +
    {exploit.exploiter}
    +
  • + )} +
+
+ } +
+ ); + } + + 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')} -

-
    - {asset.exploits.map(exploit => -
  • -
    -
    {new Date(exploit.timestamp).toLocaleString()}
    -
    {exploit.origin}
    -
    {exploit.exploiter}
    -
  • - )} -
-
- ) - } - - 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) ? - '' : -
-

Timeline

-
    - {edge.exploits.map(exploit => -
  • -
    -
    {new Date(exploit.timestamp).toLocaleString()}
    -
    {exploit.origin}
    -
    {exploit.exploiter}
    -
  • - )} -
-
- } -
- ); - } - - 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..adb024c72 100644 --- a/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -8,6 +8,7 @@ import StolenPasswords from 'components/report-components/StolenPasswords'; import CollapsibleWellComponent from 'components/report-components/CollapsibleWell'; import {Line} from 'rc-progress'; import AuthComponent from '../AuthComponent'; +import PassTheHashMapPageComponent from "./PassTheHashMapPage"; let guardicoreLogoImage = require('../../images/guardicore-logo.png'); let monkeyLogoImage = require('../../images/monkey-icon.svg'); @@ -129,6 +130,7 @@ class ReportPageComponent extends AuthComponent { {this.generateReportFindingsSection()} {this.generateReportRecommendationsSection()} {this.generateReportGlanceSection()} + {this.generateReportPthSection()} {this.generateReportFooter()}
@@ -420,6 +422,50 @@ class ReportPageComponent extends AuthComponent { ); } + generateReportPthSection() { + // 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 ( +
+

+ Pass The Hash !!!!!TODO: change this!!!!!!!! +

+
+ +
+
+ TODO: put relevant tables and stuff here... +
+
+ TODO: put relevant tables and stuff here... +
+
+ TODO: put relevant tables and stuff here... +
+
+
+ ); + } + generateReportFooter() { return (
@@ -415,14 +417,40 @@ class ReportPageComponent extends AuthComponent {
-
+ {this.generateReportPthMap()} +
+
+ { /* TODO: use dynamic data */} + +
+
+ { /* TODO: use dynamic data */} + +
+
+ { /* TODO: use dynamic data */} + +
); } - generateReportPthSection() { + generateReportPthMap() { // TODO: remove this and use updateMapFromSerever to get actual map data. const my_map = { nodes: [ @@ -447,20 +475,11 @@ class ReportPageComponent extends AuthComponent { return (

- Pass The Hash !!!!!TODO: change this!!!!!!!! + Credential Map

-
- TODO: put relevant tables and stuff here... -
-
- TODO: put relevant tables and stuff here... -
-
- TODO: put relevant tables and stuff here... -

); 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
{val.map(x =>
{x}
)}
; }; 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
{val.map(x =>
{x}
)}
; }; 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
{val.map(x =>
{x}
)}
; +}; + +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
{val.map(x =>
{x}
)}
; +}; + +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
{val.map(x =>
{x}
)}
; +}; + +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;