commit
de435e27ad
|
@ -79,8 +79,8 @@ class BatchingAgentEventForwarder:
|
|||
try:
|
||||
logger.debug(f"Sending Agent events to Island: {events}")
|
||||
self._island_api_client.send_events(events)
|
||||
except Exception as err:
|
||||
logger.warning(f"Exception caught when connecting to the Island: {err}")
|
||||
except Exception:
|
||||
logger.exception("Exception caught when connecting to the Island")
|
||||
|
||||
def _send_remaining_events(self):
|
||||
self._send_events_to_island()
|
||||
|
|
|
@ -38,3 +38,6 @@ class Machine(MutableInfectionMonkeyBaseModel):
|
|||
_make_immutable_sequence = validator("network_interfaces", pre=True, allow_reuse=True)(
|
||||
make_immutable_sequence
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return self.id
|
||||
|
|
|
@ -94,6 +94,8 @@ def initialize_services(container: DIContainer, data_dir: Path):
|
|||
container.resolve(AWSService),
|
||||
container.resolve(IAgentConfigurationRepository),
|
||||
container.resolve(ICredentialsRepository),
|
||||
container.resolve(IMachineRepository),
|
||||
container.resolve(INodeRepository),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -2,15 +2,20 @@ import functools
|
|||
import ipaddress
|
||||
import logging
|
||||
from itertools import chain, product
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from common.network.network_range import NetworkRange
|
||||
from common.network.network_utils import get_my_ip_addresses_legacy, get_network_interfaces
|
||||
from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.models import Monkey
|
||||
from monkey_island.cc.models import CommunicationType, Machine, Monkey
|
||||
from monkey_island.cc.models.report import get_report, save_report
|
||||
from monkey_island.cc.repository import IAgentConfigurationRepository, ICredentialsRepository
|
||||
from monkey_island.cc.repository import (
|
||||
IAgentConfigurationRepository,
|
||||
ICredentialsRepository,
|
||||
IMachineRepository,
|
||||
INodeRepository,
|
||||
)
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.services.reporting.exploitations.manual_exploitation import get_manual_monkeys
|
||||
from monkey_island.cc.services.reporting.exploitations.monkey_exploitation import (
|
||||
|
@ -30,10 +35,11 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class ReportService:
|
||||
|
||||
_aws_service = None
|
||||
_agent_configuration_repository = None
|
||||
_credentials_repository = None
|
||||
_aws_service: Optional[AWSService] = None
|
||||
_agent_configuration_repository: Optional[IAgentConfigurationRepository] = None
|
||||
_credentials_repository: Optional[ICredentialsRepository] = None
|
||||
_machine_repository: Optional[IMachineRepository] = None
|
||||
_node_repository: Optional[INodeRepository] = None
|
||||
|
||||
class DerivedIssueEnum:
|
||||
ZEROLOGON_PASS_RESTORE_FAILED = "zerologon_pass_restore_failed"
|
||||
|
@ -44,10 +50,14 @@ class ReportService:
|
|||
aws_service: AWSService,
|
||||
agent_configuration_repository: IAgentConfigurationRepository,
|
||||
credentials_repository: ICredentialsRepository,
|
||||
machine_repository: IMachineRepository,
|
||||
node_repository: INodeRepository,
|
||||
):
|
||||
cls._aws_service = aws_service
|
||||
cls._agent_configuration_repository = agent_configuration_repository
|
||||
cls._credentials_repository = credentials_repository
|
||||
cls._machine_repository = machine_repository
|
||||
cls._node_repository = node_repository
|
||||
|
||||
# This should pull from Simulation entity
|
||||
@staticmethod
|
||||
|
@ -102,39 +112,42 @@ class ReportService:
|
|||
def get_scanned():
|
||||
formatted_nodes = []
|
||||
|
||||
nodes = ReportService.get_all_displayed_nodes()
|
||||
machines = ReportService._machine_repository.get_machines()
|
||||
|
||||
for node in nodes:
|
||||
# This information should be evident from the map, not sure a table/list is a good way
|
||||
# to display it anyways
|
||||
nodes_that_can_access_current_node = node["accessible_from_nodes_hostnames"]
|
||||
formatted_nodes.append(
|
||||
{
|
||||
"label": node["label"],
|
||||
"ip_addresses": node["ip_addresses"],
|
||||
"accessible_from_nodes": nodes_that_can_access_current_node,
|
||||
"services": node["services"],
|
||||
"domain_name": node["domain_name"],
|
||||
"pba_results": node["pba_results"] if "pba_results" in node else "None",
|
||||
}
|
||||
)
|
||||
|
||||
logger.info("Scanned nodes generated for reporting")
|
||||
for machine in machines:
|
||||
accessible_from = ReportService.get_scanners_of_machine(machine)
|
||||
if accessible_from:
|
||||
formatted_nodes.append(
|
||||
{
|
||||
"hostname": machine.hostname,
|
||||
"ip_addresses": [str(iface.ip) for iface in machine.network_interfaces],
|
||||
"accessible_from_nodes": [m.dict(simplify=True) for m in accessible_from],
|
||||
"domain_name": "",
|
||||
# TODO add services
|
||||
"services": [],
|
||||
}
|
||||
)
|
||||
|
||||
return formatted_nodes
|
||||
|
||||
@staticmethod
|
||||
def get_all_displayed_nodes():
|
||||
nodes_without_monkeys = [
|
||||
NodeService.get_displayed_node_by_id(node["_id"], True)
|
||||
for node in mongo.db.node.find({}, {"_id": 1})
|
||||
]
|
||||
nodes_with_monkeys = [
|
||||
NodeService.get_displayed_node_by_id(monkey["_id"], True)
|
||||
for monkey in mongo.db.monkey.find({}, {"_id": 1})
|
||||
]
|
||||
nodes = nodes_without_monkeys + nodes_with_monkeys
|
||||
return nodes
|
||||
@classmethod
|
||||
def get_scanners_of_machine(cls, machine: Machine) -> List[Machine]:
|
||||
if not cls._node_repository:
|
||||
raise RuntimeError("Node repository does not exist")
|
||||
if not cls._machine_repository:
|
||||
raise RuntimeError("Machine repository does not exist")
|
||||
|
||||
nodes = cls._node_repository.get_nodes()
|
||||
scanner_machines = set()
|
||||
for node in nodes:
|
||||
for dest, conn in node.connections.items():
|
||||
if CommunicationType.SCANNED in conn and dest == machine.id:
|
||||
scanner_machine = ReportService._machine_repository.get_machine_by_id(
|
||||
node.machine_id
|
||||
)
|
||||
scanner_machines.add(scanner_machine)
|
||||
|
||||
return list(scanner_machines)
|
||||
|
||||
@staticmethod
|
||||
def process_exploit(exploit) -> ExploiterReportInfo:
|
||||
|
|
|
@ -2,7 +2,6 @@ import React, {Fragment} from 'react';
|
|||
import Pluralize from 'pluralize';
|
||||
import BreachedServers from 'components/report-components/security/BreachedServers';
|
||||
import ScannedServers from 'components/report-components/security/ScannedServers';
|
||||
import PostBreach from 'components/report-components/security/PostBreach';
|
||||
import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph';
|
||||
import {edgeGroupToColor, getOptions} from 'components/map/MapOptions';
|
||||
import StolenPasswords from 'components/report-components/security/StolenPasswords';
|
||||
|
@ -23,7 +22,7 @@ import {smbPasswordReport, smbPthReport} from './security/issues/SmbIssue';
|
|||
import {hadoopIssueOverview, hadoopIssueReport} from './security/issues/HadoopIssue';
|
||||
import {mssqlIssueOverview, mssqlIssueReport} from './security/issues/MssqlIssue';
|
||||
import {wmiPasswordIssueReport, wmiPthIssueReport} from './security/issues/WmiIssue';
|
||||
import {sshKeysReport, shhIssueReport, sshIssueOverview} from './security/issues/SshIssue';
|
||||
import {shhIssueReport, sshIssueOverview, sshKeysReport} from './security/issues/SshIssue';
|
||||
import {log4shellIssueOverview, log4shellIssueReport} from './security/issues/Log4ShellIssue';
|
||||
import {
|
||||
crossSegmentIssueOverview,
|
||||
|
@ -31,11 +30,13 @@ import {
|
|||
islandCrossSegmentIssueReport
|
||||
} from './security/issues/CrossSegmentIssue';
|
||||
import {
|
||||
sharedCredsDomainIssueReport, sharedCredsIssueReport, sharedLocalAdminsIssueReport,
|
||||
sharedAdminsDomainIssueOverview,
|
||||
sharedCredsDomainIssueReport,
|
||||
sharedCredsIssueReport,
|
||||
sharedLocalAdminsIssueReport,
|
||||
sharedPasswordsIssueOverview
|
||||
} from './security/issues/SharedPasswordsIssue';
|
||||
import {tunnelIssueReport, tunnelIssueOverview} from './security/issues/TunnelIssue';
|
||||
import {tunnelIssueOverview, tunnelIssueReport} from './security/issues/TunnelIssue';
|
||||
import {stolenCredsIssueOverview} from './security/issues/StolenCredsIssue';
|
||||
import {strongUsersOnCritIssueReport} from './security/issues/StrongUsersOnCritIssue';
|
||||
import {
|
||||
|
@ -538,9 +539,11 @@ class ReportPageComponent extends AuthComponent {
|
|||
<BreachedServers />
|
||||
</div>
|
||||
|
||||
{/* Post breach data should be separated from scanned machines
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<PostBreach data={this.state.report.glance.scanned}/>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
<div style={{marginBottom: '20px'}}>
|
||||
<StolenPasswords
|
||||
|
|
|
@ -9,6 +9,10 @@ export let renderIpAddresses = function (val) {
|
|||
</div>;
|
||||
};
|
||||
|
||||
export let renderMachineArray = function(array) {
|
||||
return <>{array.map(x => <div key={x.network_interfaces[0]}>{x.network_interfaces[0]}</div>)}</>;
|
||||
}
|
||||
|
||||
export let renderLimitedArray = function (array,
|
||||
limit,
|
||||
className='',
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table';
|
||||
import Pluralize from 'pluralize';
|
||||
import {renderArray, renderIpAddresses} from '../common/RenderArrays';
|
||||
import {renderArray, renderIpAddresses, renderMachineArray} from '../common/RenderArrays';
|
||||
|
||||
|
||||
const columns = [
|
||||
{
|
||||
Header: 'Scanned Servers',
|
||||
columns: [
|
||||
{Header: 'Machine', accessor: 'label'},
|
||||
{Header: 'Machine', id: 'machine', accessor: x => x.ip_addresses[0]},
|
||||
{
|
||||
Header: 'IP Addresses', id: 'ip_addresses',
|
||||
accessor: x => renderIpAddresses(x)
|
||||
},
|
||||
{Header: 'Accessible From', id: 'accessible_from_nodes', accessor: x => renderArray(x.accessible_from_nodes)},
|
||||
{Header: 'Accessible From', id: 'accessible_from_nodes',
|
||||
accessor: x => renderMachineArray(x.accessible_from_nodes)},
|
||||
{Header: 'Services', id: 'services', accessor: x => renderArray(x.services)}
|
||||
]
|
||||
}
|
||||
|
@ -27,7 +28,6 @@ class ScannedServersComponent extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
|
||||
let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length;
|
||||
let showPagination = this.props.data.length > pageSize;
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
from ipaddress import IPv4Interface
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from monkey_island.cc.models import CommunicationType, Machine, Node
|
||||
from monkey_island.cc.services.reporting.report import ReportService
|
||||
|
||||
ISLAND_MACHINE = Machine(
|
||||
id=99,
|
||||
island=True,
|
||||
hostname="Island",
|
||||
hardware_id=5,
|
||||
network_interfaces=[IPv4Interface("10.10.10.99/24")],
|
||||
)
|
||||
|
||||
MACHINE_1 = Machine(
|
||||
id=1,
|
||||
hardware_id=9,
|
||||
hostname="machine_1",
|
||||
network_interfaces=[IPv4Interface("10.10.10.1/24")],
|
||||
)
|
||||
|
||||
MACHINE_2 = Machine(
|
||||
id=2,
|
||||
hardware_id=9,
|
||||
network_interfaces=[IPv4Interface("10.10.10.2/24")],
|
||||
)
|
||||
|
||||
MACHINE_3 = Machine(
|
||||
id=3,
|
||||
hardware_id=9,
|
||||
network_interfaces=[IPv4Interface("10.10.10.3/24")],
|
||||
)
|
||||
|
||||
NODES = [
|
||||
Node(
|
||||
machine_id=1,
|
||||
connections={"2": frozenset([CommunicationType.EXPLOITED, CommunicationType.SCANNED])},
|
||||
),
|
||||
Node(machine_id=99, connections={"1": frozenset([CommunicationType.SCANNED])}),
|
||||
Node(
|
||||
machine_id=3,
|
||||
connections={"99": frozenset([CommunicationType.CC, CommunicationType.EXPLOITED])},
|
||||
),
|
||||
]
|
||||
|
||||
MACHINES = [MACHINE_1, MACHINE_2, MACHINE_3, ISLAND_MACHINE]
|
||||
|
||||
EXPECTED_SCANNED_MACHINES = [
|
||||
{
|
||||
"hostname": MACHINE_1.hostname,
|
||||
"ip_addresses": [str(iface.ip) for iface in MACHINE_1.network_interfaces],
|
||||
"accessible_from_nodes": [ISLAND_MACHINE.dict(simplify=True)],
|
||||
"services": [],
|
||||
"domain_name": "",
|
||||
},
|
||||
{
|
||||
"hostname": MACHINE_2.hostname,
|
||||
"ip_addresses": [str(iface.ip) for iface in MACHINE_2.network_interfaces],
|
||||
"accessible_from_nodes": [MACHINE_1.dict(simplify=True)],
|
||||
"services": [],
|
||||
"domain_name": "",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def get_machine_by_id(machine_id):
|
||||
return [machine for machine in MACHINES if machine_id == machine.id][0]
|
||||
|
||||
|
||||
def test_get_scanned():
|
||||
ReportService._node_repository = MagicMock()
|
||||
ReportService._node_repository.get_nodes.return_value = NODES
|
||||
ReportService._machine_repository = MagicMock()
|
||||
ReportService._machine_repository.get_machines.return_value = MACHINES
|
||||
ReportService._machine_repository.get_machine_by_id = get_machine_by_id
|
||||
scanned = ReportService.get_scanned()
|
||||
assert scanned == EXPECTED_SCANNED_MACHINES
|
Loading…
Reference in New Issue