Merge branch 'nadler/pth-map' into nadler/pth

This commit is contained in:
maor.rayzin 2018-07-24 14:46:32 +03:00
commit 2a12fefe6d
14 changed files with 585 additions and 315 deletions

View File

@ -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', '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', 'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux',
'monkey_linux_running', 'monkey_windows', 'monkey_windows_running']; 'monkey_linux_running', 'monkey_windows', 'monkey_windows_running'];
@ -16,7 +16,22 @@ let getGroupsOptions = () => {
return groupOptions; 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, autoResize: true,
layout: { layout: {
improvedLayout: false improvedLayout: false
@ -33,10 +48,22 @@ export const options = {
avoidOverlap: 0.5 avoidOverlap: 0.5
}, },
minVelocity: 0.75 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) { export function edgeGroupToColor(group) {
switch (group) { switch (group) {
case 'exploited': case 'exploited':

View File

@ -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&nbsp;
{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&nbsp;
{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&nbsp;
{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;

View File

@ -15,251 +15,25 @@ class PreviewPaneComponent extends AuthComponent {
); );
} }
osRow(asset) { // This should be overridden
return ( getInfoByProps() {
<tr> return null;
<th>Operating System</th>
<td>{asset.os.charAt(0).toUpperCase() + asset.os.slice(1)}</td>
</tr>
);
} }
ipsRow(asset) { getLabelByProps() {
return ( if (!this.props.item) {
<tr> return '';
<th>IP Addresses</th> } else if (this.props.item.hasOwnProperty('label')) {
<td>{asset.ip_addresses.map(val => <div key={val}>{val}</div>)}</td> return this.props.item['label'];
</tr> } else if (this.props.item.hasOwnProperty('_label')) {
); return this.props.item['_label'];
}
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&nbsp;
{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&nbsp;
{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 '';
return (
<div>
<h4 style={{'marginTop': '2em'}}>
Exploit Timeline&nbsp;
{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>
);
} }
render() { render() {
let info = null; let info = this.getInfoByProps();
switch (this.props.type) { let label = this.getLabelByProps();
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'];
}
return ( return (
<div className="preview-pane"> <div className="preview-pane">

View File

@ -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;

View File

@ -2,7 +2,7 @@ import React from 'react';
import {Col} from 'react-bootstrap'; import {Col} from 'react-bootstrap';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import {Icon} from 'react-fa'; 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 {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import {ModalContainer, ModalDialog} from 'react-modal-dialog'; import {ModalContainer, ModalDialog} from 'react-modal-dialog';
import {options, edgeGroupToColor} from 'components/map/MapOptions'; import {options, edgeGroupToColor} from 'components/map/MapOptions';
@ -190,7 +190,7 @@ class MapPageComponent extends AuthComponent {
</div> </div>
: ''} : ''}
<PreviewPane item={this.state.selected} type={this.state.selectedType}/> <InfMapPreviewPaneComponent item={this.state.selected} type={this.state.selectedType}/>
</Col> </Col>
</div> </div>
); );

View File

@ -1,83 +1,57 @@
import React from 'react'; import React from 'react';
import {Col} from 'react-bootstrap';
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
import AuthComponent from '../AuthComponent'; import AuthComponent from '../AuthComponent';
import Graph from 'react-graph-vis'; import {optionsPth, edgeGroupToColorPth, options} from '../map/MapOptions';
import PreviewPane from "../map/preview-pane/PreviewPane";
const options = { import {Col} from "react-bootstrap";
autoResize: true, import {Link} from 'react-router-dom';
layout: { import {Icon} from 'react-fa';
improvedLayout: false import PthPreviewPaneComponent from "../map/preview-pane/PthPreviewPane";
},
edges: {
width: 2,
smooth: {
type: 'curvedCW'
}
},
physics: {
barnesHut: {
gravitationalConstant: -120000,
avoidOverlap: 0.5
},
minVelocity: 0.75
}
};
class PassTheHashMapPageComponent extends AuthComponent { class PassTheHashMapPageComponent extends AuthComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
graph: {nodes: [], edges: []}, graph: props.graph,
report: "",
selected: null, selected: null,
selectedType: null, selectedType: null
killPressed: false,
showKillDialog: false,
telemetry: [],
telemetryLastTimestamp: null
}; };
} }
componentDidMount() { events = {
this.updateMapFromServer(); select: event => this.selectionChanged(event)
this.interval = setInterval(this.timedEvents, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
timedEvents = () => {
this.updateMapFromServer();
}; };
updateMapFromServer = () => { selectionChanged(event) {
this.authFetch('/api/pthmap') if (event.nodes.length === 1) {
.then(res => res.json()) let displayedNode = this.state.graph.nodes.find(
.then(res => { function (node) {
this.setState({graph: res}); return node['id'] === event.nodes[0];
this.props.onStatusChange(); });
}); this.setState({selected: displayedNode, selectedType: 'node'})
this.authFetch('/api/pthreport') }
.then(res => res.json()) else if (event.edges.length === 1) {
.then(res => { let displayedEdge = this.state.graph.edges.find(
this.setState({report: res.html}); function (edge) {
this.props.onStatusChange(); return edge['id'] === event.edges[0];
}); });
}; this.setState({selected: displayedEdge, selectedType: 'edge'});
}
else {
this.setState({selected: null, selectedType: null});
}
}
render() { render() {
return ( return (
<div> <div>
<Col xs={12} lg={8}> <Col xs={12}>
<h1 className="page-title">Pass The Hash Map</h1> <div style={{height: '70vh'}}>
<ReactiveGraph graph={this.state.graph} options={optionsPth} events={this.events}/>
</div>
</Col> </Col>
<Col xs={12}> <Col xs={12}>
<div> <PthPreviewPaneComponent item={this.state.selected} type={this.state.selectedType}/>
<Graph graph={this.state.graph} options={options} />
</div>
<div dangerouslySetInnerHTML={{__html: this.state.report}}></div>
</Col> </Col>
</div> </div>
); );

View File

@ -7,7 +7,11 @@ import {edgeGroupToColor, options} from 'components/map/MapOptions';
import StolenPasswords from 'components/report-components/StolenPasswords'; import StolenPasswords from 'components/report-components/StolenPasswords';
import CollapsibleWellComponent from 'components/report-components/CollapsibleWell'; import CollapsibleWellComponent from 'components/report-components/CollapsibleWell';
import {Line} from 'rc-progress'; 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 guardicoreLogoImage = require('../../images/guardicore-logo.png');
let monkeyLogoImage = require('../../images/monkey-icon.svg'); let monkeyLogoImage = require('../../images/monkey-icon.svg');
@ -413,9 +417,70 @@ class ReportPageComponent extends AuthComponent {
<div style={{marginBottom: '20px'}}> <div style={{marginBottom: '20px'}}>
<ScannedServers data={this.state.report.glance.scanned}/> <ScannedServers data={this.state.report.glance.scanned}/>
</div> </div>
<div> {this.generateReportPthMap()}
<div style={{marginBottom: '20px'}}>
<StolenPasswords data={this.state.report.glance.stolen_creds}/> <StolenPasswords data={this.state.report.glance.stolen_creds}/>
</div> </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> </div>
); );
} }

View File

@ -2,10 +2,7 @@ import React from 'react';
import ReactTable from 'react-table' import ReactTable from 'react-table'
let renderArray = function(val) { let renderArray = function(val) {
if (val.length === 0) { return <div>{val.map(x => <div>{x}</div>)}</div>;
return '';
}
return val.reduce((total, new_str) => total + ', ' + new_str);
}; };
const columns = [ const columns = [

View File

@ -2,10 +2,7 @@ import React from 'react';
import ReactTable from 'react-table' import ReactTable from 'react-table'
let renderArray = function(val) { let renderArray = function(val) {
if (val.length === 0) { return <div>{val.map(x => <div>{x}</div>)}</div>;
return '';
}
return val.reduce((total, new_str) => total + ', ' + new_str);
}; };
const columns = [ const columns = [

View File

@ -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;

View File

@ -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;

View File

@ -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