diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/__init__.py b/monkey/monkey_island/cc/services/reporting/issue_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/__init__.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py new file mode 100644 index 000000000..65964b5de --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py @@ -0,0 +1,42 @@ +from dataclasses import dataclass +from enum import Enum +from typing import Type, Dict + +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.cred_exploit import \ + CredExploitProcessor +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ExploitProcessor +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.shellshock_exploit import \ + ShellShockExploitProcessor +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.zerologon import \ + ZerologonExploitProcessor + + +@dataclass +class ExploiterDescriptor: + # Must match with class names of exploiters in Infection Monkey code + class_name: str + display_name: str + processor: Type[object] = 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) + ZEROLOGON = ExploiterDescriptor('ZerologonExploiter', 'Zerologon Exploiter', ZerologonExploitProcessor) + + @staticmethod + def get_by_class_name(class_name: str) -> ExploiterDescriptor: + return [descriptor.value + for descriptor in ExploiterDescriptorEnum + if descriptor.value.class_name == class_name][0] diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py new file mode 100644 index 000000000..3e1cb0601 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py @@ -0,0 +1,23 @@ +from dataclasses import dataclass +from enum import Enum +from typing import Union, List + + +class CredentialType(Enum): + PASSWORD = 'password' + HASH = 'hash' + KEY = 'key' + + +@dataclass +class ExploiterReportInfo: + machine: str + ip_address: str + type: str + username: Union[str, None] = None + credential_type: Union[CredentialType, None] = None + ssh_key: Union[str, None] = None + password: Union[str, None] = None + port: Union[str, None] = None + paths: Union[List[str], None] = None + password_restored: Union[bool, None] = None diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/__init__.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/cred_exploit.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/cred_exploit.py new file mode 100644 index 000000000..43156561c --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/cred_exploit.py @@ -0,0 +1,24 @@ +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_report_info import \ + ExploiterReportInfo, CredentialType +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ExploitProcessor + + +class CredExploitProcessor: + + @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 diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/exploit.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/exploit.py new file mode 100644 index 000000000..c541ba252 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/exploit.py @@ -0,0 +1,12 @@ +from monkey_island.cc.services.node import NodeService +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_report_info import \ + ExploiterReportInfo + + +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) diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/shellshock_exploit.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/shellshock_exploit.py new file mode 100644 index 000000000..d33bd8615 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/shellshock_exploit.py @@ -0,0 +1,14 @@ +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import \ + ExploiterReportInfo, ExploitProcessor + + +class ShellShockExploitProcessor: + + @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 diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py new file mode 100644 index 000000000..e0be6cd42 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py @@ -0,0 +1,11 @@ +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ExploitProcessor, \ + ExploiterReportInfo + + +class ZerologonExploitProcessor: + + @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) + exploit_info.password_restored = exploit_dict['data']['info']['password_restored'] + return exploit_info diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index a23aa6d85..6430a2559 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -2,7 +2,7 @@ import functools import ipaddress import itertools import logging -from enum import Enum +from typing import List from bson import json_util @@ -17,6 +17,11 @@ from common.config_value_paths import (EXPLOITER_CLASSES_PATH, LOCAL_NETWORK_SCA USER_LIST_PATH) 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.reporting.issue_processing.exploit_processing.exploiter_descriptor_enum import ExploiterDescriptorEnum +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.cred_exploit import \ + CredentialType +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import \ + ExploiterReportInfo 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_generation_synchronisation import safe_generate_regular_report @@ -27,51 +32,11 @@ logger = logging.getLogger(__name__) class ReportService: - def __init__(self): - pass - EXPLOIT_DISPLAY_DICT = \ - { - 'SmbExploiter': 'SMB Exploiter', - '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 + class DerivedIssueEnum: + WEAK_PASSWORD = "weak_password" + STOLEN_CREDS = "stolen_creds" + ZEROLOGON_PASS_RESTORE_FAILED = "zerologon_pass_restore_failed" @staticmethod def get_first_monkey_time(): @@ -169,9 +134,7 @@ class ReportService: 'label': exploited_node['label'], 'ip_addresses': exploited_node['ip_addresses'], 'domain_name': exploited_node['domain_name'], - 'exploits': list(set( - [ReportService.EXPLOIT_DISPLAY_DICT[exploit['exploiter']] for exploit in exploited_node['exploits'] - if exploit['result']])) + 'exploits': ReportService.get_exploits_used_on_node(exploited_node) } for exploited_node in exploited] @@ -179,6 +142,12 @@ class ReportService: return exploited + @staticmethod + def get_exploits_used_on_node(node: dict) -> List[str]: + return list(set([ExploiterDescriptorEnum.get_by_class_name(exploit['exploiter']).display_name + for exploit in node['exploits'] + if exploit['result']])) + @staticmethod def get_stolen_creds(): creds = [] @@ -281,151 +250,15 @@ class ReportService: return creds @staticmethod - def process_general_exploit(exploit): - 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): + def process_exploit(exploit) -> ExploiterReportInfo: exploiter_type = exploit['data']['exploiter'] - EXPLOIT_PROCESS_FUNCTION_DICT = { - 'SmbExploiter': ReportService.process_smb_exploit, - 'WmiExploiter': ReportService.process_wmi_exploit, - 'SSHExploiter': ReportService.process_ssh_exploit, - '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) + exploiter_descriptor = ExploiterDescriptorEnum.get_by_class_name(exploiter_type) + processor = exploiter_descriptor.processor() + exploiter_info = processor.get_exploit_info_by_dict(exploiter_type, exploit) + return exploiter_info @staticmethod - def get_exploits(): + def get_exploits() -> List[dict]: query = [{'$match': {'telem_category': 'exploit', 'data.result': True}}, {'$group': {'_id': {'ip_address': '$data.machine.ip_addr'}, 'data': {'$first': '$$ROOT'}, @@ -435,7 +268,7 @@ class ReportService: for exploit in mongo.db.telemetry.aggregate(query): new_exploit = ReportService.process_exploit(exploit) if new_exploit not in exploits: - exploits.append(new_exploit) + exploits.append(new_exploit.__dict__) return exploits @staticmethod @@ -585,7 +418,8 @@ class ReportService: @staticmethod def get_cross_segment_issues(): 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 = [] @@ -599,7 +433,6 @@ class ReportService: @staticmethod def get_domain_issues(): - ISSUE_GENERATORS = [ PTHReportService.get_duplicated_passwords_issues, PTHReportService.get_shared_admins_issues, @@ -627,32 +460,6 @@ class ReportService: else: 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 def get_manual_monkeys(): return [monkey['hostname'] for monkey in mongo.db.monkey.find({}, {'hostname': 1, 'parent': 1, 'guid': 1}) if @@ -677,8 +484,8 @@ class ReportService: if exploits == default_exploits: return ['default'] - return [ReportService.EXPLOIT_DISPLAY_DICT[exploit] for exploit in - exploits] + return [ExploiterDescriptorEnum.get_by_class_name(exploit).display_name + for exploit in exploits] @staticmethod def get_config_ips(): @@ -689,68 +496,41 @@ class ReportService: return ConfigService.get_config_value(LOCAL_NETWORK_SCAN_PATH, True, True) @staticmethod - def get_issues_overview(issues, config_users, config_passwords): - issues_byte_array = [False] * len(ReportService.ISSUES_DICT) + def get_issue_set(issues, config_users, config_passwords): + issue_set = set() for machine in issues: for issue in issues[machine]: - if issue['type'] == 'elastic': - issues_byte_array[ReportService.ISSUES_DICT.ELASTIC.value] = True - elif issue['type'] == 'sambacry': - issues_byte_array[ReportService.ISSUES_DICT.SAMBACRY.value] = True - elif issue['type'] == 'vsftp': - issues_byte_array[ReportService.ISSUES_DICT.VSFTPD.value] = True - elif issue['type'] == 'shellshock': - 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 + if ReportService._is_weak_credential_issue(issue, config_users, config_passwords): + issue_set.add(ReportService.DerivedIssueEnum.WEAK_PASSWORD) + elif ReportService._is_stolen_credential_issue(issue): + issue_set.add(ReportService.DerivedIssueEnum.STOLEN_CREDS) + elif ReportService._is_zerologon_pass_restore_failed(issue): + issue_set.add(ReportService.DerivedIssueEnum.ZEROLOGON_PASS_RESTORE_FAILED) - return issues_byte_array + issue_set.add(issue['type']) + + return issue_set @staticmethod - def get_warnings_overview(issues, cross_segment_issues): - warnings_byte_array = [False] * len(ReportService.WARNINGS_DICT) + def _is_weak_credential_issue(issue: dict, config_usernames: List[str], config_passwords: List[str]) -> bool: + # 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: - for issue in issues[machine]: - if issue['type'] == 'island_cross_segment': - warnings_byte_array[ReportService.WARNINGS_DICT.CROSS_SEGMENT.value] = True - elif issue['type'] == 'tunnel': - warnings_byte_array[ReportService.WARNINGS_DICT.TUNNEL.value] = True - 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 + @staticmethod + def _is_stolen_credential_issue(issue: dict) -> bool: + # Only credential exploiter issues have 'credential_type' + return 'credential_type' in issue and \ + (issue['credential_type'] == CredentialType.PASSWORD.value or + issue['credential_type'] == CredentialType.HASH.value) - if len(cross_segment_issues) != 0: - warnings_byte_array[ReportService.WARNINGS_DICT.CROSS_SEGMENT.value] = True - - return warnings_byte_array + @staticmethod + def _is_zerologon_pass_restore_failed(issue: dict): + return issue['type'] == ExploiterDescriptorEnum.ZEROLOGON.value.class_name \ + and not issue['password_restored'] @staticmethod def is_report_generated(): @@ -763,6 +543,7 @@ class ReportService: issues = ReportService.get_issues() config_users = ReportService.get_config_users() config_passwords = ReportService.get_config_passwords() + issue_set = ReportService.get_issue_set(issues, config_users, config_passwords) cross_segment_issues = ReportService.get_cross_segment_issues() monkey_latest_modify_time = Monkey.get_latest_modifytime() @@ -780,8 +561,7 @@ class ReportService: 'config_scan': ReportService.get_config_scan(), 'monkey_start_time': ReportService.get_first_monkey_time().strftime("%d/%m/%Y %H:%M:%S"), 'monkey_duration': ReportService.get_monkey_duration(), - 'issues': ReportService.get_issues_overview(issues, config_users, config_passwords), - 'warnings': ReportService.get_warnings_overview(issues, cross_segment_issues), + 'issues': issue_set, 'cross_segment_issues': cross_segment_issues }, 'glance': @@ -809,6 +589,32 @@ class ReportService: return report + @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 def encode_dot_char_before_mongo_insert(report_dict): """ diff --git a/monkey/monkey_island/cc/services/reporting/test_report.py b/monkey/monkey_island/cc/services/reporting/test_report.py new file mode 100644 index 000000000..5f95eae47 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/test_report.py @@ -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(['Elastic Groovy Exploiter', 'Drupal Server Exploiter']) + + exploits = ReportService.get_exploits_used_on_node(NODE_DICT_DUPLICATE_EXPLOITS) + assert exploits == ['Drupal Server Exploiter'] + + exploits = ReportService.get_exploits_used_on_node(NODE_DICT_FAILED_EXPLOITS) + assert exploits == [] diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js index e3a1621eb..364e77b77 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js @@ -5,7 +5,6 @@ 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'; -import CollapsibleWellComponent from 'components/report-components/security/CollapsibleWell'; import {Line} from 'rc-progress'; import AuthComponent from '../AuthComponent'; import StrongUsers from 'components/report-components/security/StrongUsers'; @@ -13,49 +12,190 @@ import ReportHeader, {ReportTypes} from './common/ReportHeader'; import ReportLoader from './common/ReportLoader'; import SecurityIssuesGlance from './common/SecurityIssuesGlance'; import PrintReportButton from './common/PrintReportButton'; -import WarningIcon from '../ui-components/WarningIcon'; -import {Button} from 'react-bootstrap'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {faMinus} from '@fortawesome/free-solid-svg-icons/faMinus'; import guardicoreLogoImage from '../../images/guardicore-logo.png' import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons'; import '../../styles/App.css'; +import {smbPasswordReport, smbPthReport} from './security/issues/SmbIssue'; +import {struts2IssueOverview, struts2IssueReport} from './security/issues/Struts2Issue'; +import {webLogicIssueOverview, webLogicIssueReport} from './security/issues/WebLogicIssue'; +import {hadoopIssueOverview, hadoopIssueReport} from './security/issues/HadoopIssue'; +import {mssqlIssueOverview, mssqlIssueReport} from './security/issues/MssqlIssue'; +import {drupalIssueOverview, drupalIssueReport} from './security/issues/DrupalIssue'; +import {vsftpdIssueOverview, vsftpdIssueReport} from './security/issues/VsftpdIssue'; +import {wmiPasswordIssueReport, wmiPthIssueReport} from './security/issues/WmiIssue'; +import {sshKeysReport, shhIssueReport, sshIssueOverview} from './security/issues/SshIssue'; +import {sambacryIssueOverview, sambacryIssueReport} from './security/issues/SambacryIssue'; +import {elasticIssueOverview, elasticIssueReport} from './security/issues/ElasticIssue'; +import {shellShockIssueOverview, shellShockIssueReport} from './security/issues/ShellShockIssue'; +import {ms08_067IssueOverview, ms08_067IssueReport} from './security/issues/MS08_067Issue'; +import { + crossSegmentIssueOverview, + crossSegmentIssueReport, + islandCrossSegmentIssueReport +} from './security/issues/CrossSegmentIssue'; +import { + sharedCredsDomainIssueReport, sharedCredsIssueReport, sharedLocalAdminsIssueReport, + sharedAdminsDomainIssueOverview, + sharedPasswordsIssueOverview +} from './security/issues/SharedPasswordsIssue'; +import {tunnelIssueReport, tunnelIssueOverview} from './security/issues/TunnelIssue'; +import {stolenCredsIssueOverview} from './security/issues/StolenCredsIssue'; +import {weakPasswordIssueOverview} from './security/issues/WeakPasswordIssue'; +import {azurePasswordIssueOverview, azurePasswordIssueReport} from './security/issues/AzurePasswordIssue'; +import {strongUsersOnCritIssueReport} from './security/issues/StrongUsersOnCritIssue'; +import { + zerologonIssueOverview, + zerologonIssueReport, + zerologonOverviewWithFailedPassResetWarning +} from './security/issues/ZerologonIssue'; class ReportPageComponent extends AuthComponent { - Issue = - { - 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 - }; + credentialTypes = { + PASSWORD: 'password', + HASH: 'hash', + KEY: 'key', + } - NotThreats = [this.Issue.ZEROLOGON_PASSWORD_RESTORE_FAILED]; + issueContentTypes = { + OVERVIEW: 'overview', + REPORT: 'report', + TYPE: 'type' + } - Warning = + issueTypes = { + WARNING: 'warning', + DANGER: 'danger' + } + + IssueDescriptorEnum = { - CROSS_SEGMENT: 0, - TUNNEL: 1, - SHARED_LOCAL_ADMIN: 2, - SHARED_PASSWORDS: 3, - SHARED_PASSWORDS_DOMAIN: 4 - }; + 'SmbExploiter': { + [this.issueContentTypes.REPORT]: { + [this.credentialTypes.PASSWORD]: smbPasswordReport, + [this.credentialTypes.HASH]: smbPthReport + }, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'Struts2Exploiter': { + [this.issueContentTypes.OVERVIEW]: struts2IssueOverview, + [this.issueContentTypes.REPORT]: struts2IssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'WebLogicExploiter': { + [this.issueContentTypes.OVERVIEW]: webLogicIssueOverview, + [this.issueContentTypes.REPORT]: webLogicIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'HadoopExploiter': { + [this.issueContentTypes.OVERVIEW]: hadoopIssueOverview, + [this.issueContentTypes.REPORT]: hadoopIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'MSSQLExploiter': { + [this.issueContentTypes.OVERVIEW]: mssqlIssueOverview, + [this.issueContentTypes.REPORT]: mssqlIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'DrupalExploiter': { + [this.issueContentTypes.OVERVIEW]: drupalIssueOverview, + [this.issueContentTypes.REPORT]: drupalIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'VSFTPDExploiter': { + [this.issueContentTypes.OVERVIEW]: vsftpdIssueOverview, + [this.issueContentTypes.REPORT]: vsftpdIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'WmiExploiter': { + [this.issueContentTypes.REPORT]: { + [this.credentialTypes.PASSWORD]: wmiPasswordIssueReport, + [this.credentialTypes.HASH]: wmiPthIssueReport + }, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'SSHExploiter': { + [this.issueContentTypes.OVERVIEW]: sshIssueOverview, + [this.issueContentTypes.REPORT]: { + [this.credentialTypes.PASSWORD]: shhIssueReport, + [this.credentialTypes.KEY]: sshKeysReport + }, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'SambaCryExploiter': { + [this.issueContentTypes.OVERVIEW]: sambacryIssueOverview, + [this.issueContentTypes.REPORT]: sambacryIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'ElasticGroovyExploiter': { + [this.issueContentTypes.OVERVIEW]: elasticIssueOverview, + [this.issueContentTypes.REPORT]: elasticIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'ShellShockExploiter': { + [this.issueContentTypes.OVERVIEW]: shellShockIssueOverview, + [this.issueContentTypes.REPORT]: shellShockIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'Ms08_067_Exploiter': { + [this.issueContentTypes.OVERVIEW]: ms08_067IssueOverview, + [this.issueContentTypes.REPORT]: ms08_067IssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'ZerologonExploiter': { + [this.issueContentTypes.OVERVIEW]: zerologonIssueOverview, + [this.issueContentTypes.REPORT]: zerologonIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'zerologon_pass_restore_failed': { + [this.issueContentTypes.OVERVIEW]: zerologonOverviewWithFailedPassResetWarning, + }, + 'island_cross_segment': { + [this.issueContentTypes.OVERVIEW]: crossSegmentIssueOverview, + [this.issueContentTypes.REPORT]: islandCrossSegmentIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.WARNING + }, + 'tunnel': { + [this.issueContentTypes.OVERVIEW]: tunnelIssueOverview, + [this.issueContentTypes.REPORT]: tunnelIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.WARNING + }, + 'shared_passwords': { + [this.issueContentTypes.OVERVIEW]: sharedPasswordsIssueOverview, + [this.issueContentTypes.REPORT]: sharedCredsIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.WARNING + }, + 'shared_admins_domain': { + [this.issueContentTypes.OVERVIEW]: sharedAdminsDomainIssueOverview, + [this.issueContentTypes.REPORT]: sharedLocalAdminsIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.WARNING + }, + 'shared_passwords_domain': { + [this.issueContentTypes.REPORT]: sharedCredsDomainIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.WARNING + }, + 'strong_users_on_crit': { + [this.issueContentTypes.REPORT]: strongUsersOnCritIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'azure_password': { + [this.issueContentTypes.OVERVIEW]: azurePasswordIssueOverview, + [this.issueContentTypes.REPORT]: azurePasswordIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'weak_password': { + [this.issueContentTypes.OVERVIEW]: weakPasswordIssueOverview, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'stolen_creds': { + [this.issueContentTypes.OVERVIEW]: stolenCredsIssueOverview, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + } + } constructor(props) { super(props); @@ -239,156 +379,23 @@ class ReportPageComponent extends AuthComponent { } generateReportFindingsSection() { + let overviews = this.getPotentialSecurityIssuesOverviews() return (

Security Findings

-
-

- Immediate Threats -

- { - this.state.report.overview.issues.filter(function (x) { - return x === true; - }).length > 0 ? -
- During this simulated attack the Monkey uncovered - {this.getThreatCount()} - : -
    - {this.state.report.overview.issues[this.Issue.STOLEN_SSH_KEYS] && -
  • Stolen SSH keys are used to exploit other machines.
  • } - {this.state.report.overview.issues[this.Issue.STOLEN_CREDS] && -
  • Stolen credentials are used to exploit other machines.
  • } - {this.state.report.overview.issues[this.Issue.ELASTIC] && -
  • Elasticsearch servers are vulnerable to - . -
  • } - {this.state.report.overview.issues[this.Issue.VSFTPD] && -
  • VSFTPD is vulnerable to - . -
  • } - {this.state.report.overview.issues[this.Issue.SAMBACRY] && -
  • Samba servers are vulnerable to ‘SambaCry’ ( - ). -
  • } - {this.state.report.overview.issues[this.Issue.SHELLSHOCK] && -
  • Machines are vulnerable to ‘Shellshock’ ( - ). -
  • } - {this.state.report.overview.issues[this.Issue.CONFICKER] && -
  • Machines are vulnerable to ‘Conficker’ ( - ). -
  • } - {this.state.report.overview.issues[this.Issue.WEAK_PASSWORD] && -
  • Machines are accessible using passwords supplied by the user during the Monkey’s - configuration.
  • } - {this.state.report.overview.issues[this.Issue.AZURE] && -
  • Azure machines expose plaintext passwords ( - ). -
  • } - {this.state.report.overview.issues[this.Issue.STRUTS2] && -
  • Struts2 servers are vulnerable to remote code execution ( - ). -
  • } - {this.state.report.overview.issues[this.Issue.WEBLOGIC] && -
  • Oracle WebLogic servers are susceptible to a remote code execution vulnerability.
  • } - {this.state.report.overview.issues[this.Issue.HADOOP] && -
  • Hadoop/Yarn servers are vulnerable to remote code execution.
  • } - {this.state.report.overview.issues[this.Issue.PTH_CRIT_SERVICES_ACCESS] && -
  • Mimikatz found login credentials of a user who has admin access to a server defined as - critical.
  • } - {this.state.report.overview.issues[this.Issue.MSSQL] && -
  • MS-SQL servers are vulnerable to remote code execution via xp_cmdshell command.
  • } - {this.state.report.overview.issues[this.Issue.DRUPAL] && -
  • Drupal servers are susceptible to a remote code execution vulnerability - (). -
  • - } - {this.generateZerologonOverview()} -
-
- : -
- During this simulated attack the Monkey uncovered 0 threats. -
- } -
+ {this.getImmediateThreats()}

Potential Security Issues

{ - this.state.report.overview.warnings.filter(function (x) { - return x === true; - }).length > 0 ? + overviews.length > 0 ?
The Monkey uncovered the following possible set of issues:
    - {this.state.report.overview.warnings[this.Warning.CROSS_SEGMENT] ? -
  • Weak segmentation - Machines from different segments are able - to - communicate.
  • : null} - {this.state.report.overview.warnings[this.Warning.TUNNEL] ? -
  • Weak segmentation - Machines were able to communicate over unused - ports.
  • : null} - {this.state.report.overview.warnings[this.Warning.SHARED_LOCAL_ADMIN] ? -
  • Shared local administrator account - Different machines - have the same account as a local - administrator.
  • : null} - {this.state.report.overview.warnings[this.Warning.SHARED_PASSWORDS] ? -
  • Multiple users have the same password
  • : null} + {this.getPotentialSecurityIssuesOverviews()}
: @@ -405,7 +412,7 @@ class ReportPageComponent extends AuthComponent {
The Monkey uncovered the following set of segmentation issues:
    - {this.state.report.overview.cross_segment_issues.map(x => this.generateCrossSegmentIssue(x))} + {this.state.report.overview.cross_segment_issues.map(x => crossSegmentIssueReport(x))}
@@ -416,53 +423,82 @@ class ReportPageComponent extends AuthComponent { ); } - getThreatCount() { - let threatCount = this.state.report.overview.issues.filter(function (x) { - return x === true; - }).length + getPotentialSecurityIssuesOverviews() { + let overviews = []; + let issues = this.state.report.overview.issues; - this.NotThreats.forEach(x => { - if (this.state.report.overview.issues[x] === true) { - threatCount -= 1; + for(let i=0; i < issues.length; i++) { + if (this.isIssuePotentialSecurityIssue(issues[i])) { + overviews.push(this.getIssueOverview(this.IssueDescriptorEnum[issues[i]])); } - }); - - if (threatCount === 1) - return '1 threat' - else - return threatCount + ' threats' + } + return overviews; } - generateZerologonOverview() { - let zerologonOverview = []; - if (this.state.report.overview.issues[this.Issue.ZEROLOGON]) { - zerologonOverview.push(<> - Some Windows domain controllers are vulnerable to 'Zerologon' ( - ). - ) + isIssuePotentialSecurityIssue(issueName) { + let issueDescriptor = this.IssueDescriptorEnum[issueName]; + return issueDescriptor.hasOwnProperty(this.issueContentTypes.TYPE) && + issueDescriptor[this.issueContentTypes.TYPE] === this.issueTypes.WARNING && + issueDescriptor.hasOwnProperty(this.issueContentTypes.OVERVIEW); + } + + getImmediateThreats() { + let threatCount = this.getImmediateThreatCount() + return ( +
+

+ Immediate Threats +

+
During this simulated attack the Monkey uncovered + { + <> + + {threatCount} threats + : + {this.getImmediateThreatsOverviews()} + + } +
+
) + } + + getImmediateThreatCount() { + let threatCount = 0; + let issues = this.state.report.overview.issues; + + for(let i=0; i < issues.length; i++) { + if(this.isIssueImmediateThreat(issues[i])) { + threatCount++; + } } - if (this.state.report.overview.issues[this.Issue.ZEROLOGON_PASSWORD_RESTORE_FAILED]) { - zerologonOverview.push( -
- - Automatic password restoration on a domain controller failed! - -
) + return threatCount; + } + + isIssueImmediateThreat(issueName) { + let issueDescriptor = this.IssueDescriptorEnum[issueName]; + return issueDescriptor.hasOwnProperty(this.issueContentTypes.TYPE) && + issueDescriptor[this.issueContentTypes.TYPE] === this.issueTypes.DANGER && + issueDescriptor.hasOwnProperty(this.issueContentTypes.OVERVIEW); + } + + getImmediateThreatsOverviews() { + let overviews = []; + let issues = this.state.report.overview.issues; + + for(let i=0; i < issues.length; i++) { + if (this.isIssueImmediateThreat(issues[i])) { + if (issues[i] === 'ZerologonExploiter' && issues.includes('zerologon_pass_restore_failed')){ + overviews.push(this.getIssueOverview(this.IssueDescriptorEnum['zerologon_pass_restore_failed'])); + } else { + overviews.push(this.getIssueOverview(this.IssueDescriptorEnum[issues[i]])); + } + } } - else { - return null; - } - return (
  • {zerologonOverview}
  • ) + return overviews; + } + + getIssueOverview(issueDescriptor) { + return issueDescriptor[this.issueContentTypes.OVERVIEW](); } generateReportRecommendationsSection() { @@ -558,578 +594,17 @@ class ReportPageComponent extends AuthComponent { ); } - generateInfoBadges(data_array) { - return data_array.map(badge_data => {badge_data}); - } - - generateCrossSegmentIssue(crossSegmentIssue) { - let crossSegmentIssueOverview = 'Communication possible from ' - + `${crossSegmentIssue['source_subnet']} to ${crossSegmentIssue['target_subnet']}`; - - return ( -
  • - {crossSegmentIssueOverview} - -
      - {crossSegmentIssue['issues'].map( - issue => this.generateCrossSegmentIssueListItem(issue) - )} -
    -
    -
  • - ); - } - - generateCrossSegmentIssueListItem(issue) { - if (issue['is_self']) { - return this.generateCrossSegmentSingleHostMessage(issue); - } - - return this.generateCrossSegmentMultiHostMessage(issue); - } - - generateCrossSegmentSingleHostMessage(issue) { - return ( -
  • - {`Machine ${issue['hostname']} has both ips: ${issue['source']} and ${issue['target']}`} -
  • - ); - } - - generateCrossSegmentMultiHostMessage(issue) { - return ( -
  • - IP {issue['source']} ({issue['hostname']}) was able to communicate with - IP {issue['target']} using: - -
  • - ); - } - - generateCrossSegmentServiceListItems(issue) { - let service_list_items = []; - - for (const [service, info] of Object.entries(issue['services'])) { - service_list_items.push( -
  • - {service} ({info['display_name']}) -
  • - ); - } - - return service_list_items; - } - - generateShellshockPathListBadges(paths) { - return paths.map(path => {path}); - } - - generateSmbPasswordIssue(issue) { - return ( - <> - Change {issue.username}'s password to a complex one-use password - that is not shared with other computers on the network. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SMB attack. -
    - The Monkey authenticated over the SMB protocol with user {issue.username} and its password. -
    - - ); - } - - generateSmbPthIssue(issue) { - return ( - <> - Change {issue.username}'s password to a complex one-use password - that is not shared with other computers on the network. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SMB attack. -
    - The Monkey used a pass-the-hash attack over SMB protocol with user {issue.username}. -
    - - ); - } - - generateWmiPasswordIssue(issue) { - return ( - <> - Change {issue.username}'s password to a complex one-use password - that is not shared with other computers on the network. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a WMI attack. -
    - The Monkey authenticated over the WMI protocol with user {issue.username} and its password. -
    - - ); - } - - generateWmiPthIssue(issue) { - return ( - <> - Change {issue.username}'s password to a complex one-use password - that is not shared with other computers on the network. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a WMI attack. -
    - The Monkey used a pass-the-hash attack over WMI protocol with user {issue.username}. -
    - - ); - } - - generateSshIssue(issue) { - return ( - <> - Change {issue.username}'s password to a complex one-use password - that is not shared with other computers on the network. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SSH attack. -
    - The Monkey authenticated over the SSH protocol with user {issue.username} and its password. -
    - - ); - } - - generateSshKeysIssue(issue) { - return ( - <> - Protect {issue.ssh_key} private key with a pass phrase. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SSH attack. -
    - The Monkey authenticated over the SSH protocol with private key {issue.ssh_key}. -
    - - ); - } - - - generateSambaCryIssue(issue) { - return ( - <> - Change {issue.username}'s password to a complex one-use password - that is not shared with other computers on the network. -
    - Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SambaCry attack. -
    - The Monkey authenticated over the SMB protocol with user {issue.username} and its password, and used the SambaCry - vulnerability. -
    - - ); - } - - generateVsftpdBackdoorIssue(issue) { - return ( - <> - Update your VSFTPD server to the latest version vsftpd-3.0.3. - - The machine {issue.machine} ({issue.ip_address}) has a backdoor running at - port 6200. -
    - The attack was made possible because the VSFTPD server was not patched against CVE-2011-2523. -

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

    - The Monkey executed commands by first logging in with ":)" in the username and then sending commands to the - backdoor - at port 6200. -

    Read more about the security issue and remediation here. -
    - - ); - } - - generateElasticIssue(issue) { - return ( - <> - Update your Elastic Search server to version 1.4.3 and up. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to an Elastic Groovy attack. -
    - The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427. -
    - - ); - } - - generateShellshockIssue(issue) { - return ( - <> - Update your Bash to a ShellShock-patched version. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a ShellShock attack. -
    - The attack was made possible because the HTTP server running on TCP port {issue.port} was vulnerable to a shell injection attack on the - paths: {this.generateShellshockPathListBadges(issue.paths)}. -
    - - ); - } - - generateAzureIssue(issue) { - return ( - <> - Delete VM Access plugin configuration files. - - Credentials could be stolen from {issue.machine} for the following users {issue.users}. Read more about the security issue and remediation here. - - - ); - } - - generateConfickerIssue(issue) { - return ( - <> - Install the latest Windows updates or upgrade to a newer operating system. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a Conficker attack. -
    - The attack was made possible because the target machine used an outdated and unpatched operating system - vulnerable to Conficker. -
    - - ); - } - - generateIslandCrossSegmentIssue(issue) { - return ( - <> - Segment your network and make sure there is no communication between machines from different segments. - - The network can probably be segmented. A monkey instance on {issue.machine} in the - networks {this.generateInfoBadges(issue.networks)} - could directly access the Monkey Island server in the - networks {this.generateInfoBadges(issue.server_networks)}. - - - ); - } - - generateSharedCredsDomainIssue(issue) { - return ( - <> - Some domain users are sharing passwords, this should be fixed by changing passwords. - - These users are sharing access password: - {this.generateInfoBadges(issue.shared_with)}. - - - ); - } - - generateSharedCredsIssue(issue) { - return ( - <> - Some users are sharing passwords, this should be fixed by changing passwords. - - These users are sharing access password: - {this.generateInfoBadges(issue.shared_with)}. - - - ); - } - - generateSharedLocalAdminsIssue(issue) { - return ( - <> - Make sure the right administrator accounts are managing the right machines, and that there isn’t an - unintentional local - admin sharing. - - Here is a list of machines which the account {issue.username} is defined as an administrator: - {this.generateInfoBadges(issue.shared_machines)} - - - ); - } - - generateStrongUsersOnCritIssue(issue) { - return ( - <> - This critical machine is open to attacks via strong users with access to it. - - 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)}. - - - ); - } - - generateTunnelIssue(issue) { - return ( - <> - Use micro-segmentation policies to disable communication other than the required. - - Machines are not locked down at port level. Network tunnel was set up from {issue.machine} to {issue.dest}. - - - ); - } - - generateStruts2Issue(issue) { - return ( - <> - Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions. - - Struts2 server at {issue.machine} ({issue.ip_address}) is vulnerable to remote code execution attack. -
    - 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 here. -
    - - ); - } - - generateDrupalIssue(issue) { - return ( - <> - Upgrade Drupal server to versions 8.5.11, 8.6.10, or later. - - Drupal server at {issue.machine} ({issue.ip_address}) is vulnerable to remote command execution attack. -
    - 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 - here. -
    - - ); - } - - generateWebLogicIssue(issue) { - return ( - <> - Update Oracle WebLogic server to the latest supported version. - - Oracle WebLogic server at {issue.machine} ({issue.ip_address}) is vulnerable to one of remote code execution attacks. -
    - The attack was made possible due to one of the following vulnerabilities: - CVE-2017-10271 or - CVE-2019-2725 -
    - - ); - } - - generateHadoopIssue(issue) { - return ( - <> - Run Hadoop in secure mode ( - add Kerberos authentication). - - The Hadoop server at {issue.machine} ({issue.ip_address}) is vulnerable to remote code execution attack. -
    - The attack was made possible due to default Hadoop/Yarn configuration being insecure. -
    - - ); - } - - generateMSSQLIssue(issue) { - return ( - <> - Disable the xp_cmdshell option. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a MSSQL exploit attack. -
    - 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 - . -
    - - ); - } - - generateZerologonIssue(issue) { - return ( - <> - Install Windows security updates. - - The machine {issue.machine} ({issue.ip_address}) is vulnerable to a Zerologon exploit. -
    - The attack was possible because the latest security updates from Microsoft - have not been applied to this machine. For more information about this - vulnerability, read - . - {!issue.password_restored && -
    -
    - - The domain controller's password was changed during the exploit and could not be restored successfully. - Instructions on how to manually reset the domain controller's password can be found - . - -
    } -
    - - ); - } - generateIssue = (issue) => { - let issueData; - switch (issue.type) { - case 'vsftp': - issueData = this.generateVsftpdBackdoorIssue(issue); - break; - case 'smb_password': - issueData = this.generateSmbPasswordIssue(issue); - break; - case 'smb_pth': - issueData = this.generateSmbPthIssue(issue); - break; - case 'wmi_password': - issueData = this.generateWmiPasswordIssue(issue); - break; - case 'wmi_pth': - issueData = this.generateWmiPthIssue(issue); - break; - case 'ssh': - issueData = this.generateSshIssue(issue); - break; - case 'ssh_key': - issueData = this.generateSshKeysIssue(issue); - break; - case 'sambacry': - issueData = this.generateSambaCryIssue(issue); - break; - case 'elastic': - issueData = this.generateElasticIssue(issue); - break; - case 'shellshock': - issueData = this.generateShellshockIssue(issue); - break; - case 'conficker': - issueData = this.generateConfickerIssue(issue); - break; - case 'island_cross_segment': - issueData = this.generateIslandCrossSegmentIssue(issue); - break; - case 'shared_passwords': - issueData = this.generateSharedCredsIssue(issue); - break; - case 'shared_passwords_domain': - issueData = this.generateSharedCredsDomainIssue(issue); - break; - case 'shared_admins_domain': - issueData = this.generateSharedLocalAdminsIssue(issue); - break; - case 'strong_users_on_crit': - issueData = this.generateStrongUsersOnCritIssue(issue); - break; - case 'tunnel': - issueData = this.generateTunnelIssue(issue); - break; - case 'azure_password': - issueData = this.generateAzureIssue(issue); - break; - case 'struts2': - issueData = this.generateStruts2Issue(issue); - break; - case 'weblogic': - issueData = this.generateWebLogicIssue(issue); - break; - case 'hadoop': - issueData = this.generateHadoopIssue(issue); - break; - case 'mssql': - issueData = this.generateMSSQLIssue(issue); - break; - case 'drupal': - issueData = this.generateDrupalIssue(issue); - break; - case 'zerologon': - issueData = this.generateZerologonIssue(issue); - break; + let issueDescriptor = this.IssueDescriptorEnum[issue.type]; + + let reportFnc = (issue) => {}; + if (issue.hasOwnProperty('credential_type')) { + reportFnc = issueDescriptor[this.issueContentTypes.REPORT][issue.credential_type]; + } else { + reportFnc = issueDescriptor[this.issueContentTypes.REPORT]; } - return
  • {issueData}
  • ; + let reportContents = reportFnc(issue); + return
  • {reportContents}
  • ; }; generateIssues = (issues) => { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/IssueDescriptor.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/IssueDescriptor.js new file mode 100644 index 000000000..330bf3828 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/IssueDescriptor.js @@ -0,0 +1,7 @@ +class IssueDescriptor { + constructor(name, overviewComponent, reportComponent) { + this.name = name; + this.overviewComponent = overviewComponent; + this.reportComponent = reportComponent; + } +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/AzurePasswordIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/AzurePasswordIssue.js new file mode 100644 index 000000000..78afa599b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/AzurePasswordIssue.js @@ -0,0 +1,23 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function azurePasswordIssueOverview() { + return (
  • Azure machines expose plaintext passwords. (More info)
  • ) +} + +export function azurePasswordIssueReport(issue) { + return ( + <> + Delete VM Access plugin configuration files. + + Credentials could be stolen from {issue.machine} for the following users {issue.users}. Read more about the security issue and remediation here. + + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/CrossSegmentIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/CrossSegmentIssue.js new file mode 100644 index 000000000..6c1ece1ea --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/CrossSegmentIssue.js @@ -0,0 +1,84 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; +import {generateInfoBadges} from './utils'; + +export function crossSegmentIssueOverview() { + return (
  • Weak segmentation - Machines from + different segments are able to communicate.
  • ) +} + +export function crossSegmentIssueReport(crossSegmentIssue) { + let crossSegmentIssueOverview = 'Communication possible from ' + + `${crossSegmentIssue['source_subnet']} to ${crossSegmentIssue['target_subnet']}`; + + return ( +
  • + {crossSegmentIssueOverview} + +
      + {crossSegmentIssue['issues'].map( + issue => getCrossSegmentIssueListItem(issue) + )} +
    +
    +
  • + ); + } + +export function getCrossSegmentIssueListItem(issue) { + if (issue['is_self']) { + return getCrossSegmentSingleHostMessage(issue); + } + + return getCrossSegmentMultiHostMessage(issue); + } + +export function getCrossSegmentSingleHostMessage(issue) { + return ( +
  • + {`Machine ${issue['hostname']} has both ips: ${issue['source']} and ${issue['target']}`} +
  • + ); + } + +export function getCrossSegmentMultiHostMessage(issue) { + return ( +
  • + IP {issue['source']} ({issue['hostname']}) was able to communicate with + IP {issue['target']} using: + +
  • + ); + } + +export function getCrossSegmentServiceListItems(issue) { + let service_list_items = []; + + for (const [service, info] of Object.entries(issue['services'])) { + service_list_items.push( +
  • + {service} ({info['display_name']}) +
  • + ); + } + + return service_list_items; + } + +export function islandCrossSegmentIssueReport(issue) { + return ( + <> + Segment your network and make sure there is no communication between machines from different segments. + + The network can probably be segmented. A monkey instance on {issue.machine} in the + networks {generateInfoBadges(issue.networks)} + could directly access the Monkey Island server in the + networks {generateInfoBadges(issue.server_networks)}. + + + ); + } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/DrupalIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/DrupalIssue.js new file mode 100644 index 000000000..d5cc068bb --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/DrupalIssue.js @@ -0,0 +1,24 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function drupalIssueOverview() { + return (
  • Drupal server/s are vulnerable to CVE-2019-6340.
  • ) +} + +export function drupalIssueReport(issue) { + return ( + <> + Upgrade Drupal server to versions 8.5.11, 8.6.10, or later. + + Drupal server at {issue.machine} ({issue.ip_address}) is vulnerable to remote command execution attack. +
    + 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 + here. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ElasticIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ElasticIssue.js new file mode 100644 index 000000000..4d389bf2b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ElasticIssue.js @@ -0,0 +1,23 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function elasticIssueOverview() { + return (
  • Elasticsearch servers are vulnerable to CVE-2015-1427. +
  • ) +} + +export function elasticIssueReport(issue) { + return ( + <> + Update your Elastic Search server to version 1.4.3 and up. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to an Elastic Groovy attack. +
    + The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/HadoopIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/HadoopIssue.js new file mode 100644 index 000000000..ff126ef8a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/HadoopIssue.js @@ -0,0 +1,23 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function hadoopIssueOverview() { + return (
  • Hadoop/Yarn servers are vulnerable to remote code execution.
  • ) +} + +export function hadoopIssueReport(issue) { + return ( + <> + Run Hadoop in secure mode ( + add Kerberos authentication). + + The Hadoop server at {issue.machine} ({issue.ip_address}) is vulnerable to remote code execution attack. +
    + The attack was made possible due to default Hadoop/Yarn configuration being insecure. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/MS08_067Issue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/MS08_067Issue.js new file mode 100644 index 000000000..2a831a093 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/MS08_067Issue.js @@ -0,0 +1,24 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function ms08_067IssueOverview() { + return (
  • Machines are vulnerable to ‘Conficker’ (MS08-067).
  • ) +} + +export function ms08_067IssueReport(issue) { + return ( + <> + Install the latest Windows updates or upgrade to a newer operating system. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a Conficker attack. +
    + The attack was made possible because the target machine used an outdated and unpatched operating system + vulnerable to Conficker. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/MssqlIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/MssqlIssue.js new file mode 100644 index 000000000..e8e1bb162 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/MssqlIssue.js @@ -0,0 +1,24 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function mssqlIssueOverview() { + return (
  • MS-SQL servers are vulnerable to remote code execution via xp_cmdshell command.
  • ) +} + +export function mssqlIssueReport(issue) { + return ( + <> + Disable the xp_cmdshell option. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a MSSQL exploit attack. +
    + 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 + Microsoft's documentation. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PthCriticalServiceIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PthCriticalServiceIssue.js new file mode 100644 index 000000000..73589715b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/PthCriticalServiceIssue.js @@ -0,0 +1,6 @@ +import React from 'react'; + +export function pthCriticalServiceIssueOverview() { + return (
  • Mimikatz found login credentials of a user who has admin access to a server defined as + critical.
  • ) +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SambacryIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SambacryIssue.js new file mode 100644 index 000000000..05bcb6850 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SambacryIssue.js @@ -0,0 +1,28 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function sambacryIssueOverview() { + return (
  • Samba servers are vulnerable to ‘SambaCry’ (CVE-2017-7494).
  • ) +} + +export function sambacryIssueReport(issue) { + return ( + <> + Change {issue.username}'s password to a complex one-use password + that is not shared with other computers on the network. +
    + Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SambaCry attack. +
    + The Monkey authenticated over the SMB protocol with user {issue.username} and its password, and used the SambaCry + vulnerability. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SharedPasswordsIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SharedPasswordsIssue.js new file mode 100644 index 000000000..2a09dbb83 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SharedPasswordsIssue.js @@ -0,0 +1,51 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; +import {generateInfoBadges} from './utils'; + +export function sharedPasswordsIssueOverview() { + return (
  • Multiple users have the same password
  • ) +} + +export function sharedAdminsDomainIssueOverview() { + return (
  • Shared local administrator account - Different machines have the same account as a local + administrator.
  • ) +} + +export function sharedCredsDomainIssueReport(issue) { + return ( + <> + Some domain users are sharing passwords, this should be fixed by changing passwords. + + These users are sharing access password: + {generateInfoBadges(issue.shared_with)}. + + + ); + } + +export function sharedCredsIssueReport(issue) { + return ( + <> + Some users are sharing passwords, this should be fixed by changing passwords. + + These users are sharing access password: + {generateInfoBadges(issue.shared_with)}. + + + ); + } + +export function sharedLocalAdminsIssueReport(issue) { + return ( + <> + Make sure the right administrator accounts are managing the right machines, and that there isn’t an + unintentional local + admin sharing. + + Here is a list of machines which the account {issue.username} is defined as an administrator: + {generateInfoBadges(issue.shared_machines)} + + + ); + } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ShellShockIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ShellShockIssue.js new file mode 100644 index 000000000..b2496fb21 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ShellShockIssue.js @@ -0,0 +1,30 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function shellShockIssueOverview() { + return (
  • Machines are vulnerable to ‘Shellshock’ (CVE-2014-6271). +
  • ) +} + + +function getShellshockPathListBadges(paths) { + return paths.map(path => {path}); +} + +export function shellShockIssueReport(issue) { + return ( + <> + Update your Bash to a ShellShock-patched version. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a ShellShock attack. +
    + The attack was made possible because the HTTP server running on TCP port {issue.port} was vulnerable to a shell injection attack on the + paths: {getShellshockPathListBadges(issue.paths)}. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SmbIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SmbIssue.js new file mode 100644 index 000000000..66e2117ff --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SmbIssue.js @@ -0,0 +1,36 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function smbPasswordReport(issue) { + return ( + <> + Change {issue.username}'s password to a complex one-use password + that is not shared with other computers on the network. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SMB attack. +
    + The Monkey authenticated over the SMB protocol with user {issue.username} and its password. +
    + + ); +} + +export function smbPthReport(issue) { + return ( + <> + Change {issue.username}'s password to a complex one-use password + that is not shared with other computers on the network. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SMB attack. +
    + The Monkey used a pass-the-hash attack over SMB protocol with user {issue.username}. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SshIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SshIssue.js new file mode 100644 index 000000000..cb74018d8 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/SshIssue.js @@ -0,0 +1,39 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function sshIssueOverview() { + return (
  • Stolen SSH keys are used to exploit other machines.
  • ) +} + +export function shhIssueReport(issue) { + return ( + <> + Change {issue.username}'s password to a complex one-use password + that is not shared with other computers on the network. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SSH attack. +
    + The Monkey authenticated over the SSH protocol with user {issue.username} and its password. +
    + + ); +} + +export function sshKeysReport(issue) { + return ( + <> + Protect {issue.ssh_key} private key with a pass phrase. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a SSH attack. +
    + The Monkey authenticated over the SSH protocol with private key {issue.ssh_key}. +
    + + ); + } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/StolenCredsIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/StolenCredsIssue.js new file mode 100644 index 000000000..a0b0c037b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/StolenCredsIssue.js @@ -0,0 +1,5 @@ +import React from 'react'; + +export function stolenCredsIssueOverview() { + return (
  • Stolen credentials are used to exploit other machines.
  • ) +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/StrongUsersOnCritIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/StrongUsersOnCritIssue.js new file mode 100644 index 000000000..328207710 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/StrongUsersOnCritIssue.js @@ -0,0 +1,16 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function strongUsersOnCritIssueReport(issue) { + return ( + <> + This critical machine is open to attacks via strong users with access to it. + + 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)}. + + + ); + } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/Struts2Issue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/Struts2Issue.js new file mode 100644 index 000000000..ca4c2b2b9 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/Struts2Issue.js @@ -0,0 +1,26 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function struts2IssueOverview() { + return (
  • Struts2 servers are vulnerable to remote code execution. ( + CVE-2017-5638)
  • ) +} + +export function struts2IssueReport(issue) { + return ( + <> + Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions. + + Struts2 server at {issue.machine} ({issue.ip_address}) is vulnerable to remote code execution attack. +
    + 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 here. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/TunnelIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/TunnelIssue.js new file mode 100644 index 000000000..c4d52751a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/TunnelIssue.js @@ -0,0 +1,19 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function tunnelIssueOverview(){ + return (
  • Weak segmentation - Machines were able to communicate over unused ports.
  • ) +} + +export function tunnelIssueReport(issue) { + return ( + <> + Use micro-segmentation policies to disable communication other than the required. + + Machines are not locked down at port level. Network tunnel was set up from {issue.machine} to {issue.dest}. + + + ); + } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/VsftpdIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/VsftpdIssue.js new file mode 100644 index 000000000..e5419a9c2 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/VsftpdIssue.js @@ -0,0 +1,36 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function vsftpdIssueOverview() { + return (
  • VSFTPD is vulnerable to CVE-2011-2523. +
  • ) +} + +export function vsftpdIssueReport(issue) { + return ( + <> + Update your VSFTPD server to the latest version vsftpd-3.0.3. + + The machine {issue.machine} ({issue.ip_address}) has a backdoor running at + port 6200. +
    + The attack was made possible because the VSFTPD server was not patched against CVE-2011-2523. +

    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. +

    + The Monkey executed commands by first logging in with ":)" in the username and then sending commands to the + backdoor + at port 6200. +

    Read more about the security issue and remediation here. +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WeakPasswordIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WeakPasswordIssue.js new file mode 100644 index 000000000..ee3c6c04f --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WeakPasswordIssue.js @@ -0,0 +1,6 @@ +import React from 'react'; + +export function weakPasswordIssueOverview() { + return (
  • Machines are accessible using passwords supplied by the user during the Monkey’s + configuration.
  • ) +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WebLogicIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WebLogicIssue.js new file mode 100644 index 000000000..e7678c448 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WebLogicIssue.js @@ -0,0 +1,23 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function webLogicIssueOverview() { + return (
  • Oracle WebLogic servers are susceptible to a remote code execution vulnerability.
  • ) +} + +export function webLogicIssueReport(issue) { + return ( + <> + Update Oracle WebLogic server to the latest supported version. + + Oracle WebLogic server at {issue.machine} ({issue.ip_address}) is vulnerable to one of remote code execution attacks. +
    + The attack was made possible due to one of the following vulnerabilities: + CVE-2017-10271 or + CVE-2019-2725 +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WmiIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WmiIssue.js new file mode 100644 index 000000000..cce631274 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WmiIssue.js @@ -0,0 +1,36 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; + +export function wmiPasswordIssueReport(issue) { + return ( + <> + Change {issue.username}'s password to a complex one-use password + that is not shared with other computers on the network. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a WMI attack. +
    + The Monkey authenticated over the WMI protocol with user {issue.username} and its password. +
    + + ); + } + +export function wmiPthIssueReport(issue) { + return ( + <> + Change {issue.username}'s password to a complex one-use password + that is not shared with other computers on the network. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a WMI attack. +
    + The Monkey used a pass-the-hash attack over WMI protocol with user {issue.username}. +
    + + ); + } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ZerologonIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ZerologonIssue.js new file mode 100644 index 000000000..771aecf6c --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/ZerologonIssue.js @@ -0,0 +1,64 @@ +import React from 'react'; +import CollapsibleWellComponent from '../CollapsibleWell'; +import WarningIcon from '../../../ui-components/WarningIcon'; +import {Button} from 'react-bootstrap'; + +export function zerologonIssueOverview() { + return ( +
  • + Some Windows domain controllers are vulnerable to 'Zerologon' ( + ). +
  • + ) +} + +export function zerologonOverviewWithFailedPassResetWarning() { + let overview = [zerologonIssueOverview()]; + overview.push( +
  • + + + Automatic password restoration on a domain controller failed! + + +
  • + ) + return overview; +} + +export function zerologonIssueReport(issue) { + return ( + <> + Install Windows security updates. + + The machine {issue.machine} ({issue.ip_address}) is vulnerable to a Zerologon exploit. +
    + The attack was possible because the latest security updates from Microsoft + have not been applied to this machine. For more information about this + vulnerability, read + Microsoft's documentation. + {!issue.password_restored ? +
    +
    + + The domain controller's password was changed during the exploit and could not be restored successfully. + Instructions on how to manually reset the domain controller's password can be found here. + +
    : null} +
    + + ); +} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/utils.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/utils.js new file mode 100644 index 000000000..6bc891201 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/utils.js @@ -0,0 +1,6 @@ +import React from 'react'; + +export function generateInfoBadges(data_array) { + return data_array.map(badge_data => {badge_data}); + }