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 (