From 7f3ee6952758e4a95ea3c42a36f46e3a062b726e Mon Sep 17 00:00:00 2001 From: "maor.rayzin" Date: Mon, 31 Dec 2018 14:51:07 +0200 Subject: [PATCH] - Created the exporter_init file, in there the exporter manager singleton is created and populated with the relevant exporters (the aws exporter in this case) - changed the report file to use the new exporter manager singleton - changed the finding structure in the aws_exporter.py, divided it to creation functions and cleaned the code. --- monkey/monkey_island/cc/exporter_init.py | 17 + monkey/monkey_island/cc/main.py | 3 + .../cc/report_exporter_manager.py | 32 + .../cc/resources/aws_exporter.py | 613 ++++++------------ monkey/monkey_island/cc/services/report.py | 9 +- .../monkey_island/report_exporter_manager.py | 40 -- 6 files changed, 261 insertions(+), 453 deletions(-) create mode 100644 monkey/monkey_island/cc/exporter_init.py create mode 100644 monkey/monkey_island/cc/report_exporter_manager.py delete mode 100644 monkey/monkey_island/report_exporter_manager.py diff --git a/monkey/monkey_island/cc/exporter_init.py b/monkey/monkey_island/cc/exporter_init.py new file mode 100644 index 000000000..c2285772e --- /dev/null +++ b/monkey/monkey_island/cc/exporter_init.py @@ -0,0 +1,17 @@ +from cc.environment.environment import load_env_from_file, AWS +from cc.report_exporter_manager import ReportExporterManager +from cc.resources.aws_exporter import AWSExporter + + +def populate_exporter_list(): + + manager = ReportExporterManager() + if is_aws_exporter_required(): + manager.add_exporter_to_list(AWSExporter) + + +def is_aws_exporter_required(): + if str(load_env_from_file()) == AWS: + return True + else: + return False diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 86015b5d4..a6ded6628 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -34,6 +34,8 @@ def main(): logger.info('Waiting for MongoDB server') time.sleep(1) + + app = init_app(mongo_url) if env.is_debug(): app.run(host='0.0.0.0', debug=True, ssl_context=('monkey_island/cc/server.crt', 'monkey_island/cc/server.key')) @@ -44,6 +46,7 @@ def main(): http_server.listen(env.get_island_port()) logger.info( 'Monkey Island Server is running on https://{}:{}'.format(local_ip_addresses()[0], env.get_island_port())) + IOLoop.instance().start() diff --git a/monkey/monkey_island/cc/report_exporter_manager.py b/monkey/monkey_island/cc/report_exporter_manager.py new file mode 100644 index 000000000..210f28966 --- /dev/null +++ b/monkey/monkey_island/cc/report_exporter_manager.py @@ -0,0 +1,32 @@ +import logging + +logger = logging.getLogger(__name__) + + +class Singleton(type): + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + +class ReportExporterManager(object): + __metaclass__ = Singleton + + def __init__(self): + self._exporters_set = set() + + def get_exporters_list(self): + return self._exporters_set + + def add_exporter_to_list(self, exporter): + self._exporters_set.add(exporter) + + def export(self, report): + try: + for exporter in self._exporters_set: + exporter().handle_report(report) + except Exception as e: + logger.exception('Failed to export report') diff --git a/monkey/monkey_island/cc/resources/aws_exporter.py b/monkey/monkey_island/cc/resources/aws_exporter.py index 735de6584..8890342dd 100644 --- a/monkey/monkey_island/cc/resources/aws_exporter.py +++ b/monkey/monkey_island/cc/resources/aws_exporter.py @@ -87,6 +87,7 @@ class AWSExporter(Exporter): "ProductArn": product_arn, "GeneratorId": issue['type'], "AwsAccountId": account_id, + "RecordState": "ACTIVE", "Types": [ "Software and Configuration Checks/Vulnerabilities/CVE" ], @@ -120,488 +121,288 @@ class AWSExporter(Exporter): return False @staticmethod - def _handle_tunnel_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 5, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Weak segmentation - Machines were able to communicate over unused ports.", - "Description": "Use micro-segmentation policies to disable communication other than the required.", - "Remediation": { - "Recommendation": { - "Text": "Machines are not locked down at port level. Network tunnel was set up from {0} to {1}" - .format(issue['machine'], issue['dest']) - } - }} - - if 'aws_instance_id' in issue: - finding["Resources"] = [{ + def _get_finding_resource(instance_id, instance_arn): + if instance_id: + return [{ "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) + "Id": instance_arn.format(instance_id=instance_id) }] else: - finding["Resources"] = [{'Type': 'Other'}] + return [{'Type': 'Other'}] + + @staticmethod + def _build_generic_finding(severity, title, description, recommendation, instance_arn, instance_id=None): + finding = { + "Severity": { + "Product": severity, + "Normalized": 100 + }, + 'Resource': AWSExporter._get_finding_resource(instance_id, instance_arn), + "Title": title, + "Description": description, + "Remediation": { + "Recommendation": { + "Text": recommendation + } + }} return finding + @staticmethod + def _handle_tunnel_issue(issue, instance_arn): + + return AWSExporter._build_generic_finding( + severity=5, + title="Weak segmentation - Machines were able to communicate over unused ports.", + description="Use micro-segmentation policies to disable communication other than the required.", + recommendation="Machines are not locked down at port level. Network tunnel was set up from {0} to {1}" + .format(issue['machine'], issue['dest']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) + @staticmethod def _handle_sambacry_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 10, - "Normalized": 100 - }, "RecordState": "ACTIVE", "Title": "Samba servers are vulnerable to 'SambaCry'", - "Description": "Change {0} 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." \ - .format(issue['username']), "Remediation": { - "Recommendation": { - "Text": "The machine {0} ({1}) is vulnerable to a SambaCry attack. The Monkey authenticated over the SMB protocol with user {2} and its password, and used the SambaCry vulnerability.".format( - issue['machine'], issue['ip_address'], issue['username']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=10, + title="Samba servers are vulnerable to 'SambaCry'", + description="Change {0} 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." \ + .format(issue['username']), + recommendation="The machine {0} ({1}) is vulnerable to a SambaCry attack. The Monkey authenticated over the SMB protocol with user {2} and its password, and used the SambaCry vulnerability.".format( + issue['machine'], issue['ip_address'], issue['username']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_smb_pth_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 5, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - "Description": "Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), "Remediation": { - "Recommendation": { - "Text": "The machine {0}({1}) is vulnerable to a SMB attack. The Monkey used a pass-the-hash attack over SMB protocol with user {2}.".format( - issue['machine'], issue['ip_address'], issue['username']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=5, + title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( + issue['username']), + recommendation="The machine {0}({1}) is vulnerable to a SMB attack. The Monkey used a pass-the-hash attack over SMB protocol with user {2}.".format( + issue['machine'], issue['ip_address'], issue['username']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_ssh_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.", - "Description": "Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), "Remediation": { - "Recommendation": { - "Text": "The machine {0} ({1}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH protocol with user {2} and its password.".format( - issue['machine'], issue['ip_address'], issue['username']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.", + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( + issue['username']), + recommendation="The machine {0} ({1}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH protocol with user {2} and its password.".format( + issue['machine'], issue['ip_address'], issue['username']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_ssh_key_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.", - "Description": "Protect {ssh_key} private key with a pass phrase.".format(ssh_key=issue['ssh_key']), - "Remediation": { - "Recommendation": { - "Text": "The machine {machine} ({ip_address}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH protocol with private key {ssh_key}.".format( - machine=issue['machine'], ip_address=issue['ip_address'], ssh_key=issue['ssh_key']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.", + description="Protect {ssh_key} private key with a pass phrase.".format(ssh_key=issue['ssh_key']), + recommendation="The machine {machine} ({ip_address}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH protocol with private key {ssh_key}.".format( + machine=issue['machine'], ip_address=issue['ip_address'], ssh_key=issue['ssh_key']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_elastic_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 10, - "Normalized": 100 - }, "RecordState": "ACTIVE", "Title": "Elasticsearch servers are vulnerable to CVE-2015-1427", - "Description": "Update your Elastic Search server to version 1.4.3 and up.", "Remediation": { - "Recommendation": { - "Text": "The machine {0}({1}) is vulnerable to an Elastic Groovy attack. The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427.".format( - issue['machine'], issue['ip_address']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=10, + title="Elastic Search servers are vulnerable to CVE-2015-1427", + description="Update your Elastic Search server to version 1.4.3 and up.", + recommendation="The machine {0}({1}) is vulnerable to an Elastic Groovy attack. The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427.".format( + issue['machine'], issue['ip_address']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_island_cross_segment_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Weak segmentation - Machines from different segments are able to communicate.", - "Description": "Segment your network and make sure there is no communication between machines from different segments.", - "Remediation": { - "Recommendation": { - "Text": "The network can probably be segmented. A monkey instance on \ + + return AWSExporter._build_generic_finding( + severity=1, + title="Weak segmentation - Machines from different segments are able to communicate.", + description="Segment your network and make sure there is no communication between machines from different segments.", + recommendation="The network can probably be segmented. A monkey instance on \ {0} in the networks {1} \ could directly access the Monkey Island server in the networks {2}.".format(issue['machine'], issue['networks'], - issue['server_networks']) - } - }} - - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + issue['server_networks']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_shared_passwords_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", "Title": "Multiple users have the same password", - "Description": "Some users are sharing passwords, this should be fixed by changing passwords.", - "Remediation": { - "Recommendation": { - "Text": "These users are sharing access password: {0}.".format(issue['shared_with']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Multiple users have the same password", + description="Some users are sharing passwords, this should be fixed by changing passwords.", + recommendation="These users are sharing access password: {0}.".format(issue['shared_with']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_shellshock_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 10, - "Normalized": 100 - }, "RecordState": "ACTIVE", "Title": "Machines are vulnerable to 'Shellshock'", - "Description": "Update your Bash to a ShellShock-patched version.", "Remediation": { - "Recommendation": { - "Text": "The machine {0} ({1}) is vulnerable to a ShellShock attack. " - "The attack was made possible because the HTTP server running on TCP port {2} was vulnerable to a shell injection attack on the paths: {3}.".format( - issue['machine'], issue['ip_address'], issue['port'], issue['paths']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=10, + title="Machines are vulnerable to 'Shellshock'", + description="Update your Bash to a ShellShock-patched version.", + recommendation="The machine {0} ({1}) is vulnerable to a ShellShock attack. " + "The attack was made possible because the HTTP server running on TCP port {2} was vulnerable to a shell injection attack on the paths: {3}.".format( + issue['machine'], issue['ip_address'], issue['port'], issue['paths']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_smb_password_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - "Description": "Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), "Remediation": { - "Recommendation": { - "Text": "The machine {0} ({1}) is vulnerable to a SMB attack. The Monkey authenticated over the SMB protocol with user {2} and its password.".format( - issue['machine'], issue['ip_address'], issue['username']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( + issue['username']), + recommendation="The machine {0} ({1}) is vulnerable to a SMB attack. The Monkey authenticated over the SMB protocol with user {2} and its password.".format( + issue['machine'], issue['ip_address'], issue['username']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_wmi_password_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - "Description": "Change {0}'s password to a complex one-use password that is not shared with other computers on the network.", - "Remediation": { - "Recommendation": { - "Text": "The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey authenticated over the WMI protocol with user {username} and its password.".format( - machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.", + recommendation="The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey authenticated over the WMI protocol with user {username} and its password.".format( + machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_wmi_pth_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - "Description": "Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), "Remediation": { - "Recommendation": { - "Text": "The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey used a pass-the-hash attack over WMI protocol with user {username}".format( - machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( + issue['username']), + recommendation="The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey used a pass-the-hash attack over WMI protocol with user {username}".format( + machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_rdp_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - "Description": "Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), "Remediation": { - "Recommendation": { - "Text": "The machine machine ({ip_address}) is vulnerable to a RDP attack. The Monkey authenticated over the RDP protocol with user {username} and its password.".format( - machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( + issue['username']), + recommendation="The machine machine ({ip_address}) is vulnerable to a RDP attack. The Monkey authenticated over the RDP protocol with user {username} and its password.".format( + machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_shared_passwords_domain_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", "Title": "Multiple users have the same password.", - "Description": "Some domain users are sharing passwords, this should be fixed by changing passwords.", - "Remediation": { - "Recommendation": { - "Text": "These users are sharing access password: {shared_with}.".format( - shared_with=issue['shared_with']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Multiple users have the same password.", + description="Some domain users are sharing passwords, this should be fixed by changing passwords.", + recommendation="These users are sharing access password: {shared_with}.".format( + shared_with=issue['shared_with']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_shared_admins_domain_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Shared local administrator account - Different machines have the same account as a local administrator.", - "Description": "Make sure the right administrator accounts are managing the right machines, and that there isn\'t an unintentional local admin sharing.", - "Remediation": { - "Recommendation": { - "Text": "Here is a list of machines which the account {username} is defined as an administrator: {shared_machines}".format( - username=issue['username'], shared_machines=issue['shared_machines']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Shared local administrator account - Different machines have the same account as a local administrator.", + description="Make sure the right administrator accounts are managing the right machines, and that there isn\'t an unintentional local admin sharing.", + recommendation="Here is a list of machines which the account {username} is defined as an administrator: {shared_machines}".format( + username=issue['username'], shared_machines=issue['shared_machines']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_strong_users_on_crit_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 1, - "Normalized": 100 - }, "RecordState": "ACTIVE", - "Title": "Mimikatz found login credentials of a user who has admin access to a server defined as critical.", - "Description": "This critical machine is open to attacks via strong users with access to it.", - "Remediation": { - "Recommendation": { - "Text": "The services: {services} have been found on the machine thus classifying it as a critical machine. These users has access to it:{threatening_users}.".format( - services=issue['services'], threatening_users=issue['threatening_users']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=1, + title="Mimikatz found login credentials of a user who has admin access to a server defined as critical.", + description="This critical machine is open to attacks via strong users with access to it.", + recommendation="The services: {services} have been found on the machine thus classifying it as a critical machine. These users has access to it:{threatening_users}.".format( + services=issue['services'], threatening_users=issue['threatening_users']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_struts2_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 10, - "Normalized": 100 - }, "RecordState": "ACTIVE", "Title": "Struts2 servers are vulnerable to remote code execution.", - "Description": "Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions.", "Remediation": { - "Recommendation": { - "Text": "Struts2 server at {machine} ({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.".format( - machine=issue['machine'], ip_address=issue['ip_address']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=10, + title="Struts2 servers are vulnerable to remote code execution.", + description="Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions.", + recommendation="Struts2 server at {machine} ({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.".format( + machine=issue['machine'], ip_address=issue['ip_address']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_weblogic_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 10, - "Normalized": 100 - }, "RecordState": "ACTIVE", "Title": "Oracle WebLogic servers are vulnerable to remote code execution.", - "Description": "Install Oracle critical patch updates. Or update to the latest version. " \ - "Vulnerable versions are 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0.", - "Remediation": { - "Recommendation": { - "Text": "Oracle WebLogic server at {machine} ({ip_address}) is vulnerable to remote code execution attack." - " The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware (subcomponent: WLS Security).".format( - machine=issue['machine'], ip_address=issue['ip_address']) - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=10, + title="Oracle WebLogic servers are vulnerable to remote code execution.", + description="Install Oracle critical patch updates. Or update to the latest version. " \ + "Vulnerable versions are 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0.", + recommendation="Oracle WebLogic server at {machine} ({ip_address}) is vulnerable to remote code execution attack." + " The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware (subcomponent: WLS Security).".format( + machine=issue['machine'], ip_address=issue['ip_address']), + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) @staticmethod def _handle_hadoop_issue(issue, instance_arn): - finding = \ - {"Severity": { - "Product": 10, - "Normalized": 100 - }, "RecordState": "ACTIVE", "Title": "Hadoop/Yarn servers are vulnerable to remote code execution.", - "Description": "Run Hadoop in secure mode, add Kerberos authentication.", "Remediation": { - "Recommendation": { - "Text": "The Hadoop server at {machine} ({ip_address}) is vulnerable to remote code execution attack." - " The attack was made possible due to default Hadoop/Yarn configuration being insecure." - } - }} - if 'aws_instance_id' in issue: - finding["Resources"] = [{ - "Type": "AwsEc2Instance", - "Id": instance_arn.format(instance_id=issue['aws_instance_id']) - }] - else: - finding["Resources"] = [{'Type': 'Other'}] - - return finding + return AWSExporter._build_generic_finding( + severity=10, + title="Hadoop/Yarn servers are vulnerable to remote code execution.", + description="Run Hadoop in secure mode, add Kerberos authentication.", + recommendation="The Hadoop server at {machine} ({ip_address}) is vulnerable to remote code execution attack." + "The attack was made possible due to default Hadoop/Yarn configuration being insecure.", + instance_arn=instance_arn, + instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None + ) diff --git a/monkey/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/report.py index 8f72e1b17..8861e8d85 100644 --- a/monkey/monkey_island/cc/services/report.py +++ b/monkey/monkey_island/cc/services/report.py @@ -8,7 +8,7 @@ from enum import Enum from six import text_type from cc.database import mongo -from cc.resources.aws_exporter import AWSExporter +from cc.report_exporter_manager import ReportExporterManager from cc.services.config import ConfigService from cc.services.edge import EdgeService from cc.services.node import NodeService @@ -723,7 +723,7 @@ class ReportService: 'latest_monkey_modifytime': monkey_latest_modify_time } } - ReportService.export_to_exporters(report) + ReportExporterManager().export(report) mongo.db.report.drop() mongo.db.report.insert_one(report) @@ -755,8 +755,3 @@ class ReportService: return mongo.db.edge.count( {'exploits': {'$elemMatch': {'exploiter': exploit_type, 'result': True}}}, limit=1) > 0 - - @staticmethod - def export_to_exporters(report): - for exporter in ReportService.get_active_exporters(): - exporter.handle_report(report) diff --git a/monkey/monkey_island/report_exporter_manager.py b/monkey/monkey_island/report_exporter_manager.py deleted file mode 100644 index 7e9afc8a9..000000000 --- a/monkey/monkey_island/report_exporter_manager.py +++ /dev/null @@ -1,40 +0,0 @@ -from cc.environment.environment import load_env_from_file, AWS -from cc.resources.aws_exporter import AWSExporter -import logging - -logger = logging.getLogger(__name__) - - -class Borg: - _shared_state = {} - - def __init__(self): - self.__dict__ = self._shared_state - - -class ReportExporterManager(Borg): - def __init__(self): - Borg.__init__(self) - self._exporters_list = [] - self._init_exporters() - - def get_exporters_list(self): - return self._exporters_list - - def _init_exporters(self): - self._init_aws_exporter() - - def _init_aws_exporter(self): - if str(load_env_from_file()) == AWS: - self._exporters_list.append(AWSExporter) - - def export(self): - try: - for exporter in self._exporters_list: - exporter().handle_report() - except Exception as e: - logger.exception('Failed to export report') - -if __name__ == '__main__': - print ReportExporterManager().get_exporters_list() - print ReportExporterManager().get_exporters_list()