forked from p15670423/monkey
Merge pull request #1360 from guardicore/manual_executions_endpoint
Manual executions endpoint
This commit is contained in:
commit
d4d055ed95
|
@ -0,0 +1,3 @@
|
||||||
|
# Default time format used in the application, follows European standard.
|
||||||
|
# Example: 1992-03-04 10:32:05
|
||||||
|
DEFAULT_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
|
from datetime import datetime
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
@ -11,6 +12,7 @@ from requests.exceptions import ConnectionError
|
||||||
import infection_monkey.monkeyfs as monkeyfs
|
import infection_monkey.monkeyfs as monkeyfs
|
||||||
import infection_monkey.tunnel as tunnel
|
import infection_monkey.tunnel as tunnel
|
||||||
from common.common_consts.api_url_consts import T1216_PBA_FILE_DOWNLOAD_PATH
|
from common.common_consts.api_url_consts import T1216_PBA_FILE_DOWNLOAD_PATH
|
||||||
|
from common.common_consts.time_formats import DEFAULT_TIME_FORMAT
|
||||||
from common.common_consts.timeouts import (
|
from common.common_consts.timeouts import (
|
||||||
LONG_REQUEST_TIMEOUT,
|
LONG_REQUEST_TIMEOUT,
|
||||||
MEDIUM_REQUEST_TIMEOUT,
|
MEDIUM_REQUEST_TIMEOUT,
|
||||||
|
@ -60,6 +62,7 @@ class ControlClient(object):
|
||||||
"internet_access": has_internet_access,
|
"internet_access": has_internet_access,
|
||||||
"config": WormConfiguration.as_dict(),
|
"config": WormConfiguration.as_dict(),
|
||||||
"parent": parent,
|
"parent": parent,
|
||||||
|
"launch_time": str(datetime.now().strftime(DEFAULT_TIME_FORMAT)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ControlClient.proxies:
|
if ControlClient.proxies:
|
||||||
|
|
|
@ -24,6 +24,7 @@ from monkey_island.cc.resources.configuration_export import ConfigurationExport
|
||||||
from monkey_island.cc.resources.configuration_import import ConfigurationImport
|
from monkey_island.cc.resources.configuration_import import ConfigurationImport
|
||||||
from monkey_island.cc.resources.edge import Edge
|
from monkey_island.cc.resources.edge import Edge
|
||||||
from monkey_island.cc.resources.environment import Environment
|
from monkey_island.cc.resources.environment import Environment
|
||||||
|
from monkey_island.cc.resources.exploitations.manual_exploitation import ManualExploitation
|
||||||
from monkey_island.cc.resources.island_configuration import IslandConfiguration
|
from monkey_island.cc.resources.island_configuration import IslandConfiguration
|
||||||
from monkey_island.cc.resources.island_logs import IslandLog
|
from monkey_island.cc.resources.island_logs import IslandLog
|
||||||
from monkey_island.cc.resources.island_mode import IslandMode
|
from monkey_island.cc.resources.island_mode import IslandMode
|
||||||
|
@ -154,6 +155,7 @@ def init_api_resources(api):
|
||||||
api.add_resource(ZeroTrustReport, "/api/report/zero-trust/<string:report_data>")
|
api.add_resource(ZeroTrustReport, "/api/report/zero-trust/<string:report_data>")
|
||||||
api.add_resource(AttackReport, "/api/report/attack")
|
api.add_resource(AttackReport, "/api/report/attack")
|
||||||
api.add_resource(RansomwareReport, "/api/report/ransomware")
|
api.add_resource(RansomwareReport, "/api/report/ransomware")
|
||||||
|
api.add_resource(ManualExploitation, "/api/exploitations/manual")
|
||||||
|
|
||||||
api.add_resource(ZeroTrustFindingEvent, "/api/zero-trust/finding-event/<string:finding_id>")
|
api.add_resource(ZeroTrustFindingEvent, "/api/zero-trust/finding-event/<string:finding_id>")
|
||||||
api.add_resource(TelemetryFeed, "/api/telemetry-feed", "/api/telemetry-feed/")
|
api.add_resource(TelemetryFeed, "/api/telemetry-feed", "/api/telemetry-feed/")
|
||||||
|
|
|
@ -40,6 +40,7 @@ class Monkey(Document):
|
||||||
hostname = StringField()
|
hostname = StringField()
|
||||||
internet_access = BooleanField()
|
internet_access = BooleanField()
|
||||||
ip_addresses = ListField(StringField())
|
ip_addresses = ListField(StringField())
|
||||||
|
launch_time = StringField()
|
||||||
keepalive = DateTimeField()
|
keepalive = DateTimeField()
|
||||||
modifytime = DateTimeField()
|
modifytime = DateTimeField()
|
||||||
# TODO make "parent" an embedded document, so this can be removed and the schema explained (
|
# TODO make "parent" an embedded document, so this can be removed and the schema explained (
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import flask_restful
|
||||||
|
|
||||||
|
from monkey_island.cc.resources.auth.auth import jwt_required
|
||||||
|
from monkey_island.cc.services.exploitations.manual_exploitation import get_manual_exploitations
|
||||||
|
|
||||||
|
|
||||||
|
class ManualExploitation(flask_restful.Resource):
|
||||||
|
@jwt_required
|
||||||
|
def get(self):
|
||||||
|
manual_exploitations = [
|
||||||
|
exploitation.__dict__ for exploitation in get_manual_exploitations()
|
||||||
|
]
|
||||||
|
return {"manual_exploitations": manual_exploitations}
|
|
@ -0,0 +1,31 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from monkey_island.cc.database import mongo
|
||||||
|
from monkey_island.cc.services.node import NodeService
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ManualExploitation:
|
||||||
|
hostname: str
|
||||||
|
ip_addresses: List[str]
|
||||||
|
start_time: str
|
||||||
|
|
||||||
|
|
||||||
|
def get_manual_exploitations() -> List[ManualExploitation]:
|
||||||
|
monkeys = get_manual_monkeys()
|
||||||
|
return [monkey_to_manual_exploitation(monkey) for monkey in monkeys]
|
||||||
|
|
||||||
|
|
||||||
|
def get_manual_monkeys():
|
||||||
|
return [
|
||||||
|
monkey for monkey in mongo.db.monkey.find({}) if NodeService.get_monkey_manual_run(monkey)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def monkey_to_manual_exploitation(monkey: dict) -> ManualExploitation:
|
||||||
|
return ManualExploitation(
|
||||||
|
hostname=monkey["hostname"],
|
||||||
|
ip_addresses=monkey["ip_addresses"],
|
||||||
|
start_time=monkey["launch_time"],
|
||||||
|
)
|
|
@ -21,6 +21,7 @@ from monkey_island.cc.services.config import ConfigService
|
||||||
from monkey_island.cc.services.configuration.utils import (
|
from monkey_island.cc.services.configuration.utils import (
|
||||||
get_config_network_segments_as_subnet_groups,
|
get_config_network_segments_as_subnet_groups,
|
||||||
)
|
)
|
||||||
|
from monkey_island.cc.services.exploitations.manual_exploitation import get_manual_monkeys
|
||||||
from monkey_island.cc.services.node import NodeService
|
from monkey_island.cc.services.node import NodeService
|
||||||
from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_descriptor_enum import ( # noqa: E501
|
from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_descriptor_enum import ( # noqa: E501
|
||||||
ExploiterDescriptorEnum,
|
ExploiterDescriptorEnum,
|
||||||
|
@ -553,12 +554,8 @@ class ReportService:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_manual_monkeys():
|
def get_manual_monkey_hostnames():
|
||||||
return [
|
return [monkey["hostname"] for monkey in get_manual_monkeys()]
|
||||||
monkey["hostname"]
|
|
||||||
for monkey in mongo.db.monkey.find({}, {"hostname": 1, "parent": 1, "guid": 1})
|
|
||||||
if NodeService.get_monkey_manual_run(monkey)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_config_users():
|
def get_config_users():
|
||||||
|
@ -654,7 +651,7 @@ class ReportService:
|
||||||
exploited_nodes = ReportService.get_exploited()
|
exploited_nodes = ReportService.get_exploited()
|
||||||
report = {
|
report = {
|
||||||
"overview": {
|
"overview": {
|
||||||
"manual_monkeys": ReportService.get_manual_monkeys(),
|
"manual_monkeys": ReportService.get_manual_monkey_hostnames(),
|
||||||
"config_users": config_users,
|
"config_users": config_users,
|
||||||
"config_passwords": config_passwords,
|
"config_passwords": config_passwords,
|
||||||
"config_exploits": ReportService.get_config_exploits(),
|
"config_exploits": ReportService.get_config_exploits(),
|
||||||
|
|
|
@ -6,6 +6,7 @@ import FileEncryptionTable from './ransomware/FileEncryptionTable';
|
||||||
import LateralMovement from './ransomware/LateralMovement';
|
import LateralMovement from './ransomware/LateralMovement';
|
||||||
|
|
||||||
import '../../styles/pages/report/RansomwareReport.scss';
|
import '../../styles/pages/report/RansomwareReport.scss';
|
||||||
|
import BreachSection from './ransomware/BreachSection';
|
||||||
|
|
||||||
class RansomwareReport extends React.Component {
|
class RansomwareReport extends React.Component {
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ class RansomwareReport extends React.Component {
|
||||||
generateReportContent() {
|
generateReportContent() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<BreachSection/>
|
||||||
<LateralMovement propagationStats={this.props.report.propagation_stats} />
|
<LateralMovement propagationStats={this.props.report.propagation_stats} />
|
||||||
<FileEncryptionTable tableData={this.props.report.encrypted_files_table} />
|
<FileEncryptionTable tableData={this.props.report.encrypted_files_table} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,36 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export let renderArray = function (val) {
|
export let renderArray = function (val, className='') {
|
||||||
return <>{val.map(x => <div key={x}>{x}</div>)}</>;
|
return <>{val.map(x => <div key={x} className={className}>{x}</div>)}</>;
|
||||||
};
|
};
|
||||||
export let renderIpAddresses = function (val) {
|
export let renderIpAddresses = function (val) {
|
||||||
return <div>{renderArray(val.ip_addresses)} {(val.domain_name ? ' ('.concat(val.domain_name, ')') : '')} </div>;
|
return <div>
|
||||||
|
{renderArray(val.ip_addresses, 'ip-address')} {(val.domain_name ? ' ('.concat(val.domain_name, ')') : '')}
|
||||||
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export let renderLimitedArray = function (array,
|
||||||
|
limit,
|
||||||
|
className='',
|
||||||
|
separator=',') {
|
||||||
|
let elements = [];
|
||||||
|
if(array.length < limit){
|
||||||
|
limit = array.length;
|
||||||
|
}
|
||||||
|
for(let i = 0; i < limit; i++){
|
||||||
|
let element = '';
|
||||||
|
if(i !== 0) {
|
||||||
|
element = (<>{separator} {array[i]}</>);
|
||||||
|
} else {
|
||||||
|
element = (<>{array[i]}</>);
|
||||||
|
}
|
||||||
|
elements.push(<div className={className} key={array[i]}>{element}</div>);
|
||||||
|
}
|
||||||
|
let remainder = array.length - limit;
|
||||||
|
if(remainder > 0){
|
||||||
|
elements.push(<div className={className} key={'remainder'}>
|
||||||
|
and {remainder} more
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
return elements
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React, {useEffect, useState} from 'react';
|
||||||
|
import IslandHttpClient from '../../IslandHttpClient';
|
||||||
|
import NumberedReportSection from './NumberedReportSection';
|
||||||
|
import LoadingIcon from '../../ui-components/LoadingIcon';
|
||||||
|
import {renderLimitedArray} from '../common/RenderArrays';
|
||||||
|
|
||||||
|
function BreachSection() {
|
||||||
|
const [machines, setMachines] = useState(null);
|
||||||
|
let description = 'Ransomware attacks start after machines in the internal network get compromised. ' +
|
||||||
|
'The initial compromise was simulated by running Monkey Agents manually.';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
IslandHttpClient.get('/api/exploitations/manual')
|
||||||
|
.then(resp => setMachines(resp.body['manual_exploitations']));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if(machines !== null){
|
||||||
|
let body = getBreachSectionBody(machines);
|
||||||
|
return (<NumberedReportSection index={1} title={'Breach'} description={description} body={body}/>)
|
||||||
|
} else {
|
||||||
|
return <LoadingIcon />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBreachSectionBody(machines) {
|
||||||
|
let machineList = [];
|
||||||
|
for(let i = 0; i < machines.length; i++){
|
||||||
|
machineList.push(getMachine(machines[i]));
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={'ransomware-breach-section'}>
|
||||||
|
<p>Ransomware attack started from these machines on the network:</p>
|
||||||
|
<ul>
|
||||||
|
{machineList}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMachine(machine) {
|
||||||
|
return (
|
||||||
|
<li key={machine['hostname']}>
|
||||||
|
<b>{machine['hostname']}</b>
|
||||||
|
({renderLimitedArray(machine['ip_addresses'], 2, 'ip-address')}) at <b>{machine['start_time']}</b>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BreachSection;
|
|
@ -18,3 +18,7 @@
|
||||||
margin-top: .28em;
|
margin-top: .28em;
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ransomware-breach-section .ip-address {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ PbaResults # unused class (monkey/monkey_island/cc/models/pba_results.py:4)
|
||||||
internet_access # unused variable (monkey/monkey_island/cc/models/monkey.py:43)
|
internet_access # unused variable (monkey/monkey_island/cc/models/monkey.py:43)
|
||||||
config_error # unused variable (monkey/monkey_island/cc/models/monkey.py:53)
|
config_error # unused variable (monkey/monkey_island/cc/models/monkey.py:53)
|
||||||
pba_results # unused variable (monkey/monkey_island/cc/models/monkey.py:55)
|
pba_results # unused variable (monkey/monkey_island/cc/models/monkey.py:55)
|
||||||
|
launch_time # unused variable (monkey/monkey_island/cc/models/monkey.py)
|
||||||
command_control_channel # unused variable (monkey/monkey_island/cc/models/monkey.py:58)
|
command_control_channel # unused variable (monkey/monkey_island/cc/models/monkey.py:58)
|
||||||
meta # unused variable (monkey/monkey_island/cc/models/zero_trust/finding.py:37)
|
meta # unused variable (monkey/monkey_island/cc/models/zero_trust/finding.py:37)
|
||||||
meta # unused variable (monkey/monkey_island/cc/models/monkey_ttl.py:34)
|
meta # unused variable (monkey/monkey_island/cc/models/monkey_ttl.py:34)
|
||||||
|
|
Loading…
Reference in New Issue