diff --git a/monkey/common/cloud/environment_names.py b/monkey/common/cloud/environment_names.py index f9e881a5a..1745eed62 100644 --- a/monkey/common/cloud/environment_names.py +++ b/monkey/common/cloud/environment_names.py @@ -1,3 +1,7 @@ +UNKNOWN = "Unknown" ON_PREMISE = "On Premise" AZURE = "Azure" AWS = "AWS" +GCP = "GCP" + +ALL_ENV_NAMES = [UNKNOWN, ON_PREMISE, AZURE, AWS, GCP] diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 2d2a93939..e76ed8101 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -125,6 +125,7 @@ class Configuration(object): finger_classes = [] exploiter_classes = [] + system_info_collectors_classes = ["EnvironmentCollector"] # how many victims to look for in a single scan iteration victims_max_find = 100 diff --git a/monkey/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py index 7d4395af7..ccec45cde 100644 --- a/monkey/infection_monkey/system_info/__init__.py +++ b/monkey/infection_monkey/system_info/__init__.py @@ -9,6 +9,7 @@ from infection_monkey.network.info import get_host_subnets from infection_monkey.system_info.aws_collector import AwsCollector from infection_monkey.system_info.azure_cred_collector import AzureCollector from infection_monkey.system_info.netstat_collector import NetstatCollector +from system_info.system_info_collectors_handler import SystemInfoCollectorsHandler LOG = logging.getLogger(__name__) @@ -61,12 +62,16 @@ class InfoCollector(object): self.info = {} def get_info(self): + # Collect all hardcoded self.get_hostname() self.get_process_list() self.get_network_info() self.get_azure_info() self.get_aws_info() + # Collect all plugins + SystemInfoCollectorsHandler().execute_all_configured() + def get_hostname(self): """ Adds the fully qualified computer hostname to the system information. diff --git a/monkey/infection_monkey/system_info/collectors/__init__.py b/monkey/infection_monkey/system_info/collectors/__init__.py index e69de29bb..f5b7166e9 100644 --- a/monkey/infection_monkey/system_info/collectors/__init__.py +++ b/monkey/infection_monkey/system_info/collectors/__init__.py @@ -0,0 +1,3 @@ +""" +This package holds all the dynamic (plugin) collectors +""" diff --git a/monkey/infection_monkey/system_info/system_info_collector.py b/monkey/infection_monkey/system_info/system_info_collector.py index 863e0e0ab..3a977fcde 100644 --- a/monkey/infection_monkey/system_info/system_info_collector.py +++ b/monkey/infection_monkey/system_info/system_info_collector.py @@ -11,7 +11,7 @@ class SystemInfoCollector(Plugin): @staticmethod def should_run(class_name) -> bool: - return class_name in WormConfiguration.system_info_collectors + return class_name in WormConfiguration.system_info_collectors_classes @staticmethod def base_package_file(): diff --git a/monkey/infection_monkey/system_info/system_info_handler.py b/monkey/infection_monkey/system_info/system_info_collectors_handler.py similarity index 92% rename from monkey/infection_monkey/system_info/system_info_handler.py rename to monkey/infection_monkey/system_info/system_info_collectors_handler.py index dd085300f..1fdc74cfa 100644 --- a/monkey/infection_monkey/system_info/system_info_handler.py +++ b/monkey/infection_monkey/system_info/system_info_collectors_handler.py @@ -10,7 +10,7 @@ PATH_TO_COLLECTORS = "infection_monkey.system_info.collectors." # TODO Add new collectors to config and config schema -class SystemInfo(object): +class SystemInfoCollectorsHandler(object): def __init__(self): self.collectors_list = self.config_to_collectors_list() @@ -27,7 +27,8 @@ class SystemInfo(object): LOG.error("Collector {} failed. Error info: {}".format(collector.name, e)) LOG.info("All system info collectors executed. Total {} executed, out of which {} collected successfully.". format(len(self.collectors_list), successful_collections)) - # TODO Send SystemInfoTelem() + + SystemInfoTelem({"collectors": system_info_telemetry}).send() @staticmethod def config_to_collectors_list() -> Sequence[SystemInfoCollector]: diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index 07b5ba3fe..65dda0850 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -9,6 +9,7 @@ from monkey_island.cc.models.monkey_ttl import MonkeyTtl, create_monkey_ttl_docu from monkey_island.cc.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS from monkey_island.cc.models.command_control_channel import CommandControlChannel from monkey_island.cc.utils import local_ip_addresses +from common.cloud import environment_names MAX_MONKEYS_AMOUNT_TO_CACHE = 100 @@ -42,6 +43,9 @@ class Monkey(Document): ttl_ref = ReferenceField(MonkeyTtl) tunnel = ReferenceField("self") command_control_channel = EmbeddedDocumentField(CommandControlChannel) + + # Environment related fields + environment = StringField(default=environment_names.UNKNOWN, choices=environment_names.ALL_ENV_NAMES) aws_instance_id = StringField(required=False) # This field only exists when the monkey is running on an AWS # instance. See https://github.com/guardicore/monkey/issues/426. @@ -55,7 +59,8 @@ class Monkey(Document): raise MonkeyNotFoundError("info: {0} | id: {1}".format(ex, str(db_id))) @staticmethod - def get_single_monkey_by_guid(monkey_guid): + # See https://www.python.org/dev/peps/pep-0484/#forward-references + def get_single_monkey_by_guid(monkey_guid) -> 'Monkey': try: return Monkey.objects.get(guid=monkey_guid) except DoesNotExist as ex: diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py index 32ee13b12..5de57e26b 100644 --- a/monkey/monkey_island/cc/services/config_schema.py +++ b/monkey/monkey_island/cc/services/config_schema.py @@ -99,6 +99,20 @@ SCHEMA = { } ] }, + "system_info_collectors_classes": { + "title": "System Information Collectors", + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + "EnvironmentCollector" + ], + "title": "Which Environment this machine is on (on prem/cloud)", + "attack_techniques": [] + }, + ], + }, "post_breach_acts": { "title": "Post breach actions", "type": "string", @@ -433,6 +447,18 @@ SCHEMA = { "attack_techniques": ["T1003"], "description": "Determines whether to use Mimikatz" }, + "system_info_collectors_classes": { + "title": "System info collectors", + "type": "array", + "uniqueItems": True, + "items": { + "$ref": "#/definitions/system_info_collectors_classes" + }, + "default": [ + "EnvironmentCollector" + ], + "description": "Determines which system information collectors will collect information." + }, } }, "life_cycle": { 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 8df189655..04ab27d95 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -5,6 +5,7 @@ from monkey_island.cc.models import Monkey from monkey_island.cc.services import mimikatz_utils from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.config import ConfigService +from monkey_island.cc.services.telemetry.processing.system_info_collectors.environment import process_environment_telemetry from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence from monkey_island.cc.services.wmi_handler import WMIHandler from monkey_island.cc.encryptor import encryptor @@ -20,6 +21,7 @@ def process_system_info_telemetry(telemetry_json): process_aws_data, update_db_with_new_hostname, test_antivirus_existence, + process_environment_telemetry ] # Calling safe_process_telemetry so if one of the stages fail, we log and move on instead of failing the rest of diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/__init__.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py new file mode 100644 index 000000000..d66019b39 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py @@ -0,0 +1,14 @@ +import logging + +from monkey_island.cc.models.monkey import Monkey + +logger = logging.getLogger(__name__) + + +def process_environment_telemetry(telemetry_json): + if "EnvironmentCollector" in telemetry_json["data"]["collectors"]: + env = telemetry_json["data"]["collectors"]["EnvironmentCollector"]["environment"] + relevant_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) + relevant_monkey.environment = env + relevant_monkey.save() + logger.debug("Updated Monkey {} with env {}".format(str(relevant_monkey), env))