forked from p15670423/monkey
Added command display component and tabs to it
This commit is contained in:
parent
5eaed088d6
commit
522644238b
|
@ -5,6 +5,7 @@ import {Container} from 'react-bootstrap';
|
|||
import RunServerPage from 'components/pages/RunServerPage';
|
||||
import ConfigurePage from 'components/pages/ConfigurePage';
|
||||
import RunMonkeyPage from 'components/pages/RunMonkeyPage/RunMonkeyPage';
|
||||
import RunMonkeyPage2 from 'components/pages/RunMonkeyPage/RunMonkeyPage2';
|
||||
import MapPage from 'components/pages/MapPage';
|
||||
import TelemetryPage from 'components/pages/TelemetryPage';
|
||||
import StartOverPage from 'components/pages/StartOverPage';
|
||||
|
@ -29,7 +30,7 @@ const reportZeroTrustRoute = '/report/zeroTrust';
|
|||
|
||||
class AppComponent extends AuthComponent {
|
||||
updateStatus = () => {
|
||||
if (this.state.isLoggedIn === false){
|
||||
if (this.state.isLoggedIn === false) {
|
||||
return
|
||||
}
|
||||
this.auth.loggedIn()
|
||||
|
@ -148,6 +149,10 @@ class AppComponent extends AuthComponent {
|
|||
<StandardLayoutComponent component={RunMonkeyPage}
|
||||
onStatusChange={this.updateStatus}
|
||||
completedSteps={this.state.completedSteps}/>)}
|
||||
{this.renderRoute('/run-monkey2',
|
||||
<StandardLayoutComponent component={RunMonkeyPage2}
|
||||
onStatusChange={this.updateStatus}
|
||||
completedSteps={this.state.completedSteps}/>)}
|
||||
{this.renderRoute('/infection/map',
|
||||
<StandardLayoutComponent component={MapPage}
|
||||
onStatusChange={this.updateStatus}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import {Button, Card, Nav} from 'react-bootstrap';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faClipboard} from '@fortawesome/free-solid-svg-icons/faClipboard';
|
||||
import React, {useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function commandDisplay(props) {
|
||||
|
||||
const [selectedVariant, setSelectedVariant] = useState(props.commands[0].name);
|
||||
|
||||
function renderNav() {
|
||||
return (
|
||||
<Nav variant='pills' fill activeKey={selectedVariant} onSelect={setSelectedVariant}>
|
||||
{props.commands.map(command => {
|
||||
return (
|
||||
<Nav.Item key={command.name}>
|
||||
<Nav.Link eventKey={command.name}>{command.name}</Nav.Link>
|
||||
</Nav.Item>);
|
||||
})}
|
||||
</Nav>);
|
||||
}
|
||||
|
||||
function getCommandByName(name, commands) {
|
||||
commands.forEach((command) => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderNav()}
|
||||
<Card style={{'margin': '0.5em'}}>
|
||||
<div style={{'overflow': 'auto', 'padding': '0.5em'}}>
|
||||
<CopyToClipboard text={props.commands[0].name} className="pull-right btn-sm">
|
||||
<Button style={{margin: '-0.5em'}} title="Copy to Clipboard">
|
||||
<FontAwesomeIcon icon={faClipboard}/>
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
<code>{props.commands[0].command}</code>
|
||||
</div>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
commandDisplay.propTypes = {
|
||||
commands: PropTypes.arrayOf(PropTypes.exact({
|
||||
name: PropTypes.string,
|
||||
command: PropTypes.string
|
||||
}))
|
||||
}
|
|
@ -1,42 +1,57 @@
|
|||
import React, {useEffect} from 'react';
|
||||
import NextSelectionButton from '../../ui-components/inline-selection/NextSelectionButton';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
|
||||
import CommandSection from '../../ui-components/inline-selection/CommandSection';
|
||||
import ManualRunOptions from './ManualRunOptions';
|
||||
import InterfaceSelection from './InterfaceSelection';
|
||||
import DropdownSelect from '../../ui-components/DropdownSelect';
|
||||
import {OS_TYPES} from './OsTypes';
|
||||
import GenerateLocalWindowsCmd from './commands/local_windows_cmd';
|
||||
import GenerateLocalWindowsPowershell from './commands/local_windows_powershell';
|
||||
import GenerateLocalLinuxWget from './commands/local_linux_wget';
|
||||
import GenerateLocalLinuxCurl from './commands/local_linux_curl';
|
||||
import CommandDisplay from './CommandDisplay';
|
||||
|
||||
|
||||
const LocalManualRunOptions = (props) => {
|
||||
return InlineSelection(getContents, props, ManualRunOptions)
|
||||
}
|
||||
|
||||
const win64commands = [{name: "CMD", command: "monkey.exe m0nk3y -s 192.168.56.1"}]
|
||||
|
||||
const getContents = (props) => {
|
||||
|
||||
const osTypes = {
|
||||
[OS_TYPES.WINDOWS_64]: 'Windows 64bit',
|
||||
[OS_TYPES.WINDOWS_32]: 'Windows 32bit',
|
||||
[OS_TYPES.LINUX_64]: 'Linux 64bit',
|
||||
[OS_TYPES.LINUX_32]: 'Linux 32bit'
|
||||
}
|
||||
|
||||
const [osType, setOsType] = useState(OS_TYPES.WINDOWS_64);
|
||||
const [selectedIp, setSelectedIp] = useState(props.ips[0]);
|
||||
const [commands, setCommands] = useState(generateCommands());
|
||||
|
||||
useEffect(() => {
|
||||
setCommands(generateCommands());
|
||||
}, [osType, selectedIp])
|
||||
|
||||
function setIp(index) {
|
||||
setSelectedIp(props.ips[index]);
|
||||
}
|
||||
|
||||
function generateCommands() {
|
||||
if(osType === OS_TYPES.WINDOWS_64 || osType === OS_TYPES.WINDOWS_32) {
|
||||
return [{name: 'CMD', command: GenerateLocalWindowsCmd(selectedIp, osType)},
|
||||
{name: 'Powershell', command: GenerateLocalWindowsPowershell(selectedIp, osType)}]
|
||||
} else {
|
||||
return [{name: 'CURL', command: GenerateLocalLinuxCurl(selectedIp, osType)},
|
||||
{name: 'WGET', command: GenerateLocalLinuxWget(selectedIp, osType)}]
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSelectionButton text={'Windows 64bit'}
|
||||
onButtonClick={() => {
|
||||
props.setComponent(InterfaceSelection('Windows64'))
|
||||
}}/>
|
||||
<NextSelectionButton text={'Windows 32bit'} onButtonClick={() => {
|
||||
}}/>
|
||||
<NextSelectionButton text={'Linux 64bit'} onButtonClick={() => {
|
||||
}}/>
|
||||
<NextSelectionButton text={'Linux 32bit'} onButtonClick={() => {
|
||||
}}/>
|
||||
<DropdownSelect defaultKey={'win64'} options={osTypes} onClick={setOsType}/>
|
||||
<DropdownSelect defaultKey={0} options={props.ips} onClick={setIp}/>
|
||||
<CommandDisplay commands={commands}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const setCommandAsContent = (props) => {
|
||||
let commandComponent = () => InlineSelection(CommandSection,
|
||||
{
|
||||
commands: win64commands,
|
||||
setComponent: props.setComponent
|
||||
},
|
||||
LocalManualRunOptions
|
||||
);
|
||||
props.setComponent(commandComponent, props);
|
||||
}
|
||||
|
||||
export default LocalManualRunOptions;
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, {useEffect, useState} from 'react';
|
|||
import NextSelectionButton from '../../ui-components/inline-selection/NextSelectionButton';
|
||||
import LocalManualRunOptions from './LocalManualRunOptions';
|
||||
import AuthComponent from '../../AuthComponent';
|
||||
import BackButton from '../../ui-components/inline-selection/BackButton';
|
||||
|
||||
function ManualRunOptions(props) {
|
||||
|
||||
|
@ -16,7 +17,7 @@ function ManualRunOptions(props) {
|
|||
authComponent.authFetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
setIps([res['ip_addresses']]);
|
||||
setIps([res['ip_addresses']][0]);
|
||||
setInitialized(true);
|
||||
});
|
||||
}
|
||||
|
@ -48,6 +49,7 @@ function ManualRunOptions(props) {
|
|||
}}/>
|
||||
<NextSelectionButton text={'Automation'} onButtonClick={() => {
|
||||
}}/>
|
||||
<BackButton onClick={props.disableManualOptions} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export const OS_TYPES = {
|
||||
WINDOWS_32: 'win32',
|
||||
WINDOWS_64: 'win64',
|
||||
LINUX_32: 'linux32',
|
||||
LINUX_64: 'linux64'
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
import React from 'react';
|
||||
import {css} from '@emotion/core';
|
||||
import {Button, Col, Card, Nav, Collapse, Row} from 'react-bootstrap';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import GridLoader from 'react-spinners/GridLoader';
|
||||
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faClipboard} from '@fortawesome/free-solid-svg-icons/faClipboard';
|
||||
import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
|
||||
import {faSync} from '@fortawesome/free-solid-svg-icons/faSync';
|
||||
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
|
||||
|
@ -17,6 +15,7 @@ import AwsRunTable from '../../run-monkey/AwsRunTable';
|
|||
|
||||
import MissingBinariesModal from '../../ui-components/MissingBinariesModal';
|
||||
import ManualRunOptions from './ManualRunOptions';
|
||||
import Emoji from '../../ui-components/Emoji';
|
||||
|
||||
const loading_css_override = css`
|
||||
display: block;
|
||||
|
@ -29,12 +28,9 @@ class RunMonkeyPageComponent extends AuthComponent {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
ips: [],
|
||||
runningOnIslandState: 'not_running',
|
||||
runningOnClientState: 'not_running',
|
||||
awsClicked: false,
|
||||
selectedIp: '0.0.0.0',
|
||||
selectedOs: 'windows-32',
|
||||
showManual: false,
|
||||
showAws: false,
|
||||
isOnAws: false,
|
||||
|
@ -52,13 +48,6 @@ class RunMonkeyPageComponent extends AuthComponent {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.authFetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({
|
||||
ips: res['ip_addresses'],
|
||||
selectedIp: res['ip_addresses'][0]
|
||||
}));
|
||||
|
||||
this.authFetch('/api/local-monkey')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
|
@ -113,17 +102,7 @@ class RunMonkeyPageComponent extends AuthComponent {
|
|||
});
|
||||
}
|
||||
|
||||
static generateLinuxCmd(ip, is32Bit) {
|
||||
let bitText = is32Bit ? '32' : '64';
|
||||
return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/monkey-linux-${bitText}; chmod +x monkey-linux-${bitText}; ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`
|
||||
}
|
||||
|
||||
static generateWindowsCmd(ip, is32Bit) {
|
||||
let bitText = is32Bit ? '32' : '64';
|
||||
return `powershell [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; (New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/monkey-windows-${bitText}.exe','.\\monkey.exe'); ;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
|
||||
}
|
||||
|
||||
runLocalMonkey = () => {
|
||||
runIslandMonkey = () => {
|
||||
this.authFetch('/api/local-monkey',
|
||||
{
|
||||
method: 'POST',
|
||||
|
@ -154,41 +133,6 @@ class RunMonkeyPageComponent extends AuthComponent {
|
|||
});
|
||||
};
|
||||
|
||||
generateCmdDiv() {
|
||||
let isLinux = (this.state.selectedOs.split('-')[0] === 'linux');
|
||||
let is32Bit = (this.state.selectedOs.split('-')[1] === '32');
|
||||
let cmdText = '';
|
||||
if (isLinux) {
|
||||
cmdText = RunMonkeyPageComponent.generateLinuxCmd(this.state.selectedIp, is32Bit);
|
||||
} else {
|
||||
cmdText = RunMonkeyPageComponent.generateWindowsCmd(this.state.selectedIp, is32Bit);
|
||||
}
|
||||
return (
|
||||
<Card key={'cmdDiv' + this.state.selectedIp} style={{'margin': '0.5em'}}>
|
||||
<div style={{'overflow': 'auto', 'padding': '0.5em'}}>
|
||||
<CopyToClipboard text={cmdText} className="pull-right btn-sm">
|
||||
<Button style={{margin: '-0.5em'}} title="Copy to Clipboard">
|
||||
<FontAwesomeIcon icon={faClipboard}/>
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
<code>{cmdText}</code>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
setSelectedOs = (key) => {
|
||||
this.setState({
|
||||
selectedOs: key
|
||||
});
|
||||
};
|
||||
|
||||
setSelectedIp = (key) => {
|
||||
this.setState({
|
||||
selectedIp: key
|
||||
});
|
||||
};
|
||||
|
||||
static renderIconByState(state) {
|
||||
if (state === 'running') {
|
||||
return (<FontAwesomeIcon icon={faCheck} className="text-success" style={{'marginLeft': '5px'}}/>)
|
||||
|
@ -274,14 +218,6 @@ class RunMonkeyPageComponent extends AuthComponent {
|
|||
rel="noopener noreferrer" target="_blank">Read the documentation</a>!
|
||||
</p>
|
||||
</div>
|
||||
{
|
||||
this.state.ips.length > 1 ?
|
||||
<Nav variant="pills" activeKey={this.state.selectedIp} onSelect={this.setSelectedIp}
|
||||
style={{'marginBottom': '2em'}}>
|
||||
{this.state.ips.map(ip => <Nav.Item key={ip}><Nav.Link eventKey={ip}>{ip}</Nav.Link></Nav.Item>)}
|
||||
</Nav>
|
||||
: <div style={{'marginBottom': '2em'}}/>
|
||||
}
|
||||
|
||||
<AwsRunTable
|
||||
data={this.state.awsMachines}
|
||||
|
@ -307,23 +243,16 @@ class RunMonkeyPageComponent extends AuthComponent {
|
|||
})
|
||||
};
|
||||
|
||||
render() {
|
||||
renderIslandVsManual = () => {
|
||||
return (
|
||||
<Col sm={{offset: 3, span: 9}} md={{offset: 3, span: 9}}
|
||||
lg={{offset: 3, span: 9}} xl={{offset: 2, span: 7}}
|
||||
className={'main'}>
|
||||
<h1 className="page-title">1. Run Monkey</h1>
|
||||
<p style={{'marginBottom': '2em', 'fontSize': '1.2em'}}>
|
||||
Go ahead and run the monkey!
|
||||
<i> (Or <Link to="/configure">configure the monkey</Link> to fine tune its behavior)</i>
|
||||
</p>
|
||||
<>
|
||||
<p className={'text-center'}>
|
||||
<Button onClick={this.runLocalMonkey}
|
||||
<Button onClick={this.runIslandMonkey}
|
||||
variant={'outline-monkey'}
|
||||
size='lg'
|
||||
disabled={this.state.runningOnIslandState !== 'not_running'}
|
||||
>
|
||||
Run on Monkey Island Server
|
||||
<Emoji symbol={"🏝️"}/>Run on Monkey Island Server
|
||||
{RunMonkeyPageComponent.renderIconByState(this.state.runningOnIslandState)}
|
||||
</Button>
|
||||
<MissingBinariesModal
|
||||
|
@ -334,76 +263,39 @@ class RunMonkeyPageComponent extends AuthComponent {
|
|||
<p className="text-center">
|
||||
OR
|
||||
</p>
|
||||
<ManualRunOptions />
|
||||
<p className={'text-center'}
|
||||
style={this.state.showManual || !this.state.isOnAws ? {'marginBottom': '2em'} : {}}>
|
||||
<Button onClick={this.toggleManual}
|
||||
variant={'outline-monkey'}
|
||||
size='lg'
|
||||
className={(this.state.showManual ? 'active' : '')}>
|
||||
Run on a machine of your choice
|
||||
<Emoji symbol={"💻"}/> Run on a machine of your choice
|
||||
</Button>
|
||||
</p>
|
||||
<Collapse in={this.state.showManual}>
|
||||
<div style={{'marginBottom': '2em'}}>
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Choose the operating system where you want to run the monkey:
|
||||
</p>
|
||||
<Row>
|
||||
<Col>
|
||||
<Nav variant='pills' fill id={'bootstrap-override'} className={'run-on-os-buttons'}
|
||||
activeKey={this.state.selectedOs} onSelect={this.setSelectedOs}>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey={'windows-32'}>
|
||||
Windows (32 bit)
|
||||
</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey='windows-64'>
|
||||
Windows (64 bit)
|
||||
</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey='linux-32'>
|
||||
Linux (32 bit)
|
||||
</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey='linux-64'>
|
||||
Linux (64 bit)
|
||||
</Nav.Link>
|
||||
</Nav.Item>
|
||||
</Nav>
|
||||
</Col>
|
||||
</Row>
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Go ahead and monitor the ongoing infection in the <Link to="/infection/map">Infection Map</Link> view.
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
{this.state.ips.length > 1 ?
|
||||
<div>
|
||||
<Row>
|
||||
<Col>
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Choose the interface to communicate with:
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<Nav variant="pills" fill activeKey={this.state.selectedIp} onSelect={this.setSelectedIp}
|
||||
className={'run-on-os-buttons'}>
|
||||
{this.state.ips.map(ip => <Nav.Item key={ip}>
|
||||
<Nav.Link eventKey={ip}>{ip}</Nav.Link></Nav.Item>)}
|
||||
</Nav>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
: <div style={{'marginBottom': '2em'}}/>
|
||||
}
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Copy the following command to your machine and run it with Administrator or root privileges.
|
||||
</p>
|
||||
{this.generateCmdDiv()}
|
||||
</div>
|
||||
</Collapse>
|
||||
disableManualOptions = () => {
|
||||
this.setState({showManual: false})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col sm={{offset: 3, span: 9}} md={{offset: 3, span: 9}}
|
||||
lg={{offset: 3, span: 9}} xl={{offset: 2, span: 7}}
|
||||
className={'main'}>
|
||||
<h1 className="page-title">1. Run Monkey</h1>
|
||||
<p style={{'marginBottom': '2em', 'fontSize': '1.2em'}}>
|
||||
Go ahead and run the monkey!
|
||||
<i> (Or <Link to="/configure">configure the monkey</Link> to fine tune its behavior)</i>
|
||||
</p>
|
||||
{this.state.showManual ?
|
||||
<ManualRunOptions disableManualOptions={this.disableManualOptions}/> :
|
||||
this.renderIslandVsManual()}
|
||||
{
|
||||
this.state.isLoadingAws ?
|
||||
<div style={{'marginBottom': '2em', 'align': 'center'}}>
|
||||
|
@ -459,10 +351,6 @@ class RunMonkeyPageComponent extends AuthComponent {
|
|||
}
|
||||
|
||||
</Collapse>
|
||||
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Go ahead and monitor the ongoing infection in the <Link to="/infection/map">Infection Map</Link> view.
|
||||
</p>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,472 @@
|
|||
import React from 'react';
|
||||
import {css} from '@emotion/core';
|
||||
import {Button, Col, Card, Nav, Collapse, Row} from 'react-bootstrap';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import GridLoader from 'react-spinners/GridLoader';
|
||||
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faClipboard} from '@fortawesome/free-solid-svg-icons/faClipboard';
|
||||
import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
|
||||
import {faSync} from '@fortawesome/free-solid-svg-icons/faSync';
|
||||
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
|
||||
import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
|
||||
|
||||
import {Link} from 'react-router-dom';
|
||||
import AuthComponent from '../../AuthComponent';
|
||||
import AwsRunTable from '../../run-monkey/AwsRunTable';
|
||||
|
||||
import MissingBinariesModal from '../../ui-components/MissingBinariesModal';
|
||||
import ManualRunOptions from './ManualRunOptions';
|
||||
import Emoji from '../../ui-components/Emoji';
|
||||
|
||||
const loading_css_override = css`
|
||||
display: block;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
class RunMonkeyPageComponent2 extends AuthComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
ips: [],
|
||||
runningOnIslandState: 'not_running',
|
||||
runningOnClientState: 'not_running',
|
||||
awsClicked: false,
|
||||
selectedIp: '0.0.0.0',
|
||||
selectedOs: 'windows-32',
|
||||
showManual: false,
|
||||
showAws: false,
|
||||
isOnAws: false,
|
||||
awsUpdateClicked: false,
|
||||
awsUpdateFailed: false,
|
||||
awsMachines: [],
|
||||
isLoadingAws: true,
|
||||
isErrorWhileCollectingAwsMachines: false,
|
||||
awsMachineCollectionErrorMsg: '',
|
||||
showModal: false,
|
||||
errorDetails: ''
|
||||
};
|
||||
|
||||
this.closeModal = this.closeModal.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.authFetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => this.setState({
|
||||
ips: res['ip_addresses'],
|
||||
selectedIp: res['ip_addresses'][0]
|
||||
}));
|
||||
|
||||
this.authFetch('/api/local-monkey')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res['is_running']) {
|
||||
this.setState({runningOnIslandState: 'running'});
|
||||
} else {
|
||||
this.setState({runningOnIslandState: 'not_running'});
|
||||
}
|
||||
});
|
||||
|
||||
this.fetchAwsInfo();
|
||||
this.fetchConfig();
|
||||
|
||||
this.authFetch('/api/client-monkey')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res['is_running']) {
|
||||
this.setState({runningOnClientState: 'running'});
|
||||
} else {
|
||||
this.setState({runningOnClientState: 'not_running'});
|
||||
}
|
||||
});
|
||||
|
||||
this.props.onStatusChange();
|
||||
}
|
||||
|
||||
fetchAwsInfo() {
|
||||
return this.authFetch('/api/remote-monkey?action=list_aws')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
let is_aws = res['is_aws'];
|
||||
if (is_aws) {
|
||||
// On AWS!
|
||||
// Checks if there was an error while collecting the aws machines.
|
||||
let is_error_while_collecting_aws_machines = (res['error'] != null);
|
||||
if (is_error_while_collecting_aws_machines) {
|
||||
// There was an error. Finish loading, and display error message.
|
||||
this.setState({
|
||||
isOnAws: true,
|
||||
isErrorWhileCollectingAwsMachines: true,
|
||||
awsMachineCollectionErrorMsg: res['error'],
|
||||
isLoadingAws: false
|
||||
});
|
||||
} else {
|
||||
// No error! Finish loading and display machines for user
|
||||
this.setState({isOnAws: true, awsMachines: res['instances'], isLoadingAws: false});
|
||||
}
|
||||
} else {
|
||||
// Not on AWS. Finish loading and don't display the AWS div.
|
||||
this.setState({isOnAws: false, isLoadingAws: false});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static generateLinuxCmd(ip, is32Bit) {
|
||||
let bitText = is32Bit ? '32' : '64';
|
||||
return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/monkey-linux-${bitText}; chmod +x monkey-linux-${bitText}; ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`
|
||||
}
|
||||
|
||||
static generateWindowsCmd(ip, is32Bit) {
|
||||
let bitText = is32Bit ? '32' : '64';
|
||||
return `powershell [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; (New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/monkey-windows-${bitText}.exe','.\\monkey.exe'); ;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
|
||||
}
|
||||
|
||||
runLocalMonkey = () => {
|
||||
this.authFetch('/api/local-monkey',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({action: 'run'})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res['is_running']) {
|
||||
this.setState({
|
||||
runningOnIslandState: 'installing'
|
||||
});
|
||||
} else {
|
||||
/* If Monkey binaries are missing, change the state accordingly */
|
||||
if (res['error_text'].startsWith('Copy file failed')) {
|
||||
this.setState({
|
||||
showModal: true,
|
||||
errorDetails: res['error_text']
|
||||
}
|
||||
);
|
||||
}
|
||||
this.setState({
|
||||
runningOnIslandState: 'not_running'
|
||||
});
|
||||
}
|
||||
|
||||
this.props.onStatusChange();
|
||||
});
|
||||
};
|
||||
|
||||
generateCmdDiv() {
|
||||
let isLinux = (this.state.selectedOs.split('-')[0] === 'linux');
|
||||
let is32Bit = (this.state.selectedOs.split('-')[1] === '32');
|
||||
let cmdText = '';
|
||||
if (isLinux) {
|
||||
cmdText = RunMonkeyPageComponent2.generateLinuxCmd(this.state.selectedIp, is32Bit);
|
||||
} else {
|
||||
cmdText = RunMonkeyPageComponent2.generateWindowsCmd(this.state.selectedIp, is32Bit);
|
||||
}
|
||||
return (
|
||||
<Card key={'cmdDiv' + this.state.selectedIp} style={{'margin': '0.5em'}}>
|
||||
<div style={{'overflow': 'auto', 'padding': '0.5em'}}>
|
||||
<CopyToClipboard text={cmdText} className="pull-right btn-sm">
|
||||
<Button style={{margin: '-0.5em'}} title="Copy to Clipboard">
|
||||
<FontAwesomeIcon icon={faClipboard}/>
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
<code>{cmdText}</code>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
setSelectedOs = (key) => {
|
||||
this.setState({
|
||||
selectedOs: key
|
||||
});
|
||||
};
|
||||
|
||||
setSelectedIp = (key) => {
|
||||
this.setState({
|
||||
selectedIp: key
|
||||
});
|
||||
};
|
||||
|
||||
static renderIconByState(state) {
|
||||
if (state === 'running') {
|
||||
return (<FontAwesomeIcon icon={faCheck} className="text-success" style={{'marginLeft': '5px'}}/>)
|
||||
} else if (state === 'installing') {
|
||||
return (<FontAwesomeIcon icon={faSync} className="text-success" style={{'marginLeft': '5px'}}/>)
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
toggleManual = () => {
|
||||
this.setState({
|
||||
showManual: !this.state.showManual
|
||||
});
|
||||
};
|
||||
|
||||
toggleAws = () => {
|
||||
this.setState({
|
||||
showAws: !this.state.showAws
|
||||
});
|
||||
};
|
||||
|
||||
runOnAws = () => {
|
||||
this.setState({
|
||||
awsClicked: true
|
||||
});
|
||||
|
||||
let instances = this.awsTable.state.selection.map(x => this.instanceIdToInstance(x));
|
||||
|
||||
this.authFetch('/api/remote-monkey',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({type: 'aws', instances: instances, island_ip: this.state.selectedIp})
|
||||
}).then(res => res.json())
|
||||
.then(res => {
|
||||
let result = res['result'];
|
||||
|
||||
// update existing state, not run-over
|
||||
let prevRes = this.awsTable.state.result;
|
||||
for (let key in result) {
|
||||
if (result.hasOwnProperty(key)) {
|
||||
prevRes[key] = result[key];
|
||||
}
|
||||
}
|
||||
this.awsTable.setState({
|
||||
result: prevRes,
|
||||
selection: [],
|
||||
selectAll: false
|
||||
});
|
||||
|
||||
this.setState({
|
||||
awsClicked: false
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
fetchConfig() {
|
||||
return this.authFetch('/api/configuration/island')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
return res.configuration;
|
||||
})
|
||||
}
|
||||
|
||||
instanceIdToInstance = (instance_id) => {
|
||||
let instance = this.state.awsMachines.find(
|
||||
function (inst) {
|
||||
return inst['instance_id'] === instance_id;
|
||||
});
|
||||
return {'instance_id': instance_id, 'os': instance['os']}
|
||||
|
||||
};
|
||||
|
||||
renderAwsMachinesDiv() {
|
||||
return (
|
||||
<div style={{'marginBottom': '2em'}}>
|
||||
<div style={{'marginTop': '1em', 'marginBottom': '1em'}}>
|
||||
<p className="alert alert-info">
|
||||
<FontAwesomeIcon icon={faInfoCircle} style={{'marginRight': '5px'}}/>
|
||||
Not sure what this is? Not seeing your AWS EC2 instances? <a
|
||||
href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances"
|
||||
rel="noopener noreferrer" target="_blank">Read the documentation</a>!
|
||||
</p>
|
||||
</div>
|
||||
{
|
||||
this.state.ips.length > 1 ?
|
||||
<Nav variant="pills" activeKey={this.state.selectedIp} onSelect={this.setSelectedIp}
|
||||
style={{'marginBottom': '2em'}}>
|
||||
{this.state.ips.map(ip => <Nav.Item key={ip}><Nav.Link eventKey={ip}>{ip}</Nav.Link></Nav.Item>)}
|
||||
</Nav>
|
||||
: <div style={{'marginBottom': '2em'}}/>
|
||||
}
|
||||
|
||||
<AwsRunTable
|
||||
data={this.state.awsMachines}
|
||||
ref={r => (this.awsTable = r)}
|
||||
/>
|
||||
<div style={{'marginTop': '1em'}}>
|
||||
<Button
|
||||
onClick={this.runOnAws}
|
||||
className={'btn btn-default btn-md center-block'}
|
||||
disabled={this.state.awsClicked}>
|
||||
Run on selected machines
|
||||
{this.state.awsClicked ?
|
||||
<FontAwesomeIcon icon={faSync} className="text-success" style={{'marginLeft': '5px'}}/> : null}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
closeModal = () => {
|
||||
this.setState({
|
||||
showModal: false
|
||||
})
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Col sm={{offset: 3, span: 9}} md={{offset: 3, span: 9}}
|
||||
lg={{offset: 3, span: 9}} xl={{offset: 2, span: 7}}
|
||||
className={'main'}>
|
||||
<h1 className="page-title">1. Run Monkey</h1>
|
||||
<p style={{'marginBottom': '2em', 'fontSize': '1.2em'}}>
|
||||
Go ahead and run the monkey!
|
||||
<i> (Or <Link to="/configure">configure the monkey</Link> to fine tune its behavior)</i>
|
||||
</p>
|
||||
<p className={'text-center'}>
|
||||
<Button onClick={this.runLocalMonkey}
|
||||
variant={'outline-monkey'}
|
||||
size='lg'
|
||||
disabled={this.state.runningOnIslandState !== 'not_running'}
|
||||
>
|
||||
<Emoji symbol={"🏝️"}/>Run on Monkey Island Server
|
||||
{RunMonkeyPageComponent2.renderIconByState(this.state.runningOnIslandState)}
|
||||
</Button>
|
||||
<MissingBinariesModal
|
||||
showModal={this.state.showModal}
|
||||
onClose={this.closeModal}
|
||||
errorDetails={this.state.errorDetails}/>
|
||||
</p>
|
||||
<p className="text-center">
|
||||
OR
|
||||
</p>
|
||||
<ManualRunOptions />
|
||||
<p className={'text-center'}
|
||||
style={this.state.showManual || !this.state.isOnAws ? {'marginBottom': '2em'} : {}}>
|
||||
<Button onClick={this.toggleManual}
|
||||
variant={'outline-monkey'}
|
||||
size='lg'
|
||||
className={(this.state.showManual ? 'active' : '')}>
|
||||
<Emoji symbol={"💻"} /> Run on a machine of your choice
|
||||
</Button>
|
||||
</p>
|
||||
<Collapse in={this.state.showManual}>
|
||||
<div style={{'marginBottom': '2em'}}>
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Choose the operating system where you want to run the monkey:
|
||||
</p>
|
||||
<Row>
|
||||
<Col>
|
||||
<Nav variant='pills' fill id={'bootstrap-override'} className={'run-on-os-buttons'}
|
||||
activeKey={this.state.selectedOs} onSelect={this.setSelectedOs}>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey={'windows-32'}>
|
||||
Windows (32 bit)
|
||||
</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey='windows-64'>
|
||||
Windows (64 bit)
|
||||
</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey='linux-32'>
|
||||
Linux (32 bit)
|
||||
</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey='linux-64'>
|
||||
Linux (64 bit)
|
||||
</Nav.Link>
|
||||
</Nav.Item>
|
||||
</Nav>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{this.state.ips.length > 1 ?
|
||||
<div>
|
||||
<Row>
|
||||
<Col>
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Choose the interface to communicate with:
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<Nav variant="pills" fill activeKey={this.state.selectedIp} onSelect={this.setSelectedIp}
|
||||
className={'run-on-os-buttons'}>
|
||||
{this.state.ips.map(ip => <Nav.Item key={ip}>
|
||||
<Nav.Link eventKey={ip}>{ip}</Nav.Link></Nav.Item>)}
|
||||
</Nav>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
: <div style={{'marginBottom': '2em'}}/>
|
||||
}
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Copy the following command to your machine and run it with Administrator or root privileges.
|
||||
</p>
|
||||
{this.generateCmdDiv()}
|
||||
</div>
|
||||
</Collapse>
|
||||
{
|
||||
this.state.isLoadingAws ?
|
||||
<div style={{'marginBottom': '2em', 'align': 'center'}}>
|
||||
<div className='sweet-loading'>
|
||||
<GridLoader
|
||||
css={loading_css_override}
|
||||
sizeUnit={'px'}
|
||||
size={30}
|
||||
color={'#ffcc00'}
|
||||
loading={this.state.loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
{
|
||||
this.state.isOnAws ?
|
||||
<p className="text-center">
|
||||
OR
|
||||
</p>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
this.state.isOnAws ?
|
||||
<p style={{'marginBottom': '2em'}} className={'text-center'}>
|
||||
<Button onClick={this.toggleAws}
|
||||
className={(this.state.showAws ? ' active' : '')}
|
||||
size='lg'
|
||||
variant={'outline-monkey'}>
|
||||
Run on AWS machine of your choice
|
||||
</Button>
|
||||
</p>
|
||||
:
|
||||
null
|
||||
}
|
||||
<Collapse in={this.state.showAws}>
|
||||
{
|
||||
this.state.isErrorWhileCollectingAwsMachines ?
|
||||
<div style={{'marginTop': '1em'}}>
|
||||
<p className="alert alert-danger">
|
||||
<FontAwesomeIcon icon={faExclamationTriangle} style={{'marginRight': '5px'}}/>
|
||||
Error while collecting AWS machine data. Error
|
||||
message: <code>{this.state.awsMachineCollectionErrorMsg}</code><br/>
|
||||
Are you sure you've set the correct role on your Island AWS machine?<br/>
|
||||
Not sure what this is? <a
|
||||
href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances">Read
|
||||
the documentation</a>!
|
||||
</p>
|
||||
</div>
|
||||
:
|
||||
this.renderAwsMachinesDiv()
|
||||
}
|
||||
|
||||
</Collapse>
|
||||
|
||||
<p style={{'fontSize': '1.2em'}}>
|
||||
Go ahead and monitor the ongoing infection in the <Link to="/infection/map">Infection Map</Link> view.
|
||||
</p>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RunMonkeyPageComponent2;
|
|
@ -0,0 +1,13 @@
|
|||
import {OS_TYPES} from '../OsTypes';
|
||||
|
||||
|
||||
export default function generateLocalLinuxCurl(ip, osType) {
|
||||
let bitText = osType === OS_TYPES.LINUX_32 ? '32' : '64';
|
||||
return `curl https://${ip}:5000/api/monkey/download/monkey-linux-${bitText} -k
|
||||
-o monkey-linux-${bitText};
|
||||
chmod +x monkey-linux-${bitText};
|
||||
./monkey-linux-${bitText} m0nk3y -s ${ip}:5000\`;`;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import {OS_TYPES} from '../OsTypes';
|
||||
|
||||
|
||||
export default function generateLocalLinuxWget(ip, osType) {
|
||||
let bitText = osType === OS_TYPES.LINUX_32 ? '32' : '64';
|
||||
return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/
|
||||
monkey-linux-${bitText};
|
||||
chmod +x monkey-linux-${bitText};
|
||||
./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import {OS_TYPES} from '../OsTypes';
|
||||
|
||||
|
||||
export default function generateLocalWindowsCmd(ip, osType) {
|
||||
let bitText = osType === OS_TYPES.WINDOWS_32 ? '32' : '64';
|
||||
return `powershell [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};
|
||||
(New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/
|
||||
monkey-windows-${bitText}.exe','.\\monkey.exe');
|
||||
;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import {OS_TYPES} from '../OsTypes';
|
||||
|
||||
|
||||
export default function generateLocalWindowsPowershell(ip, osType) {
|
||||
let bitText = osType === OS_TYPES.WINDOWS_32 ? '32' : '64';
|
||||
return `[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};
|
||||
(New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/
|
||||
monkey-windows-${bitText}.exe','.\\monkey.exe');
|
||||
;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import React, {useState} from 'react';
|
||||
import {Dropdown} from 'react-bootstrap';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function DropdownSelect(props) {
|
||||
const [selectedOption, setSelectedOption] = useState(props.defaultKey);
|
||||
|
||||
function generateDropdownItems(data) {
|
||||
if (Array.isArray(data)) {
|
||||
return generateDropdownItemsFromArray(data);
|
||||
} else if (typeof data === 'object') {
|
||||
return generateDropdownItemsFromObject(data);
|
||||
} else {
|
||||
throw "Component can only generate dropdown intems from lists and objects."
|
||||
}
|
||||
}
|
||||
|
||||
function generateDropdownItemsFromArray(data) {
|
||||
const dropdownItems = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
dropdownItems.push(generateDropdownItem(i, data[i]));
|
||||
}
|
||||
return dropdownItems;
|
||||
}
|
||||
|
||||
function generateDropdownItemsFromObject(data) {
|
||||
const dropdownItems = [];
|
||||
for (let [key, value] of Object.entries(data)) {
|
||||
dropdownItems.push(generateDropdownItem(key, value));
|
||||
}
|
||||
return dropdownItems;
|
||||
}
|
||||
|
||||
function generateDropdownItem(key, value) {
|
||||
return (
|
||||
<Dropdown.Item onClick={() => { setSelectedOption(key);
|
||||
props.onClick(key)}}
|
||||
active={(key === selectedOption)}>
|
||||
{value}
|
||||
</Dropdown.Item>);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle variant="success" id="dropdown-basic">
|
||||
{props.options[selectedOption]}
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
{generateDropdownItems(props.options)}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
DropdownSelect.propTypes = {
|
||||
options: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
defaultKey: PropTypes.oneOfType([PropTypes.string,PropTypes.number]),
|
||||
onClick: PropTypes.func
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import React from 'react';
|
||||
const Emoji = props => (
|
||||
<span
|
||||
className="emoji"
|
||||
role="img"
|
||||
aria-label={props.label ? props.label : ""}
|
||||
aria-hidden={props.label ? "false" : "true"}
|
||||
>
|
||||
{props.symbol}
|
||||
</span>
|
||||
);
|
||||
export default Emoji;
|
|
@ -18,7 +18,7 @@ function setPreviousComponent(props, previousComponent) {
|
|||
if(previousComponent === ManualRunOptions){
|
||||
return props.setComponent()
|
||||
} else {
|
||||
return props.setComponent(previousComponent)
|
||||
return props.setComponent(previousComponent, props)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue