forked from p15670423/monkey
Merge branch 'nadler/pth-map' into nadler/pth
This commit is contained in:
commit
2a12fefe6d
|
@ -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':
|
||||
|
|
|
@ -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 (
|
||||
<tr>
|
||||
<th>Operating System</th>
|
||||
<td>{asset.os.charAt(0).toUpperCase() + asset.os.slice(1)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
ipsRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>IP Addresses</th>
|
||||
<td>{asset.ip_addresses.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
servicesRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Services</th>
|
||||
<td>{asset.services.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
accessibleRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>
|
||||
Accessible From
|
||||
{this.generateToolTip('List of machine which can access this one using a network protocol')}
|
||||
</th>
|
||||
<td>{asset.accessible_from_nodes.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
statusRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td>{(asset.dead) ? 'Dead' : 'Alive'}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<tr>
|
||||
<th>
|
||||
Force Kill
|
||||
{this.generateToolTip('If this is on, monkey will die next time it communicates')}
|
||||
</th>
|
||||
<td>
|
||||
<Toggle id={asset.id} checked={!asset.config.alive} icons={false} disabled={asset.dead}
|
||||
onChange={(e) => this.forceKill(e, asset)}/>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<tr>
|
||||
<th>
|
||||
Download Log
|
||||
</th>
|
||||
<td>
|
||||
<a type="button" className="btn btn-primary"
|
||||
disabled={!asset.has_log}
|
||||
onClick={() => this.downloadLog(asset)}>Download</a>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
exploitsTimeline(asset) {
|
||||
if (asset.exploits.length === 0) {
|
||||
return (<div/>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4 style={{'marginTop': '2em'}}>
|
||||
Exploit Timeline
|
||||
{this.generateToolTip('Timeline of exploit attempts. Red is successful. Gray is unsuccessful')}
|
||||
</h4>
|
||||
<ul className="timeline">
|
||||
{asset.exploits.map(exploit =>
|
||||
<li key={exploit.timestamp}>
|
||||
<div className={'bullet ' + (exploit.result ? 'bad' : '')}/>
|
||||
<div>{new Date(exploit.timestamp).toLocaleString()}</div>
|
||||
<div>{exploit.origin}</div>
|
||||
<div>{exploit.exploiter}</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
assetInfo(asset) {
|
||||
return (
|
||||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
{this.osRow(asset)}
|
||||
{this.ipsRow(asset)}
|
||||
{this.servicesRow(asset)}
|
||||
{this.accessibleRow(asset)}
|
||||
</tbody>
|
||||
</table>
|
||||
{this.exploitsTimeline(asset)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
infectedAssetInfo(asset) {
|
||||
return (
|
||||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
{this.osRow(asset)}
|
||||
{this.statusRow(asset)}
|
||||
{this.ipsRow(asset)}
|
||||
{this.servicesRow(asset)}
|
||||
{this.accessibleRow(asset)}
|
||||
{this.forceKillRow(asset)}
|
||||
{this.downloadLogRow(asset)}
|
||||
</tbody>
|
||||
</table>
|
||||
{this.exploitsTimeline(asset)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
scanInfo(edge) {
|
||||
return (
|
||||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Operating System</th>
|
||||
<td>{edge.os.type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IP Address</th>
|
||||
<td>{edge.ip_address}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Services</th>
|
||||
<td>{edge.services.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{
|
||||
(edge.exploits.length === 0) ?
|
||||
'' :
|
||||
<div>
|
||||
<h4 style={{'marginTop': '2em'}}>Timeline</h4>
|
||||
<ul className="timeline">
|
||||
{edge.exploits.map(exploit =>
|
||||
<li key={exploit.timestamp}>
|
||||
<div className={'bullet ' + (exploit.result ? 'bad' : '')}/>
|
||||
<div>{new Date(exploit.timestamp).toLocaleString()}</div>
|
||||
<div>{exploit.origin}</div>
|
||||
<div>{exploit.exploiter}</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
islandEdgeInfo() {
|
||||
return (
|
||||
<div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
|
@ -15,251 +15,25 @@ class PreviewPaneComponent extends AuthComponent {
|
|||
);
|
||||
}
|
||||
|
||||
osRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Operating System</th>
|
||||
<td>{asset.os.charAt(0).toUpperCase() + asset.os.slice(1)}</td>
|
||||
</tr>
|
||||
);
|
||||
// This should be overridden
|
||||
getInfoByProps() {
|
||||
return null;
|
||||
}
|
||||
|
||||
ipsRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>IP Addresses</th>
|
||||
<td>{asset.ip_addresses.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
servicesRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Services</th>
|
||||
<td>{asset.services.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
accessibleRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>
|
||||
Accessible From
|
||||
{this.generateToolTip('List of machine which can access this one using a network protocol')}
|
||||
</th>
|
||||
<td>{asset.accessible_from_nodes.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
statusRow(asset) {
|
||||
return (
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td>{(asset.dead) ? 'Dead' : 'Alive'}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<tr>
|
||||
<th>
|
||||
Force Kill
|
||||
{this.generateToolTip('If this is on, monkey will die next time it communicates')}
|
||||
</th>
|
||||
<td>
|
||||
<Toggle id={asset.id} checked={!asset.config.alive} icons={false} disabled={asset.dead}
|
||||
onChange={(e) => this.forceKill(e, asset)}/>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<tr>
|
||||
<th>
|
||||
Download Log
|
||||
</th>
|
||||
<td>
|
||||
<a type="button" className="btn btn-primary"
|
||||
disabled={!asset.has_log}
|
||||
onClick={() => this.downloadLog(asset)}>Download</a>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
exploitsTimeline(asset) {
|
||||
if (asset.exploits.length === 0) {
|
||||
return (<div/>);
|
||||
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 (
|
||||
<div>
|
||||
<h4 style={{'marginTop': '2em'}}>
|
||||
Exploit Timeline
|
||||
{this.generateToolTip('Timeline of exploit attempts. Red is successful. Gray is unsuccessful')}
|
||||
</h4>
|
||||
<ul className="timeline">
|
||||
{asset.exploits.map(exploit =>
|
||||
<li key={exploit.timestamp}>
|
||||
<div className={'bullet ' + (exploit.result ? 'bad' : '')}/>
|
||||
<div>{new Date(exploit.timestamp).toLocaleString()}</div>
|
||||
<div>{exploit.origin}</div>
|
||||
<div>{exploit.exploiter}</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
assetInfo(asset) {
|
||||
return (
|
||||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
{this.osRow(asset)}
|
||||
{this.ipsRow(asset)}
|
||||
{this.servicesRow(asset)}
|
||||
{this.accessibleRow(asset)}
|
||||
</tbody>
|
||||
</table>
|
||||
{this.exploitsTimeline(asset)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
infectedAssetInfo(asset) {
|
||||
return (
|
||||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
{this.osRow(asset)}
|
||||
{this.statusRow(asset)}
|
||||
{this.ipsRow(asset)}
|
||||
{this.servicesRow(asset)}
|
||||
{this.accessibleRow(asset)}
|
||||
{this.forceKillRow(asset)}
|
||||
{this.downloadLogRow(asset)}
|
||||
</tbody>
|
||||
</table>
|
||||
{this.exploitsTimeline(asset)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
scanInfo(edge) {
|
||||
return (
|
||||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Operating System</th>
|
||||
<td>{edge.os.type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IP Address</th>
|
||||
<td>{edge.ip_address}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Services</th>
|
||||
<td>{edge.services.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{
|
||||
(edge.exploits.length === 0) ?
|
||||
'' :
|
||||
<div>
|
||||
<h4 style={{'marginTop': '2em'}}>Timeline</h4>
|
||||
<ul className="timeline">
|
||||
{edge.exploits.map(exploit =>
|
||||
<li key={exploit.timestamp}>
|
||||
<div className={'bullet ' + (exploit.result ? 'bad' : '')}/>
|
||||
<div>{new Date(exploit.timestamp).toLocaleString()}</div>
|
||||
<div>{exploit.origin}</div>
|
||||
<div>{exploit.exploiter}</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
islandEdgeInfo() {
|
||||
return (
|
||||
<div>
|
||||
</div>
|
||||
);
|
||||
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 (
|
||||
<div className="preview-pane">
|
||||
|
|
|
@ -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 (
|
||||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Hostname</th>
|
||||
<td>{asset.hostname}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IP Addresses</th>
|
||||
<td>{asset.ips.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Services</th>
|
||||
<td>{asset.services.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Compromised Users</th>
|
||||
<td>{asset.users.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
edgeInfo(edge) {
|
||||
return (
|
||||
<div>
|
||||
<table className="table table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Compromised Users</th>
|
||||
<td>{edge.users.map(val => <div key={val}>{val}</div>)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
|
@ -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 {
|
|||
</div>
|
||||
: ''}
|
||||
|
||||
<PreviewPane item={this.state.selected} type={this.state.selectedType}/>
|
||||
<InfMapPreviewPaneComponent item={this.state.selected} type={this.state.selectedType}/>
|
||||
</Col>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -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 (
|
||||
<div>
|
||||
<Col xs={12} lg={8}>
|
||||
<h1 className="page-title">Pass The Hash Map</h1>
|
||||
<Col xs={12}>
|
||||
<div style={{height: '70vh'}}>
|
||||
<ReactiveGraph graph={this.state.graph} options={optionsPth} events={this.events}/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={12}>
|
||||
<div>
|
||||
<Graph graph={this.state.graph} options={options} />
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{__html: this.state.report}}></div>
|
||||
<PthPreviewPaneComponent item={this.state.selected} type={this.state.selectedType}/>
|
||||
</Col>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -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 {
|
|||
<div style={{marginBottom: '20px'}}>
|
||||
<ScannedServers data={this.state.report.glance.scanned}/>
|
||||
</div>
|
||||
<div>
|
||||
{this.generateReportPthMap()}
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<StolenPasswords data={this.state.report.glance.stolen_creds}/>
|
||||
</div>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
{ /* TODO: use dynamic data */}
|
||||
<SharedCreds data = {[{cred_group: ['MyDomain\\user1', 'user2', 'user3']}, {cred_group: ['user2', 'user4']}]} />
|
||||
</div>
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
{ /* TODO: use dynamic data */}
|
||||
<SharedAdmins data = {[
|
||||
{
|
||||
username: 'SharedLocalAdmin',
|
||||
domain: 'MyDomain',
|
||||
machines: ['hello : 1.2.3.4']
|
||||
}
|
||||
]} />
|
||||
</div>
|
||||
<div>
|
||||
{ /* TODO: use dynamic data */}
|
||||
<StrongUsers data = {[
|
||||
{
|
||||
username: 'SharedLocalAdmin',
|
||||
domain: 'MyDomain',
|
||||
machines: ['hello : 1.2.3.4'],
|
||||
services: ['DC', 'DNS']
|
||||
}
|
||||
]} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<div id="pth">
|
||||
<h3>
|
||||
Credential Map
|
||||
</h3>
|
||||
<div style={{position: 'relative', height: '100vh'}}>
|
||||
<PassTheHashMapPageComponent graph={my_map} />
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 <div>{val.map(x => <div>{x}</div>)}</div>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
|
|
|
@ -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 <div>{val.map(x => <div>{x}</div>)}</div>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
let renderArray = function(val) {
|
||||
return <div>{val.map(x => <div>{x}</div>)}</div>;
|
||||
};
|
||||
|
||||
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 (
|
||||
<div className="data-table-container">
|
||||
<ReactTable
|
||||
columns={columns}
|
||||
data={this.props.data}
|
||||
showPagination={showPagination}
|
||||
defaultPageSize={defaultPageSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SharedAdminsComponent;
|
|
@ -0,0 +1,41 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
let renderArray = function(val) {
|
||||
console.log(val);
|
||||
return <div>{val.map(x => <div>{x}</div>)}</div>;
|
||||
};
|
||||
|
||||
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 (
|
||||
<div className="data-table-container">
|
||||
<ReactTable
|
||||
columns={columns}
|
||||
data={this.props.data}
|
||||
showPagination={showPagination}
|
||||
defaultPageSize={defaultPageSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SharedCredsComponent;
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
|
||||
let renderArray = function(val) {
|
||||
return <div>{val.map(x => <div>{x}</div>)}</div>;
|
||||
};
|
||||
|
||||
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 (
|
||||
<div className="data-table-container">
|
||||
<ReactTable
|
||||
columns={columns}
|
||||
data={this.props.data}
|
||||
showPagination={showPagination}
|
||||
defaultPageSize={defaultPageSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default StrongUsersComponent;
|
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Loading…
Reference in New Issue