Extracted exploiters from reports in front end and back end
This commit is contained in:
parent
8437f8d838
commit
8efc3e654c
|
@ -0,0 +1,32 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
|
from monkey_island.cc.services.reporting.issue_processing.exploit_processing import CredExploitProcessor
|
||||||
|
from monkey_island.cc.services.reporting.issue_processing.exploit_processing import ExploitProcessor
|
||||||
|
from monkey_island.cc.services.reporting.issue_processing.exploit_processing import \
|
||||||
|
ShellShockExploitProcessor
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ExploiterDescriptor:
|
||||||
|
# Must match with class names of exploiters in Infection Monkey code
|
||||||
|
class_name: str
|
||||||
|
display_name: str
|
||||||
|
processor: Type[ExploitProcessor]
|
||||||
|
|
||||||
|
|
||||||
|
class ExploiterDescriptorEnum(Enum):
|
||||||
|
SMB = ExploiterDescriptor('SmbExploiter', 'SMB Exploiter', CredExploitProcessor)
|
||||||
|
WMI = ExploiterDescriptor('WmiExploiter', 'WMI Exploiter', CredExploitProcessor)
|
||||||
|
SSH = ExploiterDescriptor('SSHExploiter', 'SSH Exploiter', CredExploitProcessor)
|
||||||
|
SAMBACRY = ExploiterDescriptor('SambaCryExploiter', 'SambaCry Exploiter', CredExploitProcessor)
|
||||||
|
ELASTIC = ExploiterDescriptor('ElasticGroovyExploiter', 'Elastic Groovy Exploiter', ExploitProcessor)
|
||||||
|
MS08_067 = ExploiterDescriptor('Ms08_067_Exploiter', 'Conficker Exploiter', ExploitProcessor)
|
||||||
|
SHELLSHOCK = ExploiterDescriptor('ShellShockExploiter', 'ShellShock Exploiter', ShellShockExploitProcessor)
|
||||||
|
STRUTS2 = ExploiterDescriptor('Struts2Exploiter', 'Struts2 Exploiter', ExploitProcessor)
|
||||||
|
WEBLOGIC = ExploiterDescriptor('WebLogicExploiter', 'Oracle WebLogic Exploiter', ExploitProcessor)
|
||||||
|
HADOOP = ExploiterDescriptor('HadoopExploiter', 'Hadoop/Yarn Exploiter', ExploitProcessor)
|
||||||
|
MSSQL = ExploiterDescriptor('MSSQLExploiter', 'MSSQL Exploiter', ExploitProcessor)
|
||||||
|
VSFTPD = ExploiterDescriptor('VSFTPDExploiter', 'VSFTPD Backdoor Exploiter', CredExploitProcessor)
|
||||||
|
DRUPAL = ExploiterDescriptor('DrupalExploiter', 'Drupal Server Exploiter', ExploitProcessor)
|
|
@ -0,0 +1,33 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from monkey_island.cc.services.reporting.issue_processing.exploit_processing import ExploitProcessor, \
|
||||||
|
ExploiterReportInfo
|
||||||
|
|
||||||
|
|
||||||
|
class CredentialType(Enum):
|
||||||
|
PASSWORD = 'password'
|
||||||
|
HASH = 'hash'
|
||||||
|
KEY = 'key'
|
||||||
|
|
||||||
|
|
||||||
|
class CredExploitProcessor(ExploitProcessor):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_exploit_info_by_dict(class_name: str, exploit_dict: dict) -> ExploiterReportInfo:
|
||||||
|
exploit_info = ExploitProcessor.get_exploit_info_by_dict(class_name, exploit_dict)
|
||||||
|
|
||||||
|
for attempt in exploit_dict['data']['attempts']:
|
||||||
|
if attempt['result']:
|
||||||
|
exploit_info.username = attempt['user']
|
||||||
|
if attempt['password']:
|
||||||
|
exploit_info.credential_type = CredentialType.PASSWORD.value
|
||||||
|
exploit_info.password = attempt['password']
|
||||||
|
elif attempt['ssh_key']:
|
||||||
|
exploit_info.credential_type = CredentialType.KEY.value
|
||||||
|
exploit_info.ssh_key = attempt['ssh_key']
|
||||||
|
else:
|
||||||
|
exploit_info.credential_type = CredentialType.HASH.value
|
||||||
|
return exploit_info
|
||||||
|
return exploit_info
|
|
@ -0,0 +1,21 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from monkey_island.cc.services.node import NodeService
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ExploiterReportInfo:
|
||||||
|
machine: str
|
||||||
|
ip_address: str
|
||||||
|
type: str
|
||||||
|
|
||||||
|
|
||||||
|
class ExploitProcessor:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_exploit_info_by_dict(class_name: str, exploit_dict: dict) -> ExploiterReportInfo:
|
||||||
|
ip_addr = exploit_dict['data']['machine']['ip_addr']
|
||||||
|
machine = NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_ip(ip_addr))
|
||||||
|
return ExploiterReportInfo(ip_address=ip_addr, machine=machine, type=class_name)
|
|
@ -0,0 +1,16 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from monkey_island.cc.services.reporting.issue_processing.exploit_processing import ExploitProcessor, \
|
||||||
|
ExploiterReportInfo
|
||||||
|
|
||||||
|
|
||||||
|
class ShellShockExploitProcessor(ExploitProcessor):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_exploit_info_by_dict(class_name: str, exploit_dict: dict) -> ExploiterReportInfo:
|
||||||
|
exploit_info = ExploitProcessor.get_exploit_info_by_dict(class_name, exploit_dict)
|
||||||
|
|
||||||
|
urls = exploit_dict['data']['info']['vulnerable_urls']
|
||||||
|
exploit_info.port = urls[0].split(':')[2].split('/')[0]
|
||||||
|
exploit_info.paths = ['/' + url.split(':')[2].split('/')[1] for url in urls]
|
||||||
|
return exploit_info
|
|
@ -2,7 +2,7 @@ import functools
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
from enum import Enum
|
from typing import Dict, List
|
||||||
|
|
||||||
from bson import json_util
|
from bson import json_util
|
||||||
|
|
||||||
|
@ -17,6 +17,10 @@ from common.config_value_paths import (EXPLOITER_CLASSES_PATH, LOCAL_NETWORK_SCA
|
||||||
USER_LIST_PATH)
|
USER_LIST_PATH)
|
||||||
from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups
|
from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups
|
||||||
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 ExploiterDescriptorEnum, \
|
||||||
|
ExploiterDescriptor
|
||||||
|
from monkey_island.cc.services.reporting.issue_processing.exploit_processing import CredentialType
|
||||||
|
from monkey_island.cc.services.reporting.issue_processing.exploit_processing import ExploiterReportInfo
|
||||||
from monkey_island.cc.services.reporting.pth_report import PTHReportService
|
from monkey_island.cc.services.reporting.pth_report import PTHReportService
|
||||||
from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager
|
from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager
|
||||||
from monkey_island.cc.services.reporting.report_generation_synchronisation import safe_generate_regular_report
|
from monkey_island.cc.services.reporting.report_generation_synchronisation import safe_generate_regular_report
|
||||||
|
@ -26,52 +30,19 @@ __author__ = "itay.mizeretz"
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def build_exploiter_descriptor_dict() -> Dict[str, ExploiterDescriptor]:
|
||||||
|
descriptor_dict = {}
|
||||||
|
for descriptor in ExploiterDescriptorEnum:
|
||||||
|
descriptor_dict[descriptor.value.class_name] = descriptor
|
||||||
|
return descriptor_dict
|
||||||
|
|
||||||
|
|
||||||
class ReportService:
|
class ReportService:
|
||||||
def __init__(self):
|
exploiter_descriptors = build_exploiter_descriptor_dict()
|
||||||
pass
|
|
||||||
|
|
||||||
EXPLOIT_DISPLAY_DICT = \
|
class DerivedIssueEnum:
|
||||||
{
|
WEAK_PASSWORD = "weak_password"
|
||||||
'SmbExploiter': 'SMB Exploiter',
|
STOLEN_CREDS = "stolen_creds"
|
||||||
'WmiExploiter': 'WMI Exploiter',
|
|
||||||
'SSHExploiter': 'SSH Exploiter',
|
|
||||||
'SambaCryExploiter': 'SambaCry Exploiter',
|
|
||||||
'ElasticGroovyExploiter': 'Elastic Groovy Exploiter',
|
|
||||||
'Ms08_067_Exploiter': 'Conficker Exploiter',
|
|
||||||
'ShellShockExploiter': 'ShellShock Exploiter',
|
|
||||||
'Struts2Exploiter': 'Struts2 Exploiter',
|
|
||||||
'WebLogicExploiter': 'Oracle WebLogic Exploiter',
|
|
||||||
'HadoopExploiter': 'Hadoop/Yarn Exploiter',
|
|
||||||
'MSSQLExploiter': 'MSSQL Exploiter',
|
|
||||||
'VSFTPDExploiter': 'VSFTPD Backdoor Exploiter',
|
|
||||||
'DrupalExploiter': 'Drupal Server Exploiter',
|
|
||||||
'ZerologonExploiter': 'Windows Server Zerologon Exploiter'
|
|
||||||
}
|
|
||||||
|
|
||||||
class ISSUES_DICT(Enum):
|
|
||||||
WEAK_PASSWORD = 0
|
|
||||||
STOLEN_CREDS = 1
|
|
||||||
ELASTIC = 2
|
|
||||||
SAMBACRY = 3
|
|
||||||
SHELLSHOCK = 4
|
|
||||||
CONFICKER = 5
|
|
||||||
AZURE = 6
|
|
||||||
STOLEN_SSH_KEYS = 7
|
|
||||||
STRUTS2 = 8
|
|
||||||
WEBLOGIC = 9
|
|
||||||
HADOOP = 10
|
|
||||||
PTH_CRIT_SERVICES_ACCESS = 11
|
|
||||||
MSSQL = 12
|
|
||||||
VSFTPD = 13
|
|
||||||
DRUPAL = 14
|
|
||||||
ZEROLOGON = 15
|
|
||||||
ZEROLOGON_PASSWORD_RESTORE_FAILED = 16
|
|
||||||
|
|
||||||
class WARNINGS_DICT(Enum):
|
|
||||||
CROSS_SEGMENT = 0
|
|
||||||
TUNNEL = 1
|
|
||||||
SHARED_LOCAL_ADMIN = 2
|
|
||||||
SHARED_PASSWORDS = 3
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_first_monkey_time():
|
def get_first_monkey_time():
|
||||||
|
@ -169,9 +140,7 @@ class ReportService:
|
||||||
'label': exploited_node['label'],
|
'label': exploited_node['label'],
|
||||||
'ip_addresses': exploited_node['ip_addresses'],
|
'ip_addresses': exploited_node['ip_addresses'],
|
||||||
'domain_name': exploited_node['domain_name'],
|
'domain_name': exploited_node['domain_name'],
|
||||||
'exploits': list(set(
|
'exploits': ReportService.get_exploits_used_on_node(exploited_node)
|
||||||
[ReportService.EXPLOIT_DISPLAY_DICT[exploit['exploiter']] for exploit in exploited_node['exploits']
|
|
||||||
if exploit['result']]))
|
|
||||||
}
|
}
|
||||||
for exploited_node in exploited]
|
for exploited_node in exploited]
|
||||||
|
|
||||||
|
@ -179,6 +148,10 @@ class ReportService:
|
||||||
|
|
||||||
return exploited
|
return exploited
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_exploits_used_on_node(node: dict) -> List[str]:
|
||||||
|
return list(set([exploit['exploiter'] for exploit in node['exploits'] if exploit['result']]))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_stolen_creds():
|
def get_stolen_creds():
|
||||||
creds = []
|
creds = []
|
||||||
|
@ -281,148 +254,12 @@ class ReportService:
|
||||||
return creds
|
return creds
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_general_exploit(exploit):
|
def process_exploit(exploit) -> ExploiterReportInfo:
|
||||||
ip_addr = exploit['data']['machine']['ip_addr']
|
|
||||||
return {'machine': NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_ip(ip_addr)),
|
|
||||||
'ip_address': ip_addr}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_general_creds_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
|
|
||||||
for attempt in exploit['data']['attempts']:
|
|
||||||
if attempt['result']:
|
|
||||||
processed_exploit['username'] = attempt['user']
|
|
||||||
if attempt['password']:
|
|
||||||
processed_exploit['type'] = 'password'
|
|
||||||
processed_exploit['password'] = attempt['password']
|
|
||||||
elif attempt['ssh_key']:
|
|
||||||
processed_exploit['type'] = 'ssh_key'
|
|
||||||
processed_exploit['ssh_key'] = attempt['ssh_key']
|
|
||||||
else:
|
|
||||||
processed_exploit['type'] = 'hash'
|
|
||||||
return processed_exploit
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_smb_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
|
||||||
if processed_exploit['type'] == 'password':
|
|
||||||
processed_exploit['type'] = 'smb_password'
|
|
||||||
else:
|
|
||||||
processed_exploit['type'] = 'smb_pth'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_wmi_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
|
||||||
if processed_exploit['type'] == 'password':
|
|
||||||
processed_exploit['type'] = 'wmi_password'
|
|
||||||
else:
|
|
||||||
processed_exploit['type'] = 'wmi_pth'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_ssh_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
|
||||||
# Check if it's ssh key or ssh login credentials exploit
|
|
||||||
if processed_exploit['type'] == 'ssh_key':
|
|
||||||
return processed_exploit
|
|
||||||
else:
|
|
||||||
processed_exploit['type'] = 'ssh'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_vsftpd_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'vsftp'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_sambacry_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_creds_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'sambacry'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_elastic_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'elastic'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_conficker_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'conficker'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_shellshock_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'shellshock'
|
|
||||||
urls = exploit['data']['info']['vulnerable_urls']
|
|
||||||
processed_exploit['port'] = urls[0].split(':')[2].split('/')[0]
|
|
||||||
processed_exploit['paths'] = ['/' + url.split(':')[2].split('/')[1] for url in urls]
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_struts2_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'struts2'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_weblogic_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'weblogic'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_hadoop_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'hadoop'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_mssql_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'mssql'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_drupal_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'drupal'
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_zerologon_exploit(exploit):
|
|
||||||
processed_exploit = ReportService.process_general_exploit(exploit)
|
|
||||||
processed_exploit['type'] = 'zerologon'
|
|
||||||
processed_exploit['password_restored'] = exploit['data']['info']['password_restored']
|
|
||||||
return processed_exploit
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_exploit(exploit):
|
|
||||||
exploiter_type = exploit['data']['exploiter']
|
exploiter_type = exploit['data']['exploiter']
|
||||||
EXPLOIT_PROCESS_FUNCTION_DICT = {
|
exploiter_descriptor = ReportService.exploiter_descriptors[exploiter_type].value
|
||||||
'SmbExploiter': ReportService.process_smb_exploit,
|
processor = exploiter_descriptor.processor()
|
||||||
'WmiExploiter': ReportService.process_wmi_exploit,
|
exploiter_info = processor.get_exploit_info_by_dict(exploiter_descriptor.class_name, exploit)
|
||||||
'SSHExploiter': ReportService.process_ssh_exploit,
|
return exploiter_info
|
||||||
'SambaCryExploiter': ReportService.process_sambacry_exploit,
|
|
||||||
'ElasticGroovyExploiter': ReportService.process_elastic_exploit,
|
|
||||||
'Ms08_067_Exploiter': ReportService.process_conficker_exploit,
|
|
||||||
'ShellShockExploiter': ReportService.process_shellshock_exploit,
|
|
||||||
'Struts2Exploiter': ReportService.process_struts2_exploit,
|
|
||||||
'WebLogicExploiter': ReportService.process_weblogic_exploit,
|
|
||||||
'HadoopExploiter': ReportService.process_hadoop_exploit,
|
|
||||||
'MSSQLExploiter': ReportService.process_mssql_exploit,
|
|
||||||
'VSFTPDExploiter': ReportService.process_vsftpd_exploit,
|
|
||||||
'DrupalExploiter': ReportService.process_drupal_exploit,
|
|
||||||
'ZerologonExploiter': ReportService.process_zerologon_exploit
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXPLOIT_PROCESS_FUNCTION_DICT[exploiter_type](exploit)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_exploits():
|
def get_exploits():
|
||||||
|
@ -585,7 +422,8 @@ class ReportService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_cross_segment_issues():
|
def get_cross_segment_issues():
|
||||||
scans = mongo.db.telemetry.find({'telem_category': 'scan'},
|
scans = mongo.db.telemetry.find({'telem_category': 'scan'},
|
||||||
{'monkey_guid': 1, 'data.machine.ip_addr': 1, 'data.machine.services': 1, 'data.machine.icmp': 1})
|
{'monkey_guid': 1, 'data.machine.ip_addr': 1, 'data.machine.services': 1,
|
||||||
|
'data.machine.icmp': 1})
|
||||||
|
|
||||||
cross_segment_issues = []
|
cross_segment_issues = []
|
||||||
|
|
||||||
|
@ -627,32 +465,6 @@ class ReportService:
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_issues():
|
|
||||||
ISSUE_GENERATORS = [
|
|
||||||
ReportService.get_exploits,
|
|
||||||
ReportService.get_tunnels,
|
|
||||||
ReportService.get_island_cross_segment_issues,
|
|
||||||
ReportService.get_azure_issues,
|
|
||||||
PTHReportService.get_duplicated_passwords_issues,
|
|
||||||
PTHReportService.get_strong_users_on_crit_issues
|
|
||||||
]
|
|
||||||
|
|
||||||
issues = functools.reduce(lambda acc, issue_gen: acc + issue_gen(), ISSUE_GENERATORS, [])
|
|
||||||
|
|
||||||
issues_dict = {}
|
|
||||||
for issue in issues:
|
|
||||||
if issue.get('is_local', True):
|
|
||||||
machine = issue.get('machine').upper()
|
|
||||||
aws_instance_id = ReportService.get_machine_aws_instance_id(issue.get('machine'))
|
|
||||||
if machine not in issues_dict:
|
|
||||||
issues_dict[machine] = []
|
|
||||||
if aws_instance_id:
|
|
||||||
issue['aws_instance_id'] = aws_instance_id
|
|
||||||
issues_dict[machine].append(issue)
|
|
||||||
logger.info('Issues generated for reporting')
|
|
||||||
return issues_dict
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_manual_monkeys():
|
def get_manual_monkeys():
|
||||||
return [monkey['hostname'] for monkey in mongo.db.monkey.find({}, {'hostname': 1, 'parent': 1, 'guid': 1}) if
|
return [monkey['hostname'] for monkey in mongo.db.monkey.find({}, {'hostname': 1, 'parent': 1, 'guid': 1}) if
|
||||||
|
@ -677,8 +489,8 @@ class ReportService:
|
||||||
if exploits == default_exploits:
|
if exploits == default_exploits:
|
||||||
return ['default']
|
return ['default']
|
||||||
|
|
||||||
return [ReportService.EXPLOIT_DISPLAY_DICT[exploit] for exploit in
|
# TODO investigate strange code
|
||||||
exploits]
|
return [exploit for exploit in exploits]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_config_ips():
|
def get_config_ips():
|
||||||
|
@ -689,68 +501,37 @@ class ReportService:
|
||||||
return ConfigService.get_config_value(LOCAL_NETWORK_SCAN_PATH, True, True)
|
return ConfigService.get_config_value(LOCAL_NETWORK_SCAN_PATH, True, True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_issues_overview(issues, config_users, config_passwords):
|
def get_issue_set(issues, config_users, config_passwords):
|
||||||
issues_byte_array = [False] * len(ReportService.ISSUES_DICT)
|
issue_set = set()
|
||||||
|
|
||||||
for machine in issues:
|
for machine in issues:
|
||||||
for issue in issues[machine]:
|
for issue in issues[machine]:
|
||||||
if issue['type'] == 'elastic':
|
# TODO check if this actually works, because stolen passwords get added to config
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.ELASTIC.value] = True
|
# so any password will be in config. We need to separate stolen passwords from initial
|
||||||
elif issue['type'] == 'sambacry':
|
# passwords in config.
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.SAMBACRY.value] = True
|
if ReportService._is_weak_credential_issue(issue, config_users, config_passwords):
|
||||||
elif issue['type'] == 'vsftp':
|
issue_set.add(ReportService.DerivedIssueEnum.WEAK_PASSWORD)
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.VSFTPD.value] = True
|
elif ReportService._is_stolen_credential_issue(issue):
|
||||||
elif issue['type'] == 'shellshock':
|
issue_set.add(ReportService.DerivedIssueEnum.STOLEN_CREDS)
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.SHELLSHOCK.value] = True
|
|
||||||
elif issue['type'] == 'conficker':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.CONFICKER.value] = True
|
|
||||||
elif issue['type'] == 'azure_password':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.AZURE.value] = True
|
|
||||||
elif issue['type'] == 'ssh_key':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.STOLEN_SSH_KEYS.value] = True
|
|
||||||
elif issue['type'] == 'struts2':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.STRUTS2.value] = True
|
|
||||||
elif issue['type'] == 'weblogic':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.WEBLOGIC.value] = True
|
|
||||||
elif issue['type'] == 'mssql':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.MSSQL.value] = True
|
|
||||||
elif issue['type'] == 'hadoop':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.HADOOP.value] = True
|
|
||||||
elif issue['type'] == 'drupal':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.DRUPAL.value] = True
|
|
||||||
elif issue['type'] == 'zerologon':
|
|
||||||
if not issue['password_restored']:
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.ZEROLOGON_PASSWORD_RESTORE_FAILED.value] = True
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.ZEROLOGON.value] = True
|
|
||||||
elif issue['type'].endswith('_password') and issue['password'] in config_passwords and \
|
|
||||||
issue['username'] in config_users or issue['type'] == 'ssh':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.WEAK_PASSWORD.value] = True
|
|
||||||
elif issue['type'] == 'strong_users_on_crit':
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.PTH_CRIT_SERVICES_ACCESS.value] = True
|
|
||||||
elif issue['type'].endswith('_pth') or issue['type'].endswith('_password'):
|
|
||||||
issues_byte_array[ReportService.ISSUES_DICT.STOLEN_CREDS.value] = True
|
|
||||||
|
|
||||||
return issues_byte_array
|
issue_set.add(issue['type'])
|
||||||
|
|
||||||
|
return issue_set
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_warnings_overview(issues, cross_segment_issues):
|
def _is_weak_credential_issue(issue: dict, config_usernames: List[str], config_passwords: List[str]) -> bool:
|
||||||
warnings_byte_array = [False] * len(ReportService.WARNINGS_DICT)
|
# Only credential exploiter issues have 'credential_type'
|
||||||
|
return 'credential_type' in issue and \
|
||||||
|
issue['credential_type'] == CredentialType.PASSWORD.value and \
|
||||||
|
issue['password'] in config_passwords and \
|
||||||
|
issue['username'] in config_usernames
|
||||||
|
|
||||||
for machine in issues:
|
@staticmethod
|
||||||
for issue in issues[machine]:
|
def _is_stolen_credential_issue(issue: dict) -> bool:
|
||||||
if issue['type'] == 'island_cross_segment':
|
# Only credential exploiter issues have 'credential_type'
|
||||||
warnings_byte_array[ReportService.WARNINGS_DICT.CROSS_SEGMENT.value] = True
|
return 'credential_type' in issue and \
|
||||||
elif issue['type'] == 'tunnel':
|
(issue['credential_type'] == CredentialType.PASSWORD.value or
|
||||||
warnings_byte_array[ReportService.WARNINGS_DICT.TUNNEL.value] = True
|
issue['credential_type'] == CredentialType.HASH.value)
|
||||||
elif issue['type'] == 'shared_admins':
|
|
||||||
warnings_byte_array[ReportService.WARNINGS_DICT.SHARED_LOCAL_ADMIN.value] = True
|
|
||||||
elif issue['type'] == 'shared_passwords':
|
|
||||||
warnings_byte_array[ReportService.WARNINGS_DICT.SHARED_PASSWORDS.value] = True
|
|
||||||
|
|
||||||
if len(cross_segment_issues) != 0:
|
|
||||||
warnings_byte_array[ReportService.WARNINGS_DICT.CROSS_SEGMENT.value] = True
|
|
||||||
|
|
||||||
return warnings_byte_array
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_report_generated():
|
def is_report_generated():
|
||||||
|
@ -780,8 +561,7 @@ class ReportService:
|
||||||
'config_scan': ReportService.get_config_scan(),
|
'config_scan': ReportService.get_config_scan(),
|
||||||
'monkey_start_time': ReportService.get_first_monkey_time().strftime("%d/%m/%Y %H:%M:%S"),
|
'monkey_start_time': ReportService.get_first_monkey_time().strftime("%d/%m/%Y %H:%M:%S"),
|
||||||
'monkey_duration': ReportService.get_monkey_duration(),
|
'monkey_duration': ReportService.get_monkey_duration(),
|
||||||
'issues': ReportService.get_issues_overview(issues, config_users, config_passwords),
|
'issues': ReportService.get_issue_set(issues, config_users, config_passwords),
|
||||||
'warnings': ReportService.get_warnings_overview(issues, cross_segment_issues),
|
|
||||||
'cross_segment_issues': cross_segment_issues
|
'cross_segment_issues': cross_segment_issues
|
||||||
},
|
},
|
||||||
'glance':
|
'glance':
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import datetime
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from monkey_island.cc.services.reporting.report import ReportService
|
||||||
|
|
||||||
|
NODE_DICT = {
|
||||||
|
'id': '602f62118e30cf35830ff8e4',
|
||||||
|
'label': 'WinDev2010Eval.mshome.net',
|
||||||
|
'group': 'monkey_windows',
|
||||||
|
'os': 'windows',
|
||||||
|
'dead': True,
|
||||||
|
'exploits': [{'result': True,
|
||||||
|
'exploiter': 'DrupalExploiter',
|
||||||
|
'info': {'display_name': 'Drupal Server',
|
||||||
|
'started': datetime.datetime(2021, 2, 19, 9, 0, 14, 950000),
|
||||||
|
'finished': datetime.datetime(2021, 2, 19, 9, 0, 14, 950000),
|
||||||
|
'vulnerable_urls': [],
|
||||||
|
'vulnerable_ports': [],
|
||||||
|
'executed_cmds': []},
|
||||||
|
'attempts': [],
|
||||||
|
'timestamp': datetime.datetime(2021, 2, 19, 9, 0, 14, 984000),
|
||||||
|
'origin': 'MonkeyIsland : 192.168.56.1'},
|
||||||
|
|
||||||
|
{'result': True,
|
||||||
|
'exploiter': 'ElasticGroovyExploiter',
|
||||||
|
'info': {'display_name': 'Elastic search',
|
||||||
|
'started': datetime.datetime(2021, 2, 19, 9, 0, 15, 16000),
|
||||||
|
'finished': datetime.datetime(2021, 2, 19, 9, 0, 15, 17000),
|
||||||
|
'vulnerable_urls': [], 'vulnerable_ports': [], 'executed_cmds': []},
|
||||||
|
'attempts': [],
|
||||||
|
'timestamp': datetime.datetime(2021, 2, 19, 9, 0, 15, 60000),
|
||||||
|
'origin': 'MonkeyIsland : 192.168.56.1'}]
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DICT_DUPLICATE_EXPLOITS = deepcopy(NODE_DICT)
|
||||||
|
NODE_DICT_DUPLICATE_EXPLOITS['exploits'][1] = NODE_DICT_DUPLICATE_EXPLOITS['exploits'][0]
|
||||||
|
|
||||||
|
NODE_DICT_FAILED_EXPLOITS = deepcopy(NODE_DICT)
|
||||||
|
NODE_DICT_FAILED_EXPLOITS['exploits'][0]['result'] = False
|
||||||
|
NODE_DICT_FAILED_EXPLOITS['exploits'][1]['result'] = False
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_exploits_used_on_node():
|
||||||
|
exploits = ReportService.get_exploits_used_on_node(NODE_DICT)
|
||||||
|
assert sorted(exploits) == sorted(['ElasticGroovyExploiter', 'DrupalExploiter'])
|
||||||
|
|
||||||
|
exploits = ReportService.get_exploits_used_on_node(NODE_DICT_DUPLICATE_EXPLOITS)
|
||||||
|
assert exploits == ['DrupalExploiter']
|
||||||
|
|
||||||
|
exploits = ReportService.get_exploits_used_on_node(NODE_DICT_FAILED_EXPLOITS)
|
||||||
|
assert exploits == []
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
class IssueDescriptor {
|
||||||
|
constructor(name, overviewComponent, reportComponent) {
|
||||||
|
this.name = name;
|
||||||
|
this.overviewComponent = overviewComponent;
|
||||||
|
this.reportComponent = reportComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function AzurePasswordIssueOverview() {
|
||||||
|
return (<li>Azure machines expose plaintext passwords. (<a
|
||||||
|
href="https://www.guardicore.com/2018/03/recovering-plaintext-passwords-azure/"
|
||||||
|
>More info</a>)</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AzurePasswordIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Delete VM Access plugin configuration files.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
Credentials could be stolen from <span
|
||||||
|
className="badge badge-primary">{issue.machine}</span> for the following users <span
|
||||||
|
className="badge badge-primary">{issue.users}</span>. Read more about the security issue and remediation <a
|
||||||
|
href="https://www.guardicore.com/2018/03/recovering-plaintext-passwords-azure/"
|
||||||
|
>here</a>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
import React from 'react';
|
||||||
|
import CollapsibleWellComponent from '../CollapsibleWell';
|
||||||
|
import {generateInfoBadges} from './utils';
|
||||||
|
|
||||||
|
export function crossSegmentIssueOverview() {
|
||||||
|
return (<li key="segmentation">Weak segmentation - Machines from
|
||||||
|
different segments are able to communicate.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCrossSegmentIssue(crossSegmentIssue) {
|
||||||
|
let crossSegmentIssueOverview = 'Communication possible from '
|
||||||
|
+ `${crossSegmentIssue['source_subnet']} to ${crossSegmentIssue['target_subnet']}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={crossSegmentIssueOverview}>
|
||||||
|
{crossSegmentIssueOverview}
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
<ul className='cross-segment-issues'>
|
||||||
|
{crossSegmentIssue['issues'].map(
|
||||||
|
issue => generateCrossSegmentIssueListItem(issue)
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCrossSegmentIssueListItem(issue) {
|
||||||
|
if (issue['is_self']) {
|
||||||
|
return this.generateCrossSegmentSingleHostMessage(issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.generateCrossSegmentMultiHostMessage(issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCrossSegmentSingleHostMessage(issue) {
|
||||||
|
return (
|
||||||
|
<li key={issue['hostname']}>
|
||||||
|
{`Machine ${issue['hostname']} has both ips: ${issue['source']} and ${issue['target']}`}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCrossSegmentMultiHostMessage(issue) {
|
||||||
|
return (
|
||||||
|
<li key={issue['source'] + issue['target']}>
|
||||||
|
IP {issue['source']} ({issue['hostname']}) was able to communicate with
|
||||||
|
IP {issue['target']} using:
|
||||||
|
<ul>
|
||||||
|
{issue['icmp'] && <li key='icmp'>ICMP</li>}
|
||||||
|
{this.generateCrossSegmentServiceListItems(issue)}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCrossSegmentServiceListItems(issue) {
|
||||||
|
let service_list_items = [];
|
||||||
|
|
||||||
|
for (const [service, info] of Object.entries(issue['services'])) {
|
||||||
|
service_list_items.push(
|
||||||
|
<li key={service}>
|
||||||
|
<span className='cross-segment-service'>{service}</span> ({info['display_name']})
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return service_list_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateIslandCrossSegmentIssue(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Segment your network and make sure there is no communication between machines from different segments.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The network can probably be segmented. A monkey instance on <span
|
||||||
|
className="badge badge-primary">{issue.machine}</span> in the
|
||||||
|
networks {generateInfoBadges(issue.networks)}
|
||||||
|
could directly access the Monkey Island server in the
|
||||||
|
networks {generateInfoBadges(issue.server_networks)}.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function DrupalIssueOverview() {
|
||||||
|
return (<li>Drupal server/s are vulnerable to <a
|
||||||
|
href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6340">CVE-2019-6340</a>.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DrupalIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Upgrade Drupal server to versions 8.5.11, 8.6.10, or later.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
Drupal server at <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span
|
||||||
|
className="badge badge-danger">remote command execution</span> attack.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible because the server is using an old version of Drupal, for which REST API is
|
||||||
|
enabled. For possible workarounds, fixes and more info read
|
||||||
|
<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6340">here</a>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function ElasticIssueOverview() {
|
||||||
|
return (<li>Elasticsearch servers are vulnerable to <a
|
||||||
|
href="https://www.cvedetails.com/cve/cve-2015-1427">CVE-2015-1427</a>.
|
||||||
|
</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ElasticIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Update your Elastic Search server to version 1.4.3 and up.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to an <span
|
||||||
|
className="badge badge-danger">Elastic Groovy</span> attack.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function HadoopIssueOverview() {
|
||||||
|
return (<li>Hadoop/Yarn servers are vulnerable to remote code execution.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HadoopIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Run Hadoop in secure mode (<a
|
||||||
|
href="http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/SecureMode.html">
|
||||||
|
add Kerberos authentication</a>).
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The Hadoop server at <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span
|
||||||
|
className="badge badge-danger">remote code execution</span> attack.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible due to default Hadoop/Yarn configuration being insecure.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function MS08_067IssueOverview() {
|
||||||
|
return (<li>Machines are vulnerable to ‘Conficker’ (<a
|
||||||
|
href="https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2008/ms08-067"
|
||||||
|
>MS08-067</a>). </li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MS08_067IssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Install the latest Windows updates or upgrade to a newer operating system.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">Conficker</span> attack.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible because the target machine used an outdated and unpatched operating system
|
||||||
|
vulnerable to Conficker.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function MssqlIssueOverview() {
|
||||||
|
return (<li>MS-SQL servers are vulnerable to remote code execution via xp_cmdshell command.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MssqlIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Disable the xp_cmdshell option.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">MSSQL exploit attack</span>.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible because the target machine used an outdated MSSQL server configuration allowing
|
||||||
|
the usage of the xp_cmdshell command. To learn more about how to disable this feature, read <a
|
||||||
|
href="https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/xp-cmdshell-server-configuration-option?view=sql-server-2017">
|
||||||
|
Microsoft's documentation. </a>
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function PthCriticalServiceIssueOverview() {
|
||||||
|
return (<li>Mimikatz found login credentials of a user who has admin access to a server defined as
|
||||||
|
critical.</li>)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function SambacryIssueOverview() {
|
||||||
|
return (<li>Samba servers are vulnerable to ‘SambaCry’ (<a
|
||||||
|
href="https://www.samba.org/samba/security/CVE-2017-7494.html"
|
||||||
|
>CVE-2017-7494</a>).</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SambacryIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Change <span className="badge badge-success">{issue.username}</span>'s password to a complex one-use password
|
||||||
|
that is not shared with other computers on the network.
|
||||||
|
<br/>
|
||||||
|
Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">SambaCry</span> attack.
|
||||||
|
<br/>
|
||||||
|
The Monkey authenticated over the SMB protocol with user <span
|
||||||
|
className="badge badge-success">{issue.username}</span> and its password, and used the SambaCry
|
||||||
|
vulnerability.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function sharedPasswordsIssueOverview() {
|
||||||
|
return (<li key={"shared_passwords"}>Multiple users have the same password</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sharedAdminsDomainIssueOverview() {
|
||||||
|
return (<li key={"admin_domains"}>Shared local administrator account - Different machines have the same account as a local
|
||||||
|
administrator.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateSharedCredsDomainIssue(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Some domain users are sharing passwords, this should be fixed by changing passwords.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
These users are sharing access password:
|
||||||
|
{this.generateInfoBadges(issue.shared_with)}.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateSharedCredsIssue(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Some users are sharing passwords, this should be fixed by changing passwords.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
These users are sharing access password:
|
||||||
|
{this.generateInfoBadges(issue.shared_with)}.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateSharedLocalAdminsIssue(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Make sure the right administrator accounts are managing the right machines, and that there isn’t an
|
||||||
|
unintentional local
|
||||||
|
admin sharing.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
Here is a list of machines which the account <span
|
||||||
|
className="badge badge-primary">{issue.username}</span> is defined as an administrator:
|
||||||
|
{this.generateInfoBadges(issue.shared_machines)}
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function ShellShockIssueOverview() {
|
||||||
|
return (<li>Machines are vulnerable to ‘Shellshock’ (<a
|
||||||
|
href="https://www.cvedetails.com/cve/CVE-2014-6271">CVE-2014-6271</a>).
|
||||||
|
</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function generateShellshockPathListBadges(paths) {
|
||||||
|
return paths.map(path => <span className="badge badge-warning" style={{margin: '2px'}} key={path}>{path}</span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ShellShockIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Update your Bash to a ShellShock-patched version.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">ShellShock</span> attack.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible because the HTTP server running on TCP port <span
|
||||||
|
className="badge badge-info">{issue.port}</span> was vulnerable to a shell injection attack on the
|
||||||
|
paths: {generateShellshockPathListBadges(issue.paths)}.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from 'react';
|
||||||
|
import CollapsibleWellComponent from '../CollapsibleWell';
|
||||||
|
|
||||||
|
export function generateSmbPasswordReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Change <span className="badge badge-success">{issue.username}</span>'s password to a complex one-use password
|
||||||
|
that is not shared with other computers on the network.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">SMB</span> attack.
|
||||||
|
<br/>
|
||||||
|
The Monkey authenticated over the SMB protocol with user <span
|
||||||
|
className="badge badge-success">{issue.username}</span> and its password.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateSmbPthReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Change <span className="badge badge-success">{issue.username}</span>'s password to a complex one-use password
|
||||||
|
that is not shared with other computers on the network.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">SMB</span> attack.
|
||||||
|
<br/>
|
||||||
|
The Monkey used a pass-the-hash attack over SMB protocol with user <span
|
||||||
|
className="badge badge-success">{issue.username}</span>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function SshIssueOverview() {
|
||||||
|
return (<li>Stolen SSH keys are used to exploit other machines.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ShhIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Change <span className="badge badge-success">{issue.username}</span>'s password to a complex one-use password
|
||||||
|
that is not shared with other computers on the network.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">SSH</span> attack.
|
||||||
|
<br/>
|
||||||
|
The Monkey authenticated over the SSH protocol with user <span
|
||||||
|
className="badge badge-success">{issue.username}</span> and its password.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateSshKeysReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Protect <span className="badge badge-success">{issue.ssh_key}</span> private key with a pass phrase.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">SSH</span> attack.
|
||||||
|
<br/>
|
||||||
|
The Monkey authenticated over the SSH protocol with private key <span
|
||||||
|
className="badge badge-success">{issue.ssh_key}</span>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function StolenCredsIssueOverview() {
|
||||||
|
return (<li>Stolen credentials are used to exploit other machines.</li>)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function generateStrongUsersOnCritIssue(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
This critical machine is open to attacks via strong users with access to it.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The services: {this.generateInfoBadges(issue.services)} have been found on the machine
|
||||||
|
thus classifying it as a critical machine.
|
||||||
|
These users has access to it:
|
||||||
|
{this.generateInfoBadges(issue.threatening_users)}.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function Struts2IssueOverview() {
|
||||||
|
return (<li>Struts2 servers are vulnerable to remote code execution. (<a
|
||||||
|
href="https://cwiki.apache.org/confluence/display/WW/S2-045">
|
||||||
|
CVE-2017-5638</a>)</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Struts2IssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
Struts2 server at <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span
|
||||||
|
className="badge badge-danger">remote code execution</span> attack.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible because the server is using an old version of Jakarta based file upload
|
||||||
|
Multipart parser. For possible work-arounds and more info read <a
|
||||||
|
href="https://cwiki.apache.org/confluence/display/WW/S2-045"
|
||||||
|
>here</a>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function generateTunnelIssueOverview(){
|
||||||
|
return (<li key="tunnel">Weak segmentation - Machines were able to communicate over unused ports.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateTunnelIssue(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Use micro-segmentation policies to disable communication other than the required.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
Machines are not locked down at port level. Network tunnel was set up from <span
|
||||||
|
className="badge badge-primary">{issue.machine}</span> to <span
|
||||||
|
className="badge badge-primary">{issue.dest}</span>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function VsftpdIssueOverview() {
|
||||||
|
return (<li>VSFTPD is vulnerable to <a
|
||||||
|
href="https://www.rapid7.com/db/modules/exploit/unix/ftp/vsftpd_234_backdoor">CVE-2011-2523</a>.
|
||||||
|
</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function VsftpdIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Update your VSFTPD server to the latest version vsftpd-3.0.3.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) has a backdoor running at
|
||||||
|
port <span
|
||||||
|
className="badge badge-danger">6200</span>.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible because the VSFTPD server was not patched against CVE-2011-2523.
|
||||||
|
<br/><br/>In July 2011, it was discovered that vsftpd version 2.3.4 downloadable from the master site had been
|
||||||
|
compromised.
|
||||||
|
Users logging into a compromised vsftpd-2.3.4 server may issue a ":)" smileyface as the username and gain a
|
||||||
|
command
|
||||||
|
shell on port 6200.
|
||||||
|
<br/><br/>
|
||||||
|
The Monkey executed commands by first logging in with ":)" in the username and then sending commands to the
|
||||||
|
backdoor
|
||||||
|
at port 6200.
|
||||||
|
<br/><br/>Read more about the security issue and remediation <a
|
||||||
|
href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2523"
|
||||||
|
>here</a>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function WeakPasswordIssueOverview() {
|
||||||
|
return (<li>Machines are accessible using passwords supplied by the user during the Monkey’s
|
||||||
|
configuration.</li>)
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function WebLogicIssueOverview() {
|
||||||
|
return (<li>Oracle WebLogic servers are susceptible to a remote code execution vulnerability.</li>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WebLogicIssueReport(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Update Oracle WebLogic server to the latest supported version.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
Oracle WebLogic server at <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to one of <span
|
||||||
|
className="badge badge-danger">remote code execution</span> attacks.
|
||||||
|
<br/>
|
||||||
|
The attack was made possible due to one of the following vulnerabilities:
|
||||||
|
<a href={'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-10271'}> CVE-2017-10271</a> or
|
||||||
|
<a href={'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-2725'}> CVE-2019-2725</a>
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from 'react';
|
||||||
|
import CollapsibleWellComponent from '../CollapsibleWell';
|
||||||
|
|
||||||
|
export function generateWmiPasswordIssue(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Change <span className="badge badge-success">{issue.username}</span>'s password to a complex one-use password
|
||||||
|
that is not shared with other computers on the network.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">WMI</span> attack.
|
||||||
|
<br/>
|
||||||
|
The Monkey authenticated over the WMI protocol with user <span
|
||||||
|
className="badge badge-success">{issue.username}</span> and its password.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateWmiPthIssue(issue) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Change <span className="badge badge-success">{issue.username}</span>'s password to a complex one-use password
|
||||||
|
that is not shared with other computers on the network.
|
||||||
|
<CollapsibleWellComponent>
|
||||||
|
The machine <span className="badge badge-primary">{issue.machine}</span> (<span
|
||||||
|
className="badge badge-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span
|
||||||
|
className="badge badge-danger">WMI</span> attack.
|
||||||
|
<br/>
|
||||||
|
The Monkey used a pass-the-hash attack over WMI protocol with user <span
|
||||||
|
className="badge badge-success">{issue.username}</span>.
|
||||||
|
</CollapsibleWellComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function generateInfoBadges(data_array) {
|
||||||
|
return data_array.map(badge_data => <span key={badge_data} className="badge badge-info"
|
||||||
|
style={{margin: '2px'}}>{badge_data}</span>);
|
||||||
|
}
|
Loading…
Reference in New Issue