diff --git a/.flake8 b/.flake8 index 4bf127114..1f81c9edc 100644 --- a/.flake8 +++ b/.flake8 @@ -1,7 +1,7 @@ [flake8] ## Warn about linter issues. -exclude = ../monkey/monkey_island/cc/ui +exclude = monkey/monkey_island/cc/ui show-source = True max-complexity = 10 max-line-length = 100 diff --git a/.travis.yml b/.travis.yml index 98b2d999e..d57069c05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,7 @@ install: script: # Check Python code ## Check syntax errors and fail the build if any are found. -- flake8 ./monkey +- flake8 . ## Check import order - python -m isort ./monkey --check-only diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bca2515f..d675e0461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,4 +17,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Monkey agents are stored in the configurable data_dir when monkey is "run from the island". #997 - Reformated all code using black. #1070 -- Sort all imports usind isort. #1081 +- Sorted all imports usind isort. #1081 +- Addressed all flake8 issues. #1071 diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py index 18390e67e..30e635652 100644 --- a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py @@ -33,7 +33,8 @@ class PerformanceAnalyzer(Analyzer): if self.performance_test_config.break_on_timeout and not performance_is_good_enough: LOGGER.warning( - "Calling breakpoint - pausing to enable investigation of island. Type 'c' to continue once you're done " + "Calling breakpoint - pausing to enable investigation of island. " + "Type 'c' to continue once you're done " "investigating. Type 'p timings' and 'p total_time' to see performance information." ) breakpoint() diff --git a/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py index 5c256beaf..5762a0315 100644 --- a/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/zerologon_analyzer.py @@ -3,12 +3,7 @@ from typing import List import dpath.util -from common.config_value_paths import ( - LM_HASH_LIST_PATH, - NTLM_HASH_LIST_PATH, - PASSWORD_LIST_PATH, - USER_LIST_PATH, -) +from common.config_value_paths import LM_HASH_LIST_PATH, NTLM_HASH_LIST_PATH, USER_LIST_PATH from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_monkey.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_monkey.py index 2a39e6353..37245cefc 100644 --- a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_monkey.py +++ b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/fake_monkey.py @@ -1,6 +1,6 @@ import random -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( +from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501 FakeIpGenerator, ) diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py index 7a1fb4032..221c3a87a 100644 --- a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py +++ b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py @@ -9,10 +9,10 @@ from tqdm import tqdm from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_file_parser import ( SampleFileParser, ) -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( +from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501 FakeIpGenerator, ) -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_monkey import ( +from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_monkey import ( # noqa: E501 FakeMonkey, ) diff --git a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/test_fake_ip_generator.py b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/test_fake_ip_generator.py index 7a4f30cff..55662b307 100644 --- a/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/test_fake_ip_generator.py +++ b/envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/test_fake_ip_generator.py @@ -1,6 +1,6 @@ from unittest import TestCase -from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( +from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501 FakeIpGenerator, ) diff --git a/monkey/common/cloud/aws/aws_instance.py b/monkey/common/cloud/aws/aws_instance.py index 236cde5e1..d34c6c77a 100644 --- a/monkey/common/cloud/aws/aws_instance.py +++ b/monkey/common/cloud/aws/aws_instance.py @@ -9,7 +9,6 @@ from common.cloud.instance import CloudInstance __author__ = "itay.mizeretz" - AWS_INSTANCE_METADATA_LOCAL_IP_ADDRESS = "169.254.169.254" AWS_LATEST_METADATA_URI_PREFIX = "http://{0}/latest/".format(AWS_INSTANCE_METADATA_LOCAL_IP_ADDRESS) ACCOUNT_ID_KEY = "accountId" @@ -60,7 +59,8 @@ class AwsInstance(CloudInstance): @staticmethod def _parse_region(region_url_response): # For a list of regions, see: - # https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html + # https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts + # .RegionsAndAvailabilityZones.html # This regex will find any AWS region format string in the response. re_phrase = r"((?:us|eu|ap|ca|cn|sa)-[a-z]*-[0-9])" finding = re.findall(re_phrase, region_url_response, re.IGNORECASE) @@ -79,9 +79,11 @@ class AwsInstance(CloudInstance): def _extract_account_id(instance_identity_document_response): """ Extracts the account id from the dynamic/instance-identity/document metadata path. - Based on https://forums.aws.amazon.com/message.jspa?messageID=409028 which has a few more solutions, + Based on https://forums.aws.amazon.com/message.jspa?messageID=409028 which has a few more + solutions, in case Amazon break this mechanism. - :param instance_identity_document_response: json returned via the web page ../dynamic/instance-identity/document + :param instance_identity_document_response: json returned via the web page + ../dynamic/instance-identity/document :return: The account id """ return json.loads(instance_identity_document_response)[ACCOUNT_ID_KEY] diff --git a/monkey/common/cloud/aws/aws_service.py b/monkey/common/cloud/aws/aws_service.py index 0825811a9..dd4b1cb24 100644 --- a/monkey/common/cloud/aws/aws_service.py +++ b/monkey/common/cloud/aws/aws_service.py @@ -31,12 +31,14 @@ def filter_instance_data_from_aws_response(response): class AwsService(object): """ - A wrapper class around the boto3 client and session modules, which supplies various AWS services. + A wrapper class around the boto3 client and session modules, which supplies various AWS + services. This class will assume: 1. That it's running on an EC2 instance 2. That the instance is associated with the correct IAM role. See - https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#iam-role for details. + https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#iam-role + for details. """ region = None @@ -73,7 +75,8 @@ class AwsService(object): Get the information for all instances with the relevant roles. This function will assume that it's running on an EC2 instance with the correct IAM role. - See https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#iam-role for details. + See https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#iam + -role for details. :raises: botocore.exceptions.ClientError if can't describe local instance information. :return: All visible instances from this instance diff --git a/monkey/common/cloud/aws/test_aws_instance.py b/monkey/common/cloud/aws/test_aws_instance.py index d3c89f067..74ef5dd15 100644 --- a/monkey/common/cloud/aws/test_aws_instance.py +++ b/monkey/common/cloud/aws/test_aws_instance.py @@ -30,7 +30,6 @@ INSTANCE_IDENTITY_DOCUMENT_RESPONSE = """ } """ - EXPECTED_INSTANCE_ID = "i-1234567890abcdef0" EXPECTED_REGION = "us-west-2" diff --git a/monkey/common/cloud/azure/azure_instance.py b/monkey/common/cloud/azure/azure_instance.py index 186ce3c9d..859ab279f 100644 --- a/monkey/common/cloud/azure/azure_instance.py +++ b/monkey/common/cloud/azure/azure_instance.py @@ -18,7 +18,8 @@ logger = logging.getLogger(__name__) class AzureInstance(CloudInstance): """ Access to useful information about the current machine if it's an Azure VM. - Based on Azure metadata service: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service + Based on Azure metadata service: + https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service """ def is_instance(self): @@ -44,7 +45,8 @@ class AzureInstance(CloudInstance): ) # If not on cloud, the metadata URL is non-routable and the connection will fail. - # If on AWS, should get 404 since the metadata service URL is different, so bool(response) will be false. + # If on AWS, should get 404 since the metadata service URL is different, + # so bool(response) will be false. if response: logger.debug("Trying to parse Azure metadata.") self.try_parse_response(response) @@ -52,7 +54,8 @@ class AzureInstance(CloudInstance): logger.warning(f"Metadata response not ok: {response.status_code}") except requests.RequestException: logger.debug( - "Failed to get response from Azure metadata service: This instance is not on Azure." + "Failed to get response from Azure metadata service: This instance is not on " + "Azure." ) def try_parse_response(self, response): diff --git a/monkey/common/cloud/azure/test_azure_instance.py b/monkey/common/cloud/azure/test_azure_instance.py index fb1e01abb..a7bed81dd 100644 --- a/monkey/common/cloud/azure/test_azure_instance.py +++ b/monkey/common/cloud/azure/test_azure_instance.py @@ -30,7 +30,8 @@ GOOD_DATA = { ], "publisher": "RDFE-Test-Microsoft-Windows-Server-Group", "resourceGroupName": "macikgo-test-may-23", - "resourceId": "/subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/macikgo-test-may-23/" + "resourceId": "/subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/macikgo-test" + "-may-23/" "providers/Microsoft.Compute/virtualMachines/examplevmname", "securityProfile": {"secureBootEnabled": "true", "virtualTpmEnabled": "false"}, "sku": "Windows-Server-2012-R2-Datacenter", @@ -101,12 +102,16 @@ GOOD_DATA = { }, } - -BAD_DATA_NOT_JSON = '\n\n\n\n\nWaiting...\n\n\n \n\n' - +BAD_DATA_NOT_JSON = ( + '\n\n\n\n\nWaiting...\n\n\n " + "\n\n" +) BAD_DATA_JSON = {"": ""} diff --git a/monkey/common/cloud/gcp/gcp_instance.py b/monkey/common/cloud/gcp/gcp_instance.py index 14e4e554a..1fc208165 100644 --- a/monkey/common/cloud/gcp/gcp_instance.py +++ b/monkey/common/cloud/gcp/gcp_instance.py @@ -8,13 +8,13 @@ from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT logger = logging.getLogger(__name__) - GCP_METADATA_SERVICE_URL = "http://metadata.google.internal/" class GcpInstance(CloudInstance): """ - Used to determine if on GCP. See https://cloud.google.com/compute/docs/storing-retrieving-metadata#runninggce + Used to determine if on GCP. See https://cloud.google.com/compute/docs/storing-retrieving + -metadata#runninggce """ def is_instance(self): diff --git a/monkey/common/cmd/aws/aws_cmd_result.py b/monkey/common/cmd/aws/aws_cmd_result.py index 1e89115ef..e68f5bf6d 100644 --- a/monkey/common/cmd/aws/aws_cmd_result.py +++ b/monkey/common/cmd/aws/aws_cmd_result.py @@ -20,7 +20,8 @@ class AwsCmdResult(CmdResult): @staticmethod def is_successful(command_info, is_timeout=False): """ - Determines whether the command was successful. If it timed out and was still in progress, we assume it worked. + Determines whether the command was successful. If it timed out and was still in progress, + we assume it worked. :param command_info: Command info struct (returned by ssm.get_command_invocation) :param is_timeout: Whether the given command timed out :return: True if successful, False otherwise. diff --git a/monkey/common/cmd/cmd_runner.py b/monkey/common/cmd/cmd_runner.py index 57966d0b5..efd9d7bf0 100644 --- a/monkey/common/cmd/cmd_runner.py +++ b/monkey/common/cmd/cmd_runner.py @@ -21,8 +21,10 @@ class CmdRunner(object): * command id - any unique identifier of a command which was already run * command result - represents the result of running a command. Always of type CmdResult * command status - represents the current status of a command. Always of type CmdStatus - * command info - Any consistent structure representing additional information of a command which was already run - * instance - a machine that commands will be run on. Can be any dictionary with 'instance_id' as a field + * command info - Any consistent structure representing additional information of a command + which was already run + * instance - a machine that commands will be run on. Can be any dictionary with 'instance_id' + as a field * instance_id - any unique identifier of an instance (machine). Can be of any format """ @@ -49,7 +51,8 @@ class CmdRunner(object): """ Run multiple commands on various instances :param instances: List of instances. - :param inst_to_cmd: Function which receives an instance, runs a command asynchronously and returns Cmd + :param inst_to_cmd: Function which receives an instance, runs a command asynchronously + and returns Cmd :param inst_n_cmd_res_to_res: Function which receives an instance and CmdResult and returns a parsed result (of any format) :return: Dictionary with 'instance_id' as key and parsed result as value diff --git a/monkey/common/common_consts/zero_trust_consts.py b/monkey/common/common_consts/zero_trust_consts.py index 539bb7265..6ff2ab20f 100644 --- a/monkey/common/common_consts/zero_trust_consts.py +++ b/monkey/common/common_consts/zero_trust_consts.py @@ -1,8 +1,10 @@ """ -This file contains all the static data relating to Zero Trust. It is mostly used in the zero trust report generation and +This file contains all the static data relating to Zero Trust. It is mostly used in the zero +trust report generation and in creating findings. -This file contains static mappings between zero trust components such as: pillars, principles, tests, statuses. +This file contains static mappings between zero trust components such as: pillars, principles, +tests, statuses. Some of the mappings are computed when this module is loaded. """ @@ -82,12 +84,16 @@ PRINCIPLES = { PRINCIPLE_SEGMENTATION: "Apply segmentation and micro-segmentation inside your network.", PRINCIPLE_ANALYZE_NETWORK_TRAFFIC: "Analyze network traffic for malicious activity.", PRINCIPLE_USER_BEHAVIOUR: "Adopt security user behavior analytics.", - PRINCIPLE_ENDPOINT_SECURITY: "Use anti-virus and other traditional endpoint security solutions.", + PRINCIPLE_ENDPOINT_SECURITY: "Use anti-virus and other traditional endpoint " + "security solutions.", PRINCIPLE_DATA_CONFIDENTIALITY: "Ensure data's confidentiality by encrypting it.", - PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES: "Configure network policies to be as restrictive as possible.", - PRINCIPLE_USERS_MAC_POLICIES: "Users' permissions to the network and to resources should be MAC (Mandatory " + PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES: "Configure network policies to be as restrictive as " + "possible.", + PRINCIPLE_USERS_MAC_POLICIES: "Users' permissions to the network and to resources " + "should be MAC (Mandatory " "Access Control) only.", - PRINCIPLE_DISASTER_RECOVERY: "Ensure data and infrastructure backups for disaster recovery scenarios.", + PRINCIPLE_DISASTER_RECOVERY: "Ensure data and infrastructure backups for disaster " + "recovery scenarios.", PRINCIPLE_SECURE_AUTHENTICATION: "Ensure secure authentication process's.", PRINCIPLE_MONITORING_AND_LOGGING: "Ensure monitoring and logging in network resources.", } @@ -99,32 +105,40 @@ FINDING_EXPLANATION_BY_STATUS_KEY = "finding_explanation" TEST_EXPLANATION_KEY = "explanation" TESTS_MAP = { TEST_SEGMENTATION: { - TEST_EXPLANATION_KEY: "The Monkey tried to scan and find machines that it can communicate with from the machine it's " + TEST_EXPLANATION_KEY: "The Monkey tried to scan and find machines that it can " + "communicate with from the machine it's " "running on, that belong to different network segments.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and logs.", - STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs.", + STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and" + " logs.", + STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, " + "check firewall logs.", }, PRINCIPLE_KEY: PRINCIPLE_SEGMENTATION, PILLARS_KEY: [NETWORKS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_PASSED, STATUS_FAILED], }, TEST_MALICIOUS_ACTIVITY_TIMELINE: { - TEST_EXPLANATION_KEY: "The Monkeys in the network performed malicious-looking actions, like scanning and attempting " + TEST_EXPLANATION_KEY: "The Monkeys in the network performed malicious-looking " + "actions, like scanning and attempting " "exploitation.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_VERIFY: "Monkey performed malicious actions in the network. Check SOC logs and alerts." + STATUS_VERIFY: "Monkey performed malicious actions in the network. Check SOC logs and " + "alerts." }, PRINCIPLE_KEY: PRINCIPLE_ANALYZE_NETWORK_TRAFFIC, PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY], }, TEST_ENDPOINT_SECURITY_EXISTS: { - TEST_EXPLANATION_KEY: "The Monkey checked if there is an active process of an endpoint security software.", + TEST_EXPLANATION_KEY: "The Monkey checked if there is an active process of an " + "endpoint security software.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus " + STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and " + "activate anti-virus " "software on endpoints.", - STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a " + STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to " + "see if Monkey was a " "security concern. ", }, PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY, @@ -132,9 +146,11 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED], }, TEST_MACHINE_EXPLOITED: { - TEST_EXPLANATION_KEY: "The Monkey tries to exploit machines in order to breach them and propagate in the network.", + TEST_EXPLANATION_KEY: "The Monkey tries to exploit machines in order to " + "breach them and propagate in the network.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see " + STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see " + "activity recognized and see " "which endpoints were compromised.", STATUS_PASSED: "Monkey didn't manage to exploit an endpoint.", }, @@ -145,7 +161,8 @@ TESTS_MAP = { TEST_SCHEDULED_EXECUTION: { TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security " + STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in " + "User-Behavior security " "software.", STATUS_PASSED: "Monkey failed to execute in a scheduled manner.", }, @@ -154,10 +171,13 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY], }, TEST_DATA_ENDPOINT_ELASTIC: { - TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to ElasticSearch instances.", + TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to " + "ElasticSearch instances.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.", - STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts " + STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by " + "encrypting it in in-transit.", + STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such " + "instances, look for alerts " "that indicate attempts to access them. ", }, PRINCIPLE_KEY: PRINCIPLE_DATA_CONFIDENTIALITY, @@ -165,10 +185,12 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED], }, TEST_DATA_ENDPOINT_HTTP: { - TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to HTTP servers.", + TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to HTTP " "servers.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.", - STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate " + STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in" + " in-transit.", + STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, " + "look for alerts that indicate " "attempts to access them. ", }, PRINCIPLE_KEY: PRINCIPLE_DATA_CONFIDENTIALITY, @@ -176,10 +198,12 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED], }, TEST_DATA_ENDPOINT_POSTGRESQL: { - TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to PostgreSQL servers.", + TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to " "PostgreSQL servers.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey accessed PostgreSQL servers. Limit access to data by encrypting it in in-transit.", - STATUS_PASSED: "Monkey didn't find open PostgreSQL servers. If you have such servers, look for alerts that " + STATUS_FAILED: "Monkey accessed PostgreSQL servers. Limit access to data by encrypting" + " it in in-transit.", + STATUS_PASSED: "Monkey didn't find open PostgreSQL servers. If you have such servers, " + "look for alerts that " "indicate attempts to access them. ", }, PRINCIPLE_KEY: PRINCIPLE_DATA_CONFIDENTIALITY, @@ -189,7 +213,8 @@ TESTS_MAP = { TEST_TUNNELING: { TEST_EXPLANATION_KEY: "The Monkey tried to tunnel traffic using other monkeys.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - " + STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies " + "are too permissive - " "restrict them. " }, PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES, @@ -197,9 +222,11 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED], }, TEST_COMMUNICATE_AS_NEW_USER: { - TEST_EXPLANATION_KEY: "The Monkey tried to create a new user and communicate with the internet from it.", + TEST_EXPLANATION_KEY: "The Monkey tried to create a new user and communicate " + "with the internet from it.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - " + STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies " + "are too permissive - " "restrict them to MAC only.", STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network.", }, @@ -218,7 +245,7 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED], }, TEST_SCOUTSUITE_UNENCRYPTED_DATA: { - TEST_EXPLANATION_KEY: "ScoutSuite searched for resources containing unencrypted data.", + TEST_EXPLANATION_KEY: "ScoutSuite searched for resources containing " "unencrypted data.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_FAILED: "ScoutSuite found resources with unencrypted data.", STATUS_PASSED: "ScoutSuite found no resources with unencrypted data.", @@ -228,7 +255,8 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED], }, TEST_SCOUTSUITE_DATA_LOSS_PREVENTION: { - TEST_EXPLANATION_KEY: "ScoutSuite searched for resources which are not protected against data loss.", + TEST_EXPLANATION_KEY: "ScoutSuite searched for resources which are not " + "protected against data loss.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_FAILED: "ScoutSuite found resources not protected against data loss.", STATUS_PASSED: "ScoutSuite found that all resources are secured against data loss.", @@ -238,7 +266,7 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED], }, TEST_SCOUTSUITE_SECURE_AUTHENTICATION: { - TEST_EXPLANATION_KEY: "ScoutSuite searched for issues related to users' authentication.", + TEST_EXPLANATION_KEY: "ScoutSuite searched for issues related to users' " "authentication.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_FAILED: "ScoutSuite found issues related to users' authentication.", STATUS_PASSED: "ScoutSuite found no issues related to users' authentication.", @@ -248,7 +276,7 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED], }, TEST_SCOUTSUITE_RESTRICTIVE_POLICIES: { - TEST_EXPLANATION_KEY: "ScoutSuite searched for permissive user access policies.", + TEST_EXPLANATION_KEY: "ScoutSuite searched for permissive user access " "policies.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_FAILED: "ScoutSuite found permissive user access policies.", STATUS_PASSED: "ScoutSuite found no issues related to user access policies.", diff --git a/monkey/common/network/network_range.py b/monkey/common/network/network_range.py index 581c4bf77..a9e05caa6 100644 --- a/monkey/common/network/network_range.py +++ b/monkey/common/network/network_range.py @@ -159,7 +159,8 @@ class SingleIpRange(NetworkRange): @staticmethod def string_to_host(string_): """ - Converts the string that user entered in "Scan IP/subnet list" to a tuple of domain name and ip + Converts the string that user entered in "Scan IP/subnet list" to a tuple of domain name + and ip :param string_: String that was entered in "Scan IP/subnet list" :return: A tuple in format (IP, domain_name). Eg. (192.168.55.1, www.google.com) """ diff --git a/monkey/common/network/network_utils.py b/monkey/common/network/network_utils.py index 6aa5076ae..2b01d1974 100644 --- a/monkey/common/network/network_utils.py +++ b/monkey/common/network/network_utils.py @@ -4,8 +4,10 @@ from urllib.parse import urlparse def get_host_from_network_location(network_location: str) -> str: """ - URL structure is ":///;?#" (https://tools.ietf.org/html/rfc1808.html) - And the net_loc is ":@:" (https://tools.ietf.org/html/rfc1738#section-3.1) + URL structure is ":///;?#" ( + https://tools.ietf.org/html/rfc1808.html) + And the net_loc is ":@:" ( + https://tools.ietf.org/html/rfc1738#section-3.1) :param network_location: server network location :return: host part of the network location """ diff --git a/monkey/common/network/segmentation_utils.py b/monkey/common/network/segmentation_utils.py index 9bbaabf1d..d48c005cb 100644 --- a/monkey/common/network/segmentation_utils.py +++ b/monkey/common/network/segmentation_utils.py @@ -14,8 +14,10 @@ def get_ip_in_src_and_not_in_dst(ip_addresses, source_subnet, target_subnet): def get_ip_if_in_subnet(ip_addresses, subnet): """ :param ip_addresses: IP address list. - :param subnet: Subnet to check if one of ip_addresses is in there. This is common.network.network_range.NetworkRange - :return: The first IP in ip_addresses which is in the subnet if there is one, otherwise returns None. + :param subnet: Subnet to check if one of ip_addresses is in there. This is + common.network.network_range.NetworkRange + :return: The first IP in ip_addresses which is in the subnet if there is one, otherwise + returns None. """ for ip_address in ip_addresses: if subnet.is_in_range(ip_address): diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index c911ed780..98b6361c4 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -15,7 +15,8 @@ class ScanStatus(Enum): class UsageEnum(Enum): SMB = { ScanStatus.USED.value: "SMB exploiter ran the monkey by creating a service via MS-SCMR.", - ScanStatus.SCANNED.value: "SMB exploiter failed to run the monkey by creating a service via MS-SCMR.", + ScanStatus.SCANNED.value: "SMB exploiter failed to run the monkey by creating a service " + "via MS-SCMR.", } MIMIKATZ = { ScanStatus.USED.value: "Windows module loader was used to load Mimikatz DLL.", @@ -29,7 +30,8 @@ class UsageEnum(Enum): ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot." } SINGLETON_WINAPI = { - ScanStatus.USED.value: "WinAPI was called to acquire system singleton for monkey's process.", + ScanStatus.USED.value: "WinAPI was called to acquire system singleton for monkey's " + "process.", ScanStatus.SCANNED.value: "WinAPI call to acquire system singleton" " for monkey process wasn't successful.", } diff --git a/monkey/common/version.py b/monkey/common/version.py index 4070fc2f6..3eb35274e 100644 --- a/monkey/common/version.py +++ b/monkey/common/version.py @@ -1,4 +1,5 @@ -# To get the version from shell, run `python ./version.py` (see `python ./version.py -h` for details). +# To get the version from shell, run `python ./version.py` (see `python ./version.py -h` for +# details). import argparse from pathlib import Path diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 7aeaccee2..ffdea551e 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -227,7 +227,8 @@ class Configuration(object): @staticmethod def hash_sensitive_data(sensitive_data): """ - Hash sensitive data (e.g. passwords). Used so the log won't contain sensitive data plain-text, as the log is + Hash sensitive data (e.g. passwords). Used so the log won't contain sensitive data + plain-text, as the log is saved on client machines plain-text. :param sensitive_data: the data to hash. diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 4ccd2bec4..0df989d99 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -23,7 +23,6 @@ from infection_monkey.utils.exceptions.planned_shutdown_exception import Planned __author__ = "hoffer" - requests.packages.urllib3.disable_warnings() LOG = logging.getLogger(__name__) @@ -32,7 +31,8 @@ DOWNLOAD_CHUNK = 1024 PBA_FILE_DOWNLOAD = "https://%s/api/pba/download/%s" # random number greater than 5, -# to prevent the monkey from just waiting forever to try and connect to an island before going elsewhere. +# to prevent the monkey from just waiting forever to try and connect to an island before going +# elsewhere. TIMEOUT_IN_SECONDS = 15 @@ -412,7 +412,10 @@ class ControlClient(object): @staticmethod def can_island_see_port(port): try: - url = f"https://{WormConfiguration.current_server}/api/monkey_control/check_remote_port/{port}" + url = ( + f"https://{WormConfiguration.current_server}/api/monkey_control" + f"/check_remote_port/{port}" + ) response = requests.get(url, verify=False, timeout=SHORT_REQUEST_TIMEOUT) response = json.loads(response.content.decode()) return response["status"] == "port_visible" diff --git a/monkey/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py index 74c20321b..3d34688ef 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -152,7 +152,8 @@ class MonkeyDrops(object): ) else: dest_path = self._config["destination_path"] - # In linux we have a more complex commandline. There's a general outer one, and the inner one which actually + # In linux we have a more complex commandline. There's a general outer one, + # and the inner one which actually # runs the monkey inner_monkey_cmdline = ( MONKEY_CMDLINE_LINUX % {"monkey_filename": dest_path.split("/")[-1]} @@ -207,7 +208,8 @@ class MonkeyDrops(object): dropper_source_path_ctypes, None, MOVEFILE_DELAY_UNTIL_REBOOT ): LOG.debug( - "Error marking source file '%s' for deletion on next boot (error %d)", + "Error marking source file '%s' for deletion on next boot (error " + "%d)", self._config["source_path"], ctypes.windll.kernel32.GetLastError(), ) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index e5bdf6dfe..95de0ec07 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -10,7 +10,6 @@ from infection_monkey.utils.plugins.plugin import Plugin __author__ = "itamar" - logger = logging.getLogger(__name__) @@ -37,7 +36,8 @@ class HostExploiter(Plugin): EXPLOIT_TYPE = ExploitType.VULNERABILITY # Determines if successful exploitation should stop further exploit attempts on that machine. - # Generally, should be True for RCE type exploiters and False if we don't expect the exploiter to run the monkey agent. + # Generally, should be True for RCE type exploiters and False if we don't expect the + # exploiter to run the monkey agent. # Example: Zerologon steals credentials RUNS_AGENT_ON_SUCCESS = True diff --git a/monkey/infection_monkey/exploit/drupal.py b/monkey/infection_monkey/exploit/drupal.py index efbbc2f56..50594e656 100644 --- a/monkey/infection_monkey/exploit/drupal.py +++ b/monkey/infection_monkey/exploit/drupal.py @@ -1,7 +1,8 @@ """ Remote Code Execution on Drupal server - CVE-2019-6340 Implementation is based on: - https://gist.github.com/leonjza/d0ab053be9b06fa020b66f00358e3d88/f9f6a5bb6605745e292bee3a4079f261d891738a. + https://gist.github.com/leonjza/d0ab053be9b06fa020b66f00358e3d88 + /f9f6a5bb6605745e292bee3a4079f261d891738a. """ import logging @@ -28,7 +29,8 @@ class DrupalExploiter(WebRCE): def get_exploit_config(self): """ - We override this function because the exploits requires a special extension in the URL, "node", + We override this function because the exploits requires a special extension in the URL, + "node", e.g. an exploited URL would be http://172.1.2.3:/node/3. :return: the Drupal exploit config """ @@ -42,7 +44,8 @@ class DrupalExploiter(WebRCE): def add_vulnerable_urls(self, potential_urls, stop_checking=False): """ - We need a specific implementation of this function in order to add the URLs *with the node IDs*. + We need a specific implementation of this function in order to add the URLs *with the + node IDs*. We therefore check, for every potential URL, all possible node IDs. :param potential_urls: Potentially-vulnerable URLs :param stop_checking: Stop if one vulnerable URL is found @@ -71,7 +74,8 @@ class DrupalExploiter(WebRCE): def check_if_exploitable(self, url): """ Check if a certain URL is exploitable. - We use this specific implementation (and not simply run self.exploit) because this function does not "waste" + We use this specific implementation (and not simply run self.exploit) because this + function does not "waste" a vulnerable URL. Namely, we're not actually exploiting, merely checking using a heuristic. :param url: Drupal's URL and port :return: Vulnerable URL if exploitable, otherwise False @@ -117,7 +121,8 @@ class DrupalExploiter(WebRCE): def get_target_url(self): """ - We're overriding this method such that every time self.exploit is invoked, we use a fresh vulnerable URL. + We're overriding this method such that every time self.exploit is invoked, we use a fresh + vulnerable URL. Reusing the same URL eliminates its exploitability because of caching reasons :) :return: vulnerable URL to exploit """ @@ -128,13 +133,15 @@ class DrupalExploiter(WebRCE): For the Drupal exploit, 5 distinct URLs are needed to perform the full attack. :return: Whether the list of vulnerable URLs has at least 5 elements. """ - # We need 5 URLs for a "full-chain": check remote files, check architecture, drop monkey, chmod it and run it. + # We need 5 URLs for a "full-chain": check remote files, check architecture, drop monkey, + # chmod it and run it. num_urls_needed_for_full_exploit = 5 num_available_urls = len(self.vulnerable_urls) result = num_available_urls >= num_urls_needed_for_full_exploit if not result: LOG.info( - f"{num_urls_needed_for_full_exploit} URLs are needed to fully exploit a Drupal server " + f"{num_urls_needed_for_full_exploit} URLs are needed to fully exploit a " + f"Drupal server " f"but only {num_available_urls} found" ) return result diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index ca1c0408b..734e7e5ef 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -1,6 +1,7 @@ """ Implementation is based on elastic search groovy exploit by metasploit - https://github.com/rapid7/metasploit-framework/blob/12198a088132f047e0a86724bc5ebba92a73ac66/modules/exploits/multi/elasticsearch/search_groovy_script.rb + https://github.com/rapid7/metasploit-framework/blob/12198a088132f047e0a86724bc5ebba92a73ac66 + /modules/exploits/multi/elasticsearch/search_groovy_script.rb Max vulnerable elasticsearch version is "1.4.2" """ @@ -35,9 +36,9 @@ class ElasticGroovyExploiter(WebRCE): GENERIC_QUERY = ( """{"size":1, "script_fields":{"%s": {"script": "%%s"}}}""" % MONKEY_RESULT_FIELD ) - JAVA_CMD = ( - GENERIC_QUERY - % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()""" + JAVA_CMD = GENERIC_QUERY % ( + """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(""" + """\\"%s\\").getText()""" ) _TARGET_OS_TYPE = ["linux", "windows"] @@ -57,7 +58,8 @@ class ElasticGroovyExploiter(WebRCE): return exploit_config def get_open_service_ports(self, port_list, names): - # We must append elastic port we get from elastic fingerprint module because It's not marked as 'http' service + # We must append elastic port we get from elastic fingerprint module because It's not + # marked as 'http' service valid_ports = super(ElasticGroovyExploiter, self).get_open_service_ports(port_list, names) if ES_SERVICE in self.host.services: valid_ports.append([ES_PORT, False]) @@ -70,7 +72,8 @@ class ElasticGroovyExploiter(WebRCE): response = requests.get(url, data=payload, timeout=DOWNLOAD_TIMEOUT) except requests.ReadTimeout: LOG.error( - "Elastic couldn't upload monkey, because server didn't respond to upload request." + "Elastic couldn't upload monkey, because server didn't respond to upload " + "request." ) return False result = self.get_results(response) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index d92c39f6c..a30112cce 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -1,6 +1,7 @@ """ Remote code execution on HADOOP server with YARN and default settings - Implementation is based on code from https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn + Implementation is based on code from + https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn """ import json diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 893ee8ca1..24b46d278 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -56,7 +56,8 @@ class MSSQLExploiter(HostExploiter): def _exploit_host(self): """ First this method brute forces to get the mssql connection (cursor). - Also, don't forget to start_monkey_server() before self.upload_monkey() and self.stop_monkey_server() after + Also, don't forget to start_monkey_server() before self.upload_monkey() and + self.stop_monkey_server() after """ # Brute force to get connection username_passwords_pairs_list = self._config.get_exploit_user_password_pairs() @@ -181,10 +182,12 @@ class MSSQLExploiter(HostExploiter): Args: host (str): Host ip address port (str): Tcp port that the host listens to - users_passwords_pairs_list (list): a list of users and passwords pairs to bruteforce with + users_passwords_pairs_list (list): a list of users and passwords pairs to bruteforce + with Return: - True or False depends if the whole bruteforce and attack process was completed successfully or not + True or False depends if the whole bruteforce and attack process was completed + successfully or not """ # Main loop # Iterates on users list @@ -196,9 +199,8 @@ class MSSQLExploiter(HostExploiter): host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT ) LOG.info( - "Successfully connected to host: {0}, using user: {1}, password (SHA-512): {2}".format( - host, user, self._config.hash_sensitive_data(password) - ) + "Successfully connected to host: {0}, using user: {1}, password (" + "SHA-512): {2}".format(host, user, self._config.hash_sensitive_data(password)) ) self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) self.report_login_attempt(True, user, password) diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index b0387105e..72d36e234 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -54,7 +54,8 @@ LOG = logging.getLogger(__name__) class SambaCryExploiter(HostExploiter): """ - SambaCry exploit module, partially based on the following implementation by CORE Security Technologies' impacket: + SambaCry exploit module, partially based on the following implementation by CORE Security + Technologies' impacket: https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py """ @@ -372,7 +373,8 @@ class SambaCryExploiter(HostExploiter): # the extra / on the beginning is required for the vulnerability self.open_pipe(smb_client, "/" + module_path) except Exception as e: - # This is the expected result. We can't tell whether we succeeded or not just by this error code. + # This is the expected result. We can't tell whether we succeeded or not just by this + # error code. if str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0: return True else: @@ -450,7 +452,8 @@ class SambaCryExploiter(HostExploiter): ) return smb_client - # Following are slightly modified SMB functions from impacket to fit our needs of the vulnerability # + # Following are slightly modified SMB functions from impacket to fit our needs of the + # vulnerability # @staticmethod def create_smb( smb_client, @@ -513,7 +516,8 @@ class SambaCryExploiter(HostExploiter): @staticmethod def open_pipe(smb_client, pathName): - # We need to overwrite Impacket's openFile functions since they automatically convert paths to NT style + # We need to overwrite Impacket's openFile functions since they automatically convert + # paths to NT style # to make things easier for the caller. Not this time ;) treeId = smb_client.connectTree("IPC$") LOG.debug("Triggering path: %s" % pathName) diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 11932c3f5..7854483a0 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -1,4 +1,5 @@ -# Implementation is based on shellshock script provided https://github.com/nccgroup/shocker/blob/master/shocker.py +# Implementation is based on shellshock script provided +# https://github.com/nccgroup/shocker/blob/master/shocker.py import logging import string @@ -113,7 +114,8 @@ class ShellShockExploiter(HostExploiter): self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux) ): LOG.info( - "Host %s was already infected under the current configuration, done" % self.host + "Host %s was already infected under the current configuration, " + "done" % self.host ) return True # return already infected @@ -270,7 +272,8 @@ class ShellShockExploiter(HostExploiter): break if timeout: LOG.debug( - "Some connections timed out while sending request to potentially vulnerable urls." + "Some connections timed out while sending request to potentially vulnerable " + "urls." ) valid_resps = [req for req in reqs if req and req.status_code == requests.codes.ok] urls = [resp.url for resp in valid_resps] diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 4b5e941f8..81fc2848c 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -75,7 +75,8 @@ class SmbExploiter(HostExploiter): if remote_full_path is not None: LOG.debug( - "Successfully logged in %r using SMB (%s : (SHA-512) %s : (SHA-512) %s : (SHA-512) %s)", + "Successfully logged in %r using SMB (%s : (SHA-512) %s : (SHA-512) " + "%s : (SHA-512) %s)", self.host, user, self._config.hash_sensitive_data(password), @@ -99,7 +100,8 @@ class SmbExploiter(HostExploiter): except Exception as exc: LOG.debug( "Exception when trying to copy file using SMB to %r with user:" - " %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (SHA-512): %s: (%s)", + " %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (" + "SHA-512): %s: (%s)", self.host, user, self._config.hash_sensitive_data(password), diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 0f5af3258..3dedae114 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -157,7 +157,8 @@ class SSHExploiter(HostExploiter): if stdout_res: # file exists LOG.info( - "Host %s was already infected under the current configuration, done" % self.host + "Host %s was already infected under the current configuration, " + "done" % self.host ) return True # return already infected diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py index c08c174fb..ff5b9887b 100644 --- a/monkey/infection_monkey/exploit/struts2.py +++ b/monkey/infection_monkey/exploit/struts2.py @@ -35,7 +35,8 @@ class Struts2Exploiter(WebRCE): def build_potential_urls(self, ports, extensions=None): """ We need to override this method to get redirected url's - :param ports: Array of ports. One port is described as size 2 array: [port.no(int), isHTTPS?(bool)] + :param ports: Array of ports. One port is described as size 2 array: [port.no(int), + isHTTPS?(bool)] Eg. ports: [[80, False], [443, True]] :param extensions: What subdirectories to scan. www.domain.com[/extension] :return: Array of url's to try and attack diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py index cf94f6edc..a863f9499 100644 --- a/monkey/infection_monkey/exploit/tools/helpers.py +++ b/monkey/infection_monkey/exploit/tools/helpers.py @@ -29,7 +29,8 @@ def get_target_monkey(host): if not monkey_path: if host.os.get("type") == platform.system().lower(): - # if exe not found, and we have the same arch or arch is unknown and we are 32bit, use our exe + # if exe not found, and we have the same arch or arch is unknown and we are 32bit, + # use our exe if (not host.os.get("machine") and sys.maxsize < 2 ** 32) or host.os.get( "machine", "" ).lower() == platform.machine().lower(): diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing.py b/monkey/infection_monkey/exploit/tools/payload_parsing.py index 052ab18e5..2d38b593c 100644 --- a/monkey/infection_monkey/exploit/tools/payload_parsing.py +++ b/monkey/infection_monkey/exploit/tools/payload_parsing.py @@ -17,7 +17,8 @@ class Payload(object): def get_payload(self, command=""): """ Returns prefixed and suffixed command (payload) - :param command: Command to suffix/prefix. If no command is passed than objects' property is used + :param command: Command to suffix/prefix. If no command is passed than objects' property + is used :return: prefixed and suffixed command (full payload) """ if not command: @@ -46,7 +47,8 @@ class LimitedSizePayload(Payload): def split_into_array_of_smaller_payloads(self): if self.is_suffix_and_prefix_too_long(): raise Exception( - "Can't split command into smaller sub-commands because commands' prefix and suffix already " + "Can't split command into smaller sub-commands because commands' prefix and " + "suffix already " "exceeds required length of command." ) diff --git a/monkey/infection_monkey/exploit/tools/smb_tools.py b/monkey/infection_monkey/exploit/tools/smb_tools.py index 9943b4135..5fc2b4a44 100644 --- a/monkey/infection_monkey/exploit/tools/smb_tools.py +++ b/monkey/infection_monkey/exploit/tools/smb_tools.py @@ -36,7 +36,8 @@ class SmbTools(object): # skip guest users if smb.isGuestSession() > 0: LOG.debug( - "Connection to %r granted guest privileges with user: %s, password (SHA-512): '%s'," + "Connection to %r granted guest privileges with user: %s, password (SHA-512): " + "'%s'," " LM hash (SHA-512): %s, NTLM hash (SHA-512): %s", host, username, @@ -151,7 +152,7 @@ class SmbTools(object): return remote_full_path LOG.debug( - "Remote monkey file is found but different, moving along with attack" + "Remote monkey file is found but different, moving along with " "attack" ) except Exception: pass # file isn't found on remote victim, moving on @@ -197,7 +198,8 @@ class SmbTools(object): if not file_uploaded: LOG.debug( "Couldn't find a writable share for exploiting victim %r with " - "username: %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (SHA-512): %s", + "username: %s, password (SHA-512): '%s', LM hash (SHA-512): %s, NTLM hash (" + "SHA-512): %s", host, username, Configuration.hash_sensitive_data(password), diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index d8e88b44c..8af8e24d9 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -1,6 +1,7 @@ """ Implementation is based on VSFTPD v2.3.4 Backdoor Command Execution exploit by metasploit - https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/unix/ftp/vsftpd_234_backdoor.rb + https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/unix/ftp + /vsftpd_234_backdoor.rb only vulnerable version is "2.3.4" """ @@ -151,7 +152,8 @@ class VSFTPDExploiter(HostExploiter): } # Set unlimited to memory - # we don't have to revert the ulimit because it just applies to the shell obtained by our exploit + # we don't have to revert the ulimit because it just applies to the shell obtained by our + # exploit run_monkey = ULIMIT_V + UNLIMITED + run_monkey run_monkey = str.encode(str(run_monkey) + "\n") time.sleep(FTP_TIME_BUFFER) diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index f51fe1539..dafa6164a 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -69,21 +69,27 @@ class WebRCE(HostExploiter): """ exploit_config = {} - # dropper: If true monkey will use dropper parameter that will detach monkey's process and try to copy + # dropper: If true monkey will use dropper parameter that will detach monkey's process + # and try to copy # it's file to the default destination path. exploit_config["dropper"] = False - # upload_commands: Unformatted dict with one or two commands {'linux': WGET_HTTP_UPLOAD,'windows': WIN_CMD} - # Command must have "monkey_path" and "http_path" format parameters. If None defaults will be used. + # upload_commands: Unformatted dict with one or two commands {'linux': WGET_HTTP_UPLOAD, + # 'windows': WIN_CMD} + # Command must have "monkey_path" and "http_path" format parameters. If None defaults + # will be used. exploit_config["upload_commands"] = None - # url_extensions: What subdirectories to scan (www.domain.com[/extension]). Eg. ["home", "index.php"] + # url_extensions: What subdirectories to scan (www.domain.com[/extension]). Eg. ["home", + # "index.php"] exploit_config["url_extensions"] = [] - # stop_checking_urls: If true it will stop checking vulnerable urls once one was found vulnerable. + # stop_checking_urls: If true it will stop checking vulnerable urls once one was found + # vulnerable. exploit_config["stop_checking_urls"] = False - # blind_exploit: If true we won't check if file exist and won't try to get the architecture of target. + # blind_exploit: If true we won't check if file exist and won't try to get the + # architecture of target. exploit_config["blind_exploit"] = False return exploit_config @@ -200,7 +206,8 @@ class WebRCE(HostExploiter): except KeyError: LOG.error( "Provided command is missing/bad for this type of host! " - "Check upload_monkey function docs before using custom monkey's upload commands." + "Check upload_monkey function docs before using custom monkey's upload " + "commands." ) return False return command @@ -225,8 +232,10 @@ class WebRCE(HostExploiter): def build_potential_urls(self, ports, extensions=None): """ - Build all possibly-vulnerable URLs on a specific host, based on the relevant ports and extensions. - :param ports: Array of ports. One port is described as size 2 array: [port.no(int), isHTTPS?(bool)] + Build all possibly-vulnerable URLs on a specific host, based on the relevant ports and + extensions. + :param ports: Array of ports. One port is described as size 2 array: [port.no(int), + isHTTPS?(bool)] Eg. ports: [[80, False], [443, True]] :param extensions: What subdirectories to scan. www.domain.com[/extension] :return: Array of url's to try and attack @@ -253,7 +262,8 @@ class WebRCE(HostExploiter): """ Gets vulnerable url(s) from url list :param urls: Potentially vulnerable urls - :param stop_checking: If we want to continue checking for vulnerable url even though one is found (bool) + :param stop_checking: If we want to continue checking for vulnerable url even though one + is found (bool) :return: None (we append to class variable vulnerable_urls) """ for url in urls: @@ -330,7 +340,8 @@ class WebRCE(HostExploiter): Get ports wrapped with log :param ports: Potential ports to exploit. For example WormConfiguration.HTTP_PORTS :param names: [] of service names. Example: ["http"] - :return: Array of ports: [[80, False], [443, True]] or False. Port always consists of [ port.nr, IsHTTPS?] + :return: Array of ports: [[80, False], [443, True]] or False. Port always consists of [ + port.nr, IsHTTPS?] """ ports = self.get_open_service_ports(ports, names) if not ports: @@ -350,7 +361,8 @@ class WebRCE(HostExploiter): def run_backup_commands(self, resp, url, dest_path, http_path): """ - If you need multiple commands for the same os you can override this method to add backup commands + If you need multiple commands for the same os you can override this method to add backup + commands :param resp: Response from base command :param url: Vulnerable url :param dest_path: Where to upload monkey @@ -370,7 +382,8 @@ class WebRCE(HostExploiter): def upload_monkey(self, url, commands=None): """ :param url: Where exploiter should send it's request - :param commands: Unformatted dict with one or two commands {'linux': LIN_CMD, 'windows': WIN_CMD} + :param commands: Unformatted dict with one or two commands {'linux': LIN_CMD, 'windows': + WIN_CMD} Command must have "monkey_path" and "http_path" format parameters. :return: {'response': response/False, 'path': monkeys_path_in_host} """ @@ -435,7 +448,7 @@ class WebRCE(HostExploiter): return False elif "No such file or directory" in resp: LOG.error( - "Could not change permission because monkey was not found. Check path parameter." + "Could not change permission because monkey was not found. Check path " "parameter." ) return False LOG.info("Permission change finished") @@ -499,7 +512,8 @@ class WebRCE(HostExploiter): def get_monkey_upload_path(self, url_to_monkey): """ Gets destination path from one of WEB_RCE predetermined paths(self.monkey_target_paths). - :param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe + :param url_to_monkey: Hosted monkey's url. egz : + http://localserver:9999/monkey/windows-32.exe :return: Corresponding monkey path from self.monkey_target_paths """ if not url_to_monkey or ("linux" not in url_to_monkey and "windows" not in url_to_monkey): @@ -522,7 +536,8 @@ class WebRCE(HostExploiter): return False except KeyError: LOG.error( - 'Unknown key was found. Please use "linux", "win32" and "win64" keys to initialize ' + 'Unknown key was found. Please use "linux", "win32" and "win64" keys to ' + "initialize " "custom dict of monkey's destination paths" ) return False @@ -577,8 +592,10 @@ class WebRCE(HostExploiter): def are_vulnerable_urls_sufficient(self): """ - Determine whether the number of vulnerable URLs is sufficient in order to perform the full attack. - Often, a single URL will suffice. However, in some cases (e.g. the Drupal exploit) a vulnerable URL is for + Determine whether the number of vulnerable URLs is sufficient in order to perform the + full attack. + Often, a single URL will suffice. However, in some cases (e.g. the Drupal exploit) a + vulnerable URL is for single use, thus we need a couple of them. :return: Whether or not a full attack can be performed using the available vulnerable URLs. """ diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 2d1a40c0a..017050346 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -160,7 +160,8 @@ class WebLogic201710271(WebRCE): :param command: command itself :return: Formatted payload """ - empty_payload = """ + empty_payload = """ @@ -195,7 +196,8 @@ class WebLogic201710271(WebRCE): :param port: Server's port :return: Formatted payload """ - generic_check_payload = """ + generic_check_payload = """ @@ -272,7 +274,8 @@ class WebLogic20192725(WebRCE): return exploit_config def execute_remote_monkey(self, url, path, dropper=False): - # Without delay exploiter tries to launch monkey file that is still finishing up after downloading. + # Without delay exploiter tries to launch monkey file that is still finishing up after + # downloading. time.sleep(WebLogic20192725.DELAY_BEFORE_EXPLOITING_SECONDS) super(WebLogic20192725, self).execute_remote_monkey(url, path, dropper) @@ -307,7 +310,8 @@ class WebLogic20192725(WebRCE): """ empty_payload = """ + xmlns:wsa=\"http://www.w3.org/2005/08/addressing\" + xmlns:asy=\"http://www.bea.com/async/AsyncResponseService\"> xx xx diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 7120f5720..cad313f8c 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -142,7 +142,8 @@ class WmiExploiter(HostExploiter): success = True else: LOG.debug( - "Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", + "Error executing dropper '%s' on remote victim %r (pid=%d, exit_code=%d, " + "cmdline=%r)", remote_full_path, self.host, result.ProcessId, diff --git a/monkey/infection_monkey/exploit/zerologon.py b/monkey/infection_monkey/exploit/zerologon.py index 9c18b2de3..232436cdf 100644 --- a/monkey/infection_monkey/exploit/zerologon.py +++ b/monkey/infection_monkey/exploit/zerologon.py @@ -1,6 +1,7 @@ """ Zerologon, CVE-2020-1472 -Implementation based on https://github.com/dirkjanm/CVE-2020-1472/ and https://github.com/risksense/zerologon/. +Implementation based on https://github.com/dirkjanm/CVE-2020-1472/ and +https://github.com/risksense/zerologon/. """ import logging @@ -54,7 +55,8 @@ class ZerologonExploiter(HostExploiter): else: LOG.info( - "Exploit not attempted. Target is most likely patched, or an error was encountered." + "Exploit not attempted. Target is most likely patched, or an error was " + "encountered." ) return False @@ -131,7 +133,8 @@ class ZerologonExploiter(HostExploiter): self.report_login_attempt(result=False, user=self.dc_name) _exploited = False LOG.info( - f"Non-zero return code: {exploit_attempt_result['ErrorCode']}. Something went wrong." + f"Non-zero return code: {exploit_attempt_result['ErrorCode']}. Something " + f"went wrong." ) return _exploited @@ -194,7 +197,8 @@ class ZerologonExploiter(HostExploiter): def get_all_user_creds(self) -> List[Tuple[str, Dict]]: try: options = OptionsForSecretsdump( - target=f"{self.dc_name}$@{self.dc_ip}", # format for DC account - "NetBIOSName$@0.0.0.0" + # format for DC account - "NetBIOSName$@0.0.0.0" + target=f"{self.dc_name}$@{self.dc_ip}", target_ip=self.dc_ip, dc_ip=self.dc_ip, ) @@ -221,7 +225,8 @@ class ZerologonExploiter(HostExploiter): except Exception as e: LOG.info( - f"Exception occurred while dumping secrets to get some username and its password's NT hash: {str(e)}" + f"Exception occurred while dumping secrets to get some username and its " + f"password's NT hash: {str(e)}" ) return None @@ -310,7 +315,8 @@ class ZerologonExploiter(HostExploiter): except Exception as e: LOG.info( - f"Exception occurred while dumping secrets to get original DC password's NT hash: {str(e)}" + f"Exception occurred while dumping secrets to get original DC password's NT " + f"hash: {str(e)}" ) finally: @@ -339,7 +345,8 @@ class ZerologonExploiter(HostExploiter): + "reg save HKLM\\SECURITY security.save" ) - # Get HKLM keys locally (can't run these together because it needs to call do_get()). + # Get HKLM keys locally (can't run these together because it needs to call + # do_get()). remote_shell.onecmd("get system.save") remote_shell.onecmd("get sam.save") remote_shell.onecmd("get security.save") diff --git a/monkey/infection_monkey/exploit/zerologon_utils/dump_secrets.py b/monkey/infection_monkey/exploit/zerologon_utils/dump_secrets.py index 9d2116d07..f76fe361b 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/dump_secrets.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/dump_secrets.py @@ -132,8 +132,10 @@ class DumpSecrets: self.connect() except Exception as e: if os.getenv("KRB5CCNAME") is not None and self.__do_kerberos is True: - # SMBConnection failed. That might be because there was no way to log into the - # target system. We just have a last resort. Hope we have tickets cached and that they + # SMBConnection failed. That might be because there was no way to + # log into the + # target system. We just have a last resort. Hope we have tickets + # cached and that they # will work LOG.debug( "SMBConnection didn't work, hoping Kerberos will help (%s)" @@ -162,11 +164,13 @@ class DumpSecrets: and os.getenv("KRB5CCNAME") is not None and self.__do_kerberos is True ): - # Giving some hints here when SPN target name validation is set to something different to Off. - # This will prevent establishing SMB connections using TGS for SPNs different to cifs/. + # Giving some hints here when SPN target name validation is set to + # something different to Off. + # This will prevent establishing SMB connections using TGS for SPNs + # different to cifs/. LOG.error( - "Policy SPN target name validation might be restricting full DRSUAPI dump." - + "Try -just-dc-user" + "Policy SPN target name validation might be restricting full " + "DRSUAPI dump." + "Try -just-dc-user" ) else: LOG.error("RemoteOperations failed: %s" % str(e)) @@ -208,7 +212,8 @@ class DumpSecrets: LOG.debug(traceback.print_exc()) LOG.error("LSA hashes extraction failed: %s" % str(e)) - # NTDS Extraction we can try regardless of RemoteOperations failing. It might still work. + # NTDS Extraction we can try regardless of RemoteOperations failing. It might + # still work. if self.__is_remote is True: if self.__use_VSS_method and self.__remote_ops is not None: NTDS_file_name = self.__remote_ops.saveNTDS() @@ -231,7 +236,8 @@ class DumpSecrets: except Exception as e: LOG.debug(traceback.print_exc()) if str(e).find("ERROR_DS_DRA_BAD_DN") >= 0: - # We don't store the resume file if this error happened, since this error is related to lack + # We don't store the resume file if this error happened, since this error + # is related to lack # of enough privileges to access DRSUAPI. resume_file = self.__NTDS_hashes.getResumeSessionFile() if resume_file is not None: @@ -239,7 +245,8 @@ class DumpSecrets: LOG.error(e) if self.__use_VSS_method is False: LOG.error( - "Something wen't wrong with the DRSUAPI approach. Try again with -use-vss parameter" + "Something wen't wrong with the DRSUAPI approach. Try again with " + "-use-vss parameter" ) self.cleanup() except (Exception, KeyboardInterrupt) as e: diff --git a/monkey/infection_monkey/exploit/zerologon_utils/options.py b/monkey/infection_monkey/exploit/zerologon_utils/options.py index 32cdfe40f..0745dc4c6 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/options.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/options.py @@ -35,9 +35,11 @@ class OptionsForSecretsdump: target=None, target_ip=None, ): - # dc_ip is assigned in get_original_pwd_nthash() and get_admin_pwd_hashes() in ../zerologon.py + # dc_ip is assigned in get_original_pwd_nthash() and get_admin_pwd_hashes() in + # ../zerologon.py self.dc_ip = dc_ip - # just_dc becomes False, and sam, security, and system are assigned in get_original_pwd_nthash() in ../zerologon.py + # just_dc becomes False, and sam, security, and system are assigned in + # get_original_pwd_nthash() in ../zerologon.py self.just_dc = just_dc self.sam = sam self.security = security diff --git a/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py b/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py index 3b635f6b5..3097610fb 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py @@ -134,8 +134,10 @@ class RemoteShell(cmd.Cmd): self.__outputBuffer += data.decode(self.CODEC) except UnicodeDecodeError: LOG.error( - "Decoding error detected, consider running chcp.com at the target,\nmap the result with " - "https://docs.python.org/3/library/codecs.html#standard-encodings\nand then execute wmiexec.py " + "Decoding error detected, consider running chcp.com at the target," + "\nmap the result with " + "https://docs.python.org/3/library/codecs.html#standard-encodings\nand " + "then execute wmiexec.py " "again with -codec and the corresponding codec" ) self.__outputBuffer += data.decode(self.CODEC, errors="replace") diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py index e0c0eef08..9bdece16d 100644 --- a/monkey/infection_monkey/main.py +++ b/monkey/infection_monkey/main.py @@ -26,7 +26,8 @@ LOG_CONFIG = { "disable_existing_loggers": False, "formatters": { "standard": { - "format": "%(asctime)s [%(process)d:%(thread)d:%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s" + "format": "%(asctime)s [%(process)d:%(thread)d:%(levelname)s] %(module)s.%(" + "funcName)s.%(lineno)d: %(message)s" }, }, "handlers": { @@ -71,8 +72,8 @@ def main(): print("Error loading config: %s, using default" % (e,)) else: print( - "Config file wasn't supplied and default path: %s wasn't found, using internal default" - % (config_file,) + "Config file wasn't supplied and default path: %s wasn't found, using internal " + "default" % (config_file,) ) print( @@ -104,7 +105,8 @@ def main(): if WormConfiguration.use_file_logging: if os.path.exists(log_path): - # If log exists but can't be removed it means other monkey is running. This usually happens on upgrade + # If log exists but can't be removed it means other monkey is running. This usually + # happens on upgrade # from 32bit to 64bit monkey on Windows. In all cases this shouldn't be a problem. try: os.remove(log_path) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 7123d8b9e..c81a62517 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -227,7 +227,8 @@ class InfectionMonkey(object): host_exploited = True VictimHostTelem("T1210", ScanStatus.USED, machine=machine).send() if exploiter.RUNS_AGENT_ON_SUCCESS: - break # if adding machine to exploited, won't try other exploits on it + break # if adding machine to exploited, won't try other exploits + # on it if not host_exploited: self._fail_exploitation_machines.add(machine) VictimHostTelem("T1210", ScanStatus.SCANNED, machine=machine).send() @@ -244,7 +245,8 @@ class InfectionMonkey(object): elif not WormConfiguration.alive: LOG.info("Marked not alive from configuration") - # if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to + # if host was exploited, before continue to closing the tunnel ensure the exploited + # host had its chance to # connect to the tunnel if len(self._exploited_machines) > 0: time_to_sleep = WormConfiguration.keep_tunnel_open_time @@ -261,7 +263,8 @@ class InfectionMonkey(object): except PlannedShutdownException: LOG.info( - "A planned shutdown of the Monkey occurred. Logging the reason and finishing execution." + "A planned shutdown of the Monkey occurred. Logging the reason and finishing " + "execution." ) LOG.exception("Planned shutdown, reason:") diff --git a/monkey/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py index 21adae9f8..c30f3d436 100644 --- a/monkey/infection_monkey/network/info.py +++ b/monkey/infection_monkey/network/info.py @@ -88,7 +88,8 @@ else: continue try: ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", iff)) - except IOError: # interface is present in routing tables but does not have any assigned IP + except IOError: # interface is present in routing tables but does not have any + # assigned IP ifaddr = "0.0.0.0" else: addrfamily = struct.unpack("h", ifreq[16:18])[0] diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index 3113d278f..c743fa3a5 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -49,22 +49,21 @@ class MSSQLFinger(HostFinger): data, server = sock.recvfrom(self.BUFFER_SIZE) except socket.timeout: LOG.info( - "Socket timeout reached, maybe browser service on host: {0} doesnt exist".format( - host - ) + "Socket timeout reached, maybe browser service on host: {0} doesnt " + "exist".format(host) ) sock.close() return False except socket.error as e: if e.errno == errno.ECONNRESET: LOG.info( - "Connection was forcibly closed by the remote host. The host: {0} is rejecting the packet.".format( - host - ) + "Connection was forcibly closed by the remote host. The host: {0} is " + "rejecting the packet.".format(host) ) else: LOG.error( - "An unknown socket error occurred while trying the mssql fingerprint, closing socket.", + "An unknown socket error occurred while trying the mssql fingerprint, " + "closing socket.", exc_info=True, ) sock.close() @@ -82,7 +81,8 @@ class MSSQLFinger(HostFinger): if len(instance_info) > 1: host.services[self._SCANNED_SERVICE][instance_info[1]] = {} for i in range(1, len(instance_info), 2): - # Each instance's info is nested under its own name, if there are multiple instances + # Each instance's info is nested under its own name, if there are multiple + # instances # each will appear under its own name host.services[self._SCANNED_SERVICE][instance_info[1]][ instance_info[i - 1] diff --git a/monkey/infection_monkey/network/network_scanner.py b/monkey/infection_monkey/network/network_scanner.py index 0b8a75120..677466190 100644 --- a/monkey/infection_monkey/network/network_scanner.py +++ b/monkey/infection_monkey/network/network_scanner.py @@ -44,9 +44,11 @@ class NetworkScanner(object): def _get_inaccessible_subnets_ips(self): """ For each of the machine's IPs, checks if it's in one of the subnets specified in the - 'inaccessible_subnets' config value. If so, all other subnets in the config value shouldn't be accessible. + 'inaccessible_subnets' config value. If so, all other subnets in the config value + shouldn't be accessible. All these subnets are returned. - :return: A list of subnets that shouldn't be accessible from the machine the monkey is running on. + :return: A list of subnets that shouldn't be accessible from the machine the monkey is + running on. """ subnets_to_scan = [] if len(WormConfiguration.inaccessible_subnets) > 1: @@ -54,7 +56,8 @@ class NetworkScanner(object): if NetworkScanner._is_any_ip_in_subnet( [str(x) for x in self._ip_addresses], subnet_str ): - # If machine has IPs from 2 different subnets in the same group, there's no point checking the other + # If machine has IPs from 2 different subnets in the same group, there's no + # point checking the other # subnet. for other_subnet_str in WormConfiguration.inaccessible_subnets: if other_subnet_str == subnet_str: @@ -74,9 +77,12 @@ class NetworkScanner(object): :param stop_callback: A callback to check at any point if we should stop scanning :return: yields a sequence of VictimHost instances """ - # We currently use the ITERATION_BLOCK_SIZE as the pool size, however, this may not be the best decision - # However, the decision what ITERATION_BLOCK_SIZE also requires balancing network usage (pps and bw) - # Because we are using this to spread out IO heavy tasks, we can probably go a lot higher than CPU core size + # We currently use the ITERATION_BLOCK_SIZE as the pool size, however, this may not be + # the best decision + # However, the decision what ITERATION_BLOCK_SIZE also requires balancing network usage ( + # pps and bw) + # Because we are using this to spread out IO heavy tasks, we can probably go a lot higher + # than CPU core size # But again, balance pool = Pool(ITERATION_BLOCK_SIZE) victim_generator = VictimHostGenerator( diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index dd1577e47..73c89f841 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -59,7 +59,8 @@ class PingScanner(HostScanner, HostFinger): ttl = int(regex_result.group(0)) if ttl <= LINUX_TTL: host.os["type"] = "linux" - else: # as far we we know, could also be OSX/BSD but lets handle that when it comes up. + else: # as far we we know, could also be OSX/BSD but lets handle that when it + # comes up. host.os["type"] = "windows" host.icmp = True diff --git a/monkey/infection_monkey/network/postgresql_finger.py b/monkey/infection_monkey/network/postgresql_finger.py index 16f6327f9..f19c128e8 100644 --- a/monkey/infection_monkey/network/postgresql_finger.py +++ b/monkey/infection_monkey/network/postgresql_finger.py @@ -51,12 +51,14 @@ class PostgreSQLFinger(HostFinger): self.init_service(host.services, self._SCANNED_SERVICE, self.POSTGRESQL_DEFAULT_PORT) host.services[self._SCANNED_SERVICE]["communication_encryption_details"] = ( "The PostgreSQL server was unexpectedly accessible with the credentials - " - + f"user: '{self.CREDS['username']}' and password: '{self.CREDS['password']}'. Is this a honeypot?" + + f"user: '{self.CREDS['username']}' and password: '" + f"{self.CREDS['password']}'. Is this a honeypot?" ) return True except psycopg2.OperationalError as ex: - # try block will throw an OperationalError since the credentials are wrong, which we then analyze + # try block will throw an OperationalError since the credentials are wrong, which we + # then analyze try: exception_string = str(ex) diff --git a/monkey/infection_monkey/network/smbfinger.py b/monkey/infection_monkey/network/smbfinger.py index 457d0213d..b035a053f 100644 --- a/monkey/infection_monkey/network/smbfinger.py +++ b/monkey/infection_monkey/network/smbfinger.py @@ -68,14 +68,16 @@ class SMBNegoFingerData(Packet): ("separator1", b"\x02"), ( "dialect1", - b"\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00", + b"\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d" + b"\x20\x31\x2e\x30\x00", ), ("separator2", b"\x02"), ("dialect2", b"\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"), ("separator3", b"\x02"), ( "dialect3", - b"\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00", + b"\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72" + b"\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00", ), ("separator4", b"\x02"), ("dialect4", b"\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00"), @@ -104,12 +106,18 @@ class SMBSessionFingerData(Packet): ("bcc1", ""), ( "Data", - b"\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02" - b"\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f" - b"\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63" - b"\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00" - b"\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35" + b"\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c" + b"\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02" + b"\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00" + b"\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f" + b"\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f" + b"\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53" + b"\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63" + b"\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20" + b"\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00" + b"\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32" + b"\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35" b"\x00\x2e\x00\x31\x00\x00\x00\x00\x00", ), ] diff --git a/monkey/infection_monkey/network/tcp_scanner.py b/monkey/infection_monkey/network/tcp_scanner.py index 0547d315a..1260a590d 100644 --- a/monkey/infection_monkey/network/tcp_scanner.py +++ b/monkey/infection_monkey/network/tcp_scanner.py @@ -22,7 +22,8 @@ class TcpScanner(HostScanner, HostFinger): def get_host_fingerprint(self, host, only_one_port=False): """ - Scans a target host to see if it's alive using the tcp_target_ports specified in the configuration. + Scans a target host to see if it's alive using the tcp_target_ports specified in the + configuration. :param host: VictimHost structure :param only_one_port: Currently unused. :return: T/F if there is at least one open port. diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py index 63f8604f8..2ccfeb35b 100644 --- a/monkey/infection_monkey/network/tools.py +++ b/monkey/infection_monkey/network/tools.py @@ -129,7 +129,8 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): possible_ports.append((port, sock)) continue if err == 10035: # WSAEWOULDBLOCK is valid, see - # https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 + # https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668%28v=vs.85%29 + # .aspx?f=255&MSPPError=-2147217396 possible_ports.append((port, sock)) continue if err == 115: # EINPROGRESS 115 /* Operation now in progress */ @@ -164,7 +165,8 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): readable_sockets, _, _ = select.select( [s[1] for s in connected_ports_sockets], [], [], 0 ) - # read first BANNER_READ bytes. We ignore errors because service might not send a decodable byte string. + # read first BANNER_READ bytes. We ignore errors because service might not send a + # decodable byte string. banners = [ sock.recv(BANNER_READ).decode(errors="ignore") if sock in readable_sockets @@ -209,7 +211,8 @@ def _get_traceroute_bin_path(): Its been built using the buildroot utility with the following settings: * Statically link to musl and all other required libs * Optimize for size - This is done because not all linux distros come with traceroute out-of-the-box, and to ensure it behaves as expected + This is done because not all linux distros come with traceroute out-of-the-box, and to ensure + it behaves as expected :return: Path to traceroute executable """ @@ -223,7 +226,8 @@ def _parse_traceroute(output, regex, ttl): :param regex: Regex for finding an IP address :param ttl: Max TTL. Must be the same as the TTL used as param for traceroute. :return: List of ips which are the hops on the way to the traceroute destination. - If a hop's IP wasn't found by traceroute, instead of an IP, the array will contain None + If a hop's IP wasn't found by traceroute, instead of an IP, the array will + contain None """ ip_lines = output.split("\n") trace_list = [] diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 97f8ede43..ecbebd4d0 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -27,7 +27,8 @@ logger = logging.getLogger(__name__) class CommunicateAsNewUser(PBA): """ - This PBA creates a new user, and then creates HTTPS requests as that user. This is used for a Zero Trust test of the + This PBA creates a new user, and then creates HTTPS requests as that user. This is used for a + Zero Trust test of the People pillar. See the relevant telemetry processing to see what findings are created. """ @@ -58,7 +59,8 @@ class CommunicateAsNewUser(PBA): def get_commandline_for_http_request(url, is_windows=is_windows_os()): if is_windows: format_string = ( - 'powershell.exe -command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ' + 'powershell.exe -command "[Net.ServicePointManager]::SecurityProtocol = [' + "Net.SecurityProtocolType]::Tls12; " 'Invoke-WebRequest {url} -UseBasicParsing"' ) else: diff --git a/monkey/infection_monkey/post_breach/actions/use_signed_scripts.py b/monkey/infection_monkey/post_breach/actions/use_signed_scripts.py index 555de4667..782f85d13 100644 --- a/monkey/infection_monkey/post_breach/actions/use_signed_scripts.py +++ b/monkey/infection_monkey/post_breach/actions/use_signed_scripts.py @@ -28,7 +28,8 @@ class SignedScriptProxyExecution(PBA): super().run() except Exception as e: LOG.warning( - f"An exception occurred on running PBA {POST_BREACH_SIGNED_SCRIPT_PROXY_EXEC}: {str(e)}" + f"An exception occurred on running PBA " + f"{POST_BREACH_SIGNED_SCRIPT_PROXY_EXEC}: {str(e)}" ) finally: cleanup_changes(original_comspec) diff --git a/monkey/infection_monkey/post_breach/job_scheduling/windows_job_scheduling.py b/monkey/infection_monkey/post_breach/job_scheduling/windows_job_scheduling.py index 4c4927419..9f44768d2 100644 --- a/monkey/infection_monkey/post_breach/job_scheduling/windows_job_scheduling.py +++ b/monkey/infection_monkey/post_breach/job_scheduling/windows_job_scheduling.py @@ -1,7 +1,9 @@ SCHEDULED_TASK_NAME = "monkey-spawn-cmd" SCHEDULED_TASK_COMMAND = r"C:\windows\system32\cmd.exe" -# Commands from: https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1053.005/T1053.005.md + +# Commands from: https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1053.005 +# /T1053.005.md def get_windows_commands_to_schedule_jobs(): diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index e9bf61935..a87cfec48 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -16,7 +16,8 @@ __author__ = "VakarisZ" class PBA(Plugin): """ - Post breach action object. Can be extended to support more than command execution on target machine. + Post breach action object. Can be extended to support more than command execution on target + machine. """ @staticmethod diff --git a/monkey/infection_monkey/post_breach/setuid_setgid/linux_setuid_setgid.py b/monkey/infection_monkey/post_breach/setuid_setgid/linux_setuid_setgid.py index 5a4a7f1dc..15809f481 100644 --- a/monkey/infection_monkey/post_breach/setuid_setgid/linux_setuid_setgid.py +++ b/monkey/infection_monkey/post_breach/setuid_setgid/linux_setuid_setgid.py @@ -1,11 +1,14 @@ TEMP_FILE = "$HOME/monkey-temp-file" -# Commands from https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1548.001/T1548.001.md + +# Commands from https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1548.001 +# /T1548.001.md def get_linux_commands_to_setuid_setgid(): return [ - f"touch {TEMP_FILE} && chown root {TEMP_FILE} && chmod u+s {TEMP_FILE} && chmod g+s {TEMP_FILE} &&", + f"touch {TEMP_FILE} && chown root {TEMP_FILE} && chmod u+s {TEMP_FILE} && chmod g+s " + f"{TEMP_FILE} &&", 'echo "Successfully changed setuid/setgid bits" &&', f"rm {TEMP_FILE}", ] diff --git a/monkey/infection_monkey/post_breach/shell_startup_files/shell_startup_files_modification.py b/monkey/infection_monkey/post_breach/shell_startup_files/shell_startup_files_modification.py index 0be9ec369..604d7c198 100644 --- a/monkey/infection_monkey/post_breach/shell_startup_files/shell_startup_files_modification.py +++ b/monkey/infection_monkey/post_breach/shell_startup_files/shell_startup_files_modification.py @@ -1,7 +1,7 @@ -from infection_monkey.post_breach.shell_startup_files.linux.shell_startup_files_modification import ( +from infection_monkey.post_breach.shell_startup_files.linux.shell_startup_files_modification import ( # noqa: E501 get_linux_commands_to_modify_shell_startup_files, ) -from infection_monkey.post_breach.shell_startup_files.windows.shell_startup_files_modification import ( +from infection_monkey.post_breach.shell_startup_files.windows.shell_startup_files_modification import ( # noqa: E501 get_windows_commands_to_modify_shell_startup_files, ) diff --git a/monkey/infection_monkey/post_breach/shell_startup_files/windows/shell_startup_files_modification.py b/monkey/infection_monkey/post_breach/shell_startup_files/windows/shell_startup_files_modification.py index 517692276..e65893095 100644 --- a/monkey/infection_monkey/post_breach/shell_startup_files/windows/shell_startup_files_modification.py +++ b/monkey/infection_monkey/post_breach/shell_startup_files/windows/shell_startup_files_modification.py @@ -26,6 +26,7 @@ def get_windows_commands_to_modify_shell_startup_files(): return [ "powershell.exe", - "infection_monkey/post_breach/shell_startup_files/windows/modify_powershell_startup_file.ps1", + "infection_monkey/post_breach/shell_startup_files/windows" + "/modify_powershell_startup_file.ps1", "-startup_file_path {0}", ], STARTUP_FILES_PER_USER diff --git a/monkey/infection_monkey/post_breach/tests/actions/test_users_custom_pba.py b/monkey/infection_monkey/post_breach/tests/actions/test_users_custom_pba.py index 2956c140f..e7da336eb 100644 --- a/monkey/infection_monkey/post_breach/tests/actions/test_users_custom_pba.py +++ b/monkey/infection_monkey/post_breach/tests/actions/test_users_custom_pba.py @@ -75,7 +75,6 @@ def test_command_windows_custom_file_and_cmd( @pytest.fixture def mock_UsersPBA_linux_custom_file(set_os_linux, fake_monkey_dir_path, monkeypatch): - monkeypatch.setattr("infection_monkey.config.WormConfiguration.custom_PBA_linux_cmd", None) monkeypatch.setattr( "infection_monkey.config.WormConfiguration.PBA_linux_filename", @@ -91,7 +90,6 @@ def test_command_linux_custom_file(mock_UsersPBA_linux_custom_file): @pytest.fixture def mock_UsersPBA_windows_custom_file(set_os_windows, fake_monkey_dir_path, monkeypatch): - monkeypatch.setattr("infection_monkey.config.WormConfiguration.custom_PBA_windows_cmd", None) monkeypatch.setattr( "infection_monkey.config.WormConfiguration.PBA_windows_filename", @@ -107,7 +105,6 @@ def test_command_windows_custom_file(mock_UsersPBA_windows_custom_file): @pytest.fixture def mock_UsersPBA_linux_custom_cmd(set_os_linux, fake_monkey_dir_path, monkeypatch): - monkeypatch.setattr( "infection_monkey.config.WormConfiguration.custom_PBA_linux_cmd", CUSTOM_LINUX_CMD, @@ -123,7 +120,6 @@ def test_command_linux_custom_cmd(mock_UsersPBA_linux_custom_cmd): @pytest.fixture def mock_UsersPBA_windows_custom_cmd(set_os_windows, fake_monkey_dir_path, monkeypatch): - monkeypatch.setattr( "infection_monkey.config.WormConfiguration.custom_PBA_windows_cmd", CUSTOM_WINDOWS_CMD, diff --git a/monkey/infection_monkey/post_breach/timestomping/linux/timestomping.py b/monkey/infection_monkey/post_breach/timestomping/linux/timestomping.py index 4860e2d3e..07841d280 100644 --- a/monkey/infection_monkey/post_breach/timestomping/linux/timestomping.py +++ b/monkey/infection_monkey/post_breach/timestomping/linux/timestomping.py @@ -11,4 +11,5 @@ def get_linux_timestomping_commands(): ] -# Commands' source: https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1070.006/T1070.006.md +# Commands' source: https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1070.006 +# /T1070.006.md diff --git a/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py b/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py index 952ae46c6..dbea6aaea 100644 --- a/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py +++ b/monkey/infection_monkey/post_breach/timestomping/windows/timestomping.py @@ -5,4 +5,5 @@ def get_windows_timestomping_commands(): return "powershell.exe infection_monkey/post_breach/timestomping/windows/timestomping.ps1" -# Commands' source: https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1070.006/T1070.006.md +# Commands' source: https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1070.006 +# /T1070.006.md diff --git a/monkey/infection_monkey/post_breach/trap_command/linux_trap_command.py b/monkey/infection_monkey/post_breach/trap_command/linux_trap_command.py index 0b9c74b04..75d545140 100644 --- a/monkey/infection_monkey/post_breach/trap_command/linux_trap_command.py +++ b/monkey/infection_monkey/post_breach/trap_command/linux_trap_command.py @@ -1,5 +1,6 @@ def get_linux_trap_commands(): return [ - "trap 'echo \"Successfully used trap command\"' INT && kill -2 $$ ;", # trap and send SIGINT signal + # trap and send SIGINT signal + "trap 'echo \"Successfully used trap command\"' INT && kill -2 $$ ;", "trap - INT", # untrap SIGINT ] diff --git a/monkey/infection_monkey/pyinstaller_utils.py b/monkey/infection_monkey/pyinstaller_utils.py index 2dd8325ce..f13f6f71b 100644 --- a/monkey/infection_monkey/pyinstaller_utils.py +++ b/monkey/infection_monkey/pyinstaller_utils.py @@ -6,7 +6,8 @@ __author__ = "itay.mizeretz" def get_binaries_dir_path(): """ - Gets the path to the binaries dir (files packaged in pyinstaller if it was used, infection_monkey dir otherwise) + Gets the path to the binaries dir (files packaged in pyinstaller if it was used, + infection_monkey dir otherwise) :return: Binaries dir path """ if getattr(sys, "frozen", False): diff --git a/monkey/infection_monkey/system_info/SSH_info_collector.py b/monkey/infection_monkey/system_info/SSH_info_collector.py index 556b5472c..d7b204fed 100644 --- a/monkey/infection_monkey/system_info/SSH_info_collector.py +++ b/monkey/infection_monkey/system_info/SSH_info_collector.py @@ -72,7 +72,8 @@ class SSHCollector(object): try: with open(public) as f: info["public_key"] = f.read() - # By default private key has the same name as public, only without .pub + # By default private key has the same name as public, + # only without .pub private = os.path.splitext(public)[0] if os.path.exists(private): try: diff --git a/monkey/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py index 5fc91f371..765f298d2 100644 --- a/monkey/infection_monkey/system_info/__init__.py +++ b/monkey/infection_monkey/system_info/__init__.py @@ -29,7 +29,8 @@ class OperatingSystem(IntEnum): class SystemInfoCollector(object): """ - A class that checks the current operating system and calls system information collecting modules accordingly + A class that checks the current operating system and calls system information collecting + modules accordingly """ def __init__(self): @@ -113,5 +114,6 @@ class InfoCollector(object): 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. + # 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) diff --git a/monkey/infection_monkey/system_info/azure_cred_collector.py b/monkey/infection_monkey/system_info/azure_cred_collector.py index 68cd05a4e..ca5d8064e 100644 --- a/monkey/infection_monkey/system_info/azure_cred_collector.py +++ b/monkey/infection_monkey/system_info/azure_cred_collector.py @@ -97,7 +97,8 @@ class AzureCollector(object): # 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', + '[System.Reflection.Assembly]::LoadWithPartialName("System.Security") | ' + "Out-Null", '$base64 = "%s"' % protected_data, "$content = [Convert]::FromBase64String($base64)", "$env = New-Object Security.Cryptography.Pkcs.EnvelopedCms", diff --git a/monkey/infection_monkey/system_info/collectors/process_list_collector.py b/monkey/infection_monkey/system_info/collectors/process_list_collector.py index a95ac385b..12cdf8aeb 100644 --- a/monkey/infection_monkey/system_info/collectors/process_list_collector.py +++ b/monkey/infection_monkey/system_info/collectors/process_list_collector.py @@ -37,7 +37,8 @@ class ProcessListCollector(SystemInfoCollector): "full_image_path": process.exe(), } except (psutil.AccessDenied, WindowsError): - # we may be running as non root and some processes are impossible to acquire in Windows/Linux. + # we may be running as non root and some processes are impossible to acquire in + # Windows/Linux. # In this case we'll just add what we know. processes[process.pid] = { "name": "null", diff --git a/monkey/infection_monkey/system_info/netstat_collector.py b/monkey/infection_monkey/system_info/netstat_collector.py index d35b4c1fb..b0acc6b65 100644 --- a/monkey/infection_monkey/system_info/netstat_collector.py +++ b/monkey/infection_monkey/system_info/netstat_collector.py @@ -1,4 +1,5 @@ -# Inspired by Giampaolo Rodola's psutil example from https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py +# Inspired by Giampaolo Rodola's psutil example from +# https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py import logging import socket diff --git a/monkey/infection_monkey/system_info/system_info_collector.py b/monkey/infection_monkey/system_info/system_info_collector.py index ac269f5b0..fe160de16 100644 --- a/monkey/infection_monkey/system_info/system_info_collector.py +++ b/monkey/infection_monkey/system_info/system_info_collector.py @@ -7,9 +7,12 @@ from infection_monkey.utils.plugins.plugin import Plugin class SystemInfoCollector(Plugin, metaclass=ABCMeta): """ - ABC for system info collection. See system_info_collector_handler for more info. Basically, to implement a new system info - collector, inherit from this class in an implementation in the infection_monkey.system_info.collectors class, and override - the 'collect' method. Don't forget to parse your results in the Monkey Island and to add the collector to the configuration + ABC for system info collection. See system_info_collector_handler for more info. Basically, + to implement a new system info + collector, inherit from this class in an implementation in the + infection_monkey.system_info.collectors class, and override + the 'collect' method. Don't forget to parse your results in the Monkey Island and to add the + collector to the configuration as well - see monkey_island.cc.services.processing.system_info_collectors for examples. See the Wiki page "How to add a new System Info Collector to the Monkey?" for a detailed guide. diff --git a/monkey/infection_monkey/system_info/system_info_collectors_handler.py b/monkey/infection_monkey/system_info/system_info_collectors_handler.py index 9c883084c..c54286931 100644 --- a/monkey/infection_monkey/system_info/system_info_collectors_handler.py +++ b/monkey/infection_monkey/system_info/system_info_collectors_handler.py @@ -24,9 +24,8 @@ class SystemInfoCollectorsHandler(object): # If we failed one collector, no need to stop execution. Log and continue. 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 - ) + "All system info collectors executed. Total {} executed, out of which {} " + "collected successfully.".format(len(self.collectors_list), successful_collections) ) SystemInfoTelem({"collectors": system_info_telemetry}).send() diff --git a/monkey/infection_monkey/system_info/windows_cred_collector/test_pypykatz_handler.py b/monkey/infection_monkey/system_info/windows_cred_collector/test_pypykatz_handler.py index f2d9565b1..4d3259e67 100644 --- a/monkey/infection_monkey/system_info/windows_cred_collector/test_pypykatz_handler.py +++ b/monkey/infection_monkey/system_info/windows_cred_collector/test_pypykatz_handler.py @@ -56,7 +56,8 @@ class TestPypykatzHandler(TestCase): { "credtype": "dpapi", "key_guid": "9123-123ae123de4-121239-3123-421f", - "masterkey": "6e81d0cfd5e9ec083cfbdaf4d25b9cc9cc6b72947f5e80920034d1275d8613532025975e" + "masterkey": "6e81d0cfd5e9ec083cfbdaf4d25b9cc9cc6b7294" + "7f5e80920034d1275d8613532025975e" "f051e891c30e6e9af6db54500fedfed1c968389bf6262c77fbaa68c9", "sha1_masterkey": "bbdabc3cd2f6bcbe3e2cee6ce4ce4cebcef4c6da", "luid": 123086, @@ -64,7 +65,8 @@ class TestPypykatzHandler(TestCase): { "credtype": "dpapi", "key_guid": "9123-123ae123de4-121239-3123-421f", - "masterkey": "6e81d0cfd5e9ec083cfbdaf4d25b9cc9cc6b72947f5e80920034d1275d8613532025975e" + "masterkey": "6e81d0cfd5e9ec083cfbdaf4d25b9cc9cc6b729" + "47f5e80920034d1275d8613532025975e" "f051e891c30e6e9af6db54500fedfed1c968389bf6262c77fbaa68c9", "sha1_masterkey": "bbdabc3cd2f6bcbe3e2cee6ce4ce4cebcef4c6da", "luid": 123086, @@ -72,7 +74,8 @@ class TestPypykatzHandler(TestCase): { "credtype": "dpapi", "key_guid": "9123-123ae123de4-121239-3123-421f", - "masterkey": "6e81d0cfd5e9ec083cfbdaf4d25b9cc9cc6b72947f5e80920034d1275d8613532025975e" + "masterkey": "6e81d0cfd5e9ec083cfbdaf4d25b9cc9cc6b72" + "947f5e80920034d1275d8613532025975e" "f051e891c30e6e9af6db54500fedfed1c968389bf6262c77fbaa68c9", "sha1_masterkey": "bbdabc3cd2f6bcbe3e2cee6ce4ce4cebcef4c6da", "luid": 123086, @@ -80,7 +83,8 @@ class TestPypykatzHandler(TestCase): { "credtype": "dpapi", "key_guid": "9123-123ae123de4-121239-3123-421f", - "masterkey": "6e81d0cfd5e9ec083cfbdaf4d25b9cc9cc6b72947f5e80920034d1275d8613532025975e" + "masterkey": "6e81d0cfd5e9ec083cfbdaf4d25b9cc9cc6b729" + "47f5e80920034d1275d8613532025975e" "f051e891c30e6e9af6db54500fedfed1c968389bf6262c77fbaa68c9", "sha1_masterkey": "bbdabc3cd2f6bcbe3e2cee6ce4ce4cebcef4c6da", "luid": 123086, diff --git a/monkey/infection_monkey/telemetry/attack/t1064_telem.py b/monkey/infection_monkey/telemetry/attack/t1064_telem.py index f8cdf379c..de2333ca8 100644 --- a/monkey/infection_monkey/telemetry/attack/t1064_telem.py +++ b/monkey/infection_monkey/telemetry/attack/t1064_telem.py @@ -3,7 +3,8 @@ from infection_monkey.telemetry.attack.usage_telem import AttackTelem class T1064Telem(AttackTelem): def __init__(self, status, usage): - # TODO: rename parameter "usage" to avoid confusion with parameter "usage" in UsageTelem techniques + # TODO: rename parameter "usage" to avoid confusion with parameter "usage" in UsageTelem + # techniques """ T1064 telemetry. :param status: ScanStatus of technique diff --git a/monkey/infection_monkey/telemetry/attack/t1197_telem.py b/monkey/infection_monkey/telemetry/attack/t1197_telem.py index c5c98a9d0..e9fb7fca6 100644 --- a/monkey/infection_monkey/telemetry/attack/t1197_telem.py +++ b/monkey/infection_monkey/telemetry/attack/t1197_telem.py @@ -5,7 +5,8 @@ __author__ = "itay.mizeretz" class T1197Telem(VictimHostTelem): def __init__(self, status, machine, usage): - # TODO: rename parameter "usage" to avoid confusion with parameter "usage" in UsageTelem techniques + # TODO: rename parameter "usage" to avoid confusion with parameter "usage" in UsageTelem + # techniques """ T1197 telemetry. :param status: ScanStatus of technique diff --git a/monkey/infection_monkey/telemetry/base_telem.py b/monkey/infection_monkey/telemetry/base_telem.py index e179a24df..0fcf4b203 100644 --- a/monkey/infection_monkey/telemetry/base_telem.py +++ b/monkey/infection_monkey/telemetry/base_telem.py @@ -9,6 +9,7 @@ LOGGED_DATA_LENGTH = 300 # How many characters of telemetry data will be logged __author__ = "itay.mizeretz" + # TODO: Rework the interface for telemetry; this class has too many responsibilities # (i.e. too many reasons to change): # diff --git a/monkey/infection_monkey/tunnel.py b/monkey/infection_monkey/tunnel.py index 83e03fec2..3fbfc5bdb 100644 --- a/monkey/infection_monkey/tunnel.py +++ b/monkey/infection_monkey/tunnel.py @@ -173,7 +173,8 @@ class MonkeyTunnel(Thread): LOG.info("Stopping tunnel, waiting for clients: %s" % repr(self._clients)) - # wait till all of the tunnel clients has been disconnected, or no one used the tunnel in QUIT_TIMEOUT seconds + # wait till all of the tunnel clients has been disconnected, or no one used the tunnel in + # QUIT_TIMEOUT seconds while self._clients and (time.time() - get_last_serve_time() < QUIT_TIMEOUT): try: search, address = self._broad_sock.recvfrom(BUFFER_READ) diff --git a/monkey/infection_monkey/utils/auto_new_user.py b/monkey/infection_monkey/utils/auto_new_user.py index 26c1c837c..f3ebda0af 100644 --- a/monkey/infection_monkey/utils/auto_new_user.py +++ b/monkey/infection_monkey/utils/auto_new_user.py @@ -8,8 +8,10 @@ class AutoNewUser(metaclass=abc.ABCMeta): """ RAII object to use for creating and using a new user. Use with `with`. User will be created when the instance is instantiated. - User will be available for use (log on for Windows, for example) at the start of the `with` scope. - User will be removed (deactivated and deleted for Windows, for example) at the end of said `with` scope. + User will be available for use (log on for Windows, for example) at the start of the `with` + scope. + User will be removed (deactivated and deleted for Windows, for example) at the end of said + `with` scope. Example: # Created # Logged on diff --git a/monkey/infection_monkey/utils/auto_new_user_factory.py b/monkey/infection_monkey/utils/auto_new_user_factory.py index 898226d46..22c57c578 100644 --- a/monkey/infection_monkey/utils/auto_new_user_factory.py +++ b/monkey/infection_monkey/utils/auto_new_user_factory.py @@ -5,13 +5,15 @@ from infection_monkey.utils.windows.users import AutoNewWindowsUser def create_auto_new_user(username, password, is_windows=is_windows_os()): """ - Factory method for creating an AutoNewUser. See AutoNewUser's documentation for more information. + Factory method for creating an AutoNewUser. See AutoNewUser's documentation for more + information. Example usage: with create_auto_new_user(username, PASSWORD) as new_user: ... :param username: The username of the new user. :param password: The password of the new user. - :param is_windows: If True, a new Windows user is created. Otherwise, a Linux user is created. Leave blank for + :param is_windows: If True, a new Windows user is created. Otherwise, a Linux user is + created. Leave blank for automatic detection. :return: The new AutoNewUser object - use with a `with` scope. """ diff --git a/monkey/infection_monkey/utils/linux/users.py b/monkey/infection_monkey/utils/linux/users.py index 9144a24ec..fa91fced8 100644 --- a/monkey/infection_monkey/utils/linux/users.py +++ b/monkey/infection_monkey/utils/linux/users.py @@ -14,7 +14,8 @@ def get_linux_commands_to_add_user(username): "-M", # Do not create homedir "--expiredate", # The date on which the user account will be disabled. datetime.datetime.today().strftime("%Y-%m-%d"), - "--inactive", # The number of days after a password expires until the account is permanently disabled. + # The number of days after a password expires until the account is permanently disabled. + "--inactive", "0", # A value of 0 disables the account as soon as the password has expired "-c", # Comment "MONKEY_USER", # Comment diff --git a/monkey/infection_monkey/utils/windows/users.py b/monkey/infection_monkey/utils/windows/users.py index 9e5913673..d27b74547 100644 --- a/monkey/infection_monkey/utils/windows/users.py +++ b/monkey/infection_monkey/utils/windows/users.py @@ -47,12 +47,14 @@ class AutoNewWindowsUser(AutoNewUser): import win32security try: - # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera + # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf + # -winbase-logonusera self.logon_handle = win32security.LogonUser( self.username, ".", # Use current domain. self.password, - win32con.LOGON32_LOGON_INTERACTIVE, # Logon type - interactive (normal user), since we're using a shell. + # Logon type - interactive (normal user), since we're using a shell. + win32con.LOGON32_LOGON_INTERACTIVE, win32con.LOGON32_PROVIDER_DEFAULT, ) # Which logon provider to use - whatever Windows offers. except Exception as err: @@ -83,9 +85,13 @@ class AutoNewWindowsUser(AutoNewUser): "Waiting for process to finish. Timeout: {}ms".format(WAIT_TIMEOUT_IN_MILLISECONDS) ) - # https://social.msdn.microsoft.com/Forums/vstudio/en-US/b6d6a7ae-71e9-4edb-ac8f-408d2a41750d/what-events-on-a-process-handle-signal-satisify-waitforsingleobject?forum=vcgeneral - # Ignoring return code, as we'll use `GetExitCode` to determine the state of the process later. - _ = win32event.WaitForSingleObject( # Waits until the specified object is signaled, or time-out. + # https://social.msdn.microsoft.com/Forums/vstudio/en-US/b6d6a7ae-71e9-4edb-ac8f + # -408d2a41750d/what-events-on-a-process-handle-signal-satisify-waitforsingleobject + # ?forum=vcgeneral + # Ignoring return code, as we'll use `GetExitCode` to determine the state of the + # process later. + _ = win32event.WaitForSingleObject( + # Waits until the specified object is signaled, or time-out. process_handle, # Ping process handle WAIT_TIMEOUT_IN_MILLISECONDS, # Timeout in milliseconds ) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index b06494c21..1d9b7ce79 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -62,11 +62,13 @@ def serve_static_file(static_path): try: return send_from_directory(os.path.join(MONKEY_ISLAND_ABS_PATH, "cc/ui/dist"), static_path) except NotFound: - # Because react uses various urls for same index page, this is probably the user's intention. + # Because react uses various urls for same index page, this is probably the user's + # intention. if static_path == HOME_FILE: flask_restful.abort( Response( - "Page not found. Make sure you ran the npm script and the cwd is monkey\\monkey.", + "Page not found. Make sure you ran the npm script and the cwd is " + "monkey\\monkey.", 500, ) ) @@ -82,11 +84,13 @@ def init_app_config(app, mongo_url): # See https://flask-jwt-extended.readthedocs.io/en/stable/options app.config["JWT_ACCESS_TOKEN_EXPIRES"] = env_singleton.env.get_auth_expiration_time() - # Invalidate the signature of JWTs if the server process restarts. This avoids the edge case of getting a JWT, + # Invalidate the signature of JWTs if the server process restarts. This avoids the edge case + # of getting a JWT, # deciding to reset credentials and then still logging in with the old JWT. app.config["JWT_SECRET_KEY"] = str(uuid.uuid4()) - # By default, Flask sorts keys of JSON objects alphabetically, which messes with the ATT&CK matrix in the + # By default, Flask sorts keys of JSON objects alphabetically, which messes with the ATT&CK + # matrix in the # configuration. See https://flask.palletsprojects.com/en/1.1.x/config/#JSON_SORT_KEYS. app.config["JSON_SORT_KEYS"] = False @@ -101,7 +105,8 @@ def init_app_services(app): database.init() Database.init_db() - # If running on AWS, this will initialize the instance data, which is used "later" in the execution of the island. + # If running on AWS, this will initialize the instance data, which is used "later" in the + # execution of the island. RemoteRunAwsService.init() diff --git a/monkey/monkey_island/cc/environment/aws.py b/monkey/monkey_island/cc/environment/aws.py index 89e7b428d..c11e40436 100644 --- a/monkey/monkey_island/cc/environment/aws.py +++ b/monkey/monkey_island/cc/environment/aws.py @@ -5,7 +5,6 @@ __author__ = "itay.mizeretz" class AwsEnvironment(Environment): - _credentials_required = True def __init__(self, config): diff --git a/monkey/monkey_island/cc/environment/password.py b/monkey/monkey_island/cc/environment/password.py index 88d1f76f0..4a8a6d855 100644 --- a/monkey/monkey_island/cc/environment/password.py +++ b/monkey/monkey_island/cc/environment/password.py @@ -4,7 +4,6 @@ __author__ = "itay.mizeretz" class PasswordEnvironment(Environment): - _credentials_required = True def get_auth_users(self): diff --git a/monkey/monkey_island/cc/environment/standard.py b/monkey/monkey_island/cc/environment/standard.py index 8135e8e3f..35ca84a34 100644 --- a/monkey/monkey_island/cc/environment/standard.py +++ b/monkey/monkey_island/cc/environment/standard.py @@ -5,7 +5,6 @@ __author__ = "itay.mizeretz" class StandardEnvironment(Environment): - _credentials_required = False # SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()' diff --git a/monkey/monkey_island/cc/environment/testing.py b/monkey/monkey_island/cc/environment/testing.py index 2dd34a920..efa323fe8 100644 --- a/monkey/monkey_island/cc/environment/testing.py +++ b/monkey/monkey_island/cc/environment/testing.py @@ -4,7 +4,8 @@ from monkey_island.cc.environment import Environment, EnvironmentConfig class TestingEnvironment(Environment): """ Use this environment for running Unit Tests. - This will cause all mongo connections to happen via `mongomock` instead of using an actual mongodb instance. + This will cause all mongo connections to happen via `mongomock` instead of using an actual + mongodb instance. """ _credentials_required = True diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 75d105f70..ba5ee856c 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -5,7 +5,8 @@ import time from pathlib import Path from threading import Thread -# Add the monkey_island directory to the path, to make sure imports that don't start with "monkey_island." work. +# Add the monkey_island directory to the path, to make sure imports that don't start with +# "monkey_island." work. from gevent.pywsgi import WSGIServer MONKEY_ISLAND_DIR_BASE_PATH = str(Path(__file__).parent.parent) @@ -48,7 +49,6 @@ def main(should_setup_only=False, server_config_filename=DEFAULT_SERVER_CONFIG_P def start_island_server(should_setup_only): - mongo_url = os.environ.get("MONGO_URL", env_singleton.env.get_mongo_url()) wait_for_mongo_db_server(mongo_url) assert_mongo_db_version(mongo_url) diff --git a/monkey/monkey_island/cc/models/__init__.py b/monkey/monkey_island/cc/models/__init__.py index c668be7ae..602d815c4 100644 --- a/monkey/monkey_island/cc/models/__init__.py +++ b/monkey/monkey_island/cc/models/__init__.py @@ -4,7 +4,8 @@ import monkey_island.cc.environment.environment_singleton as env_singleton from .command_control_channel import CommandControlChannel # noqa: F401 -# Order of importing matters here, for registering the embedded and referenced documents before using them. +# Order of importing matters here, for registering the embedded and referenced documents before +# using them. from .config import Config # noqa: F401 from .creds import Creds # noqa: F401 from .monkey import Monkey # noqa: F401 diff --git a/monkey/monkey_island/cc/models/attack/attack_mitigations.py b/monkey/monkey_island/cc/models/attack/attack_mitigations.py index 3df6b839d..271b68461 100644 --- a/monkey/monkey_island/cc/models/attack/attack_mitigations.py +++ b/monkey/monkey_island/cc/models/attack/attack_mitigations.py @@ -8,7 +8,6 @@ from monkey_island.cc.services.attack.test_mitre_api_interface import MitreApiIn class AttackMitigations(Document): - COLLECTION_NAME = "attack_mitigations" technique_id = StringField(required=True, primary_key=True) diff --git a/monkey/monkey_island/cc/models/attack/mitigation.py b/monkey/monkey_island/cc/models/attack/mitigation.py index 3c096b618..626c1800a 100644 --- a/monkey/monkey_island/cc/models/attack/mitigation.py +++ b/monkey/monkey_island/cc/models/attack/mitigation.py @@ -5,7 +5,6 @@ from monkey_island.cc.services.attack.test_mitre_api_interface import MitreApiIn class Mitigation(EmbeddedDocument): - name = StringField(required=True) description = StringField(required=True) url = StringField() diff --git a/monkey/monkey_island/cc/models/edge.py b/monkey/monkey_island/cc/models/edge.py index bb4f8a2c6..88858cfcb 100644 --- a/monkey/monkey_island/cc/models/edge.py +++ b/monkey/monkey_island/cc/models/edge.py @@ -2,7 +2,6 @@ from mongoengine import BooleanField, Document, DynamicField, ListField, ObjectI class Edge(Document): - meta = {"allow_inheritance": True} # SCHEMA diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index 90255099e..3bb3c57c9 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -26,8 +26,10 @@ MAX_MONKEYS_AMOUNT_TO_CACHE = 100 class Monkey(Document): """ This class has 2 main section: - * The schema section defines the DB fields in the document. This is the data of the object. - * The logic section defines complex questions we can ask about a single document which are asked multiple + * The schema section defines the DB fields in the document. This is the data of the + object. + * The logic section defines complex questions we can ask about a single document which + are asked multiple times, somewhat like an API. """ @@ -42,7 +44,8 @@ class Monkey(Document): ip_addresses = ListField(StringField()) keepalive = DateTimeField() modifytime = DateTimeField() - # TODO make "parent" an embedded document, so this can be removed and the schema explained (and validated) verbosely. + # TODO make "parent" an embedded document, so this can be removed and the schema explained ( + # and validated) verbosely. # This is a temporary fix, since mongoengine doesn't allow for lists of strings to be null # (even with required=False of null=True). # See relevant issue: https://github.com/MongoEngine/mongoengine/issues/1904 @@ -146,7 +149,8 @@ class Monkey(Document): return {"ips": self.ip_addresses, "hostname": self.hostname} @ring.lru( - expire=1 # data has TTL of 1 second. This is useful for rapid calls for report generation. + # data has TTL of 1 second. This is useful for rapid calls for report generation. + expire=1 ) @staticmethod def is_monkey(object_id): diff --git a/monkey/monkey_island/cc/models/monkey_ttl.py b/monkey/monkey_island/cc/models/monkey_ttl.py index e3025c250..74d38c639 100644 --- a/monkey/monkey_island/cc/models/monkey_ttl.py +++ b/monkey/monkey_island/cc/models/monkey_ttl.py @@ -7,10 +7,12 @@ class MonkeyTtl(Document): """ This model represents the monkey's TTL, and is referenced by the main Monkey document. See https://docs.mongodb.com/manual/tutorial/expire-data/ and - https://stackoverflow.com/questions/55994379/mongodb-ttl-index-doesnt-delete-expired-documents/56021663#56021663 + https://stackoverflow.com/questions/55994379/mongodb-ttl-index-doesnt-delete-expired + -documents/56021663#56021663 for more information about how TTL indexing works and why this class is set up the way it is. - If you wish to use this class, you can create it using the create_ttl_expire_in(seconds) function. + If you wish to use this class, you can create it using the create_ttl_expire_in(seconds) + function. If you wish to create an instance of this class directly, see the inner implementation of create_ttl_expire_in(seconds) to see how to do so. """ @@ -20,11 +22,13 @@ class MonkeyTtl(Document): """ Initializes a TTL object which will expire in expire_in_seconds seconds from when created. Remember to call .save() on the object after creation. - :param expiry_in_seconds: How long should the TTL be in the DB, in seconds. Please take into consideration + :param expiry_in_seconds: How long should the TTL be in the DB, in seconds. Please take + into consideration that the cleanup thread of mongo might take extra time to delete the TTL from the DB. """ # Using UTC to make the mongodb TTL feature work. See - # https://stackoverflow.com/questions/55994379/mongodb-ttl-index-doesnt-delete-expired-documents. + # https://stackoverflow.com/questions/55994379/mongodb-ttl-index-doesnt-delete-expired + # -documents. return MonkeyTtl(expire_at=datetime.utcnow() + timedelta(seconds=expiry_in_seconds)) meta = {"indexes": [{"name": "TTL_index", "fields": ["expire_at"], "expireAfterSeconds": 0}]} @@ -35,7 +39,8 @@ class MonkeyTtl(Document): def create_monkey_ttl_document(expiry_duration_in_seconds): """ Create a new Monkey TTL document and save it as a document. - :param expiry_duration_in_seconds: How long should the TTL last for. THIS IS A LOWER BOUND - depends on mongodb + :param expiry_duration_in_seconds: How long should the TTL last for. THIS IS A LOWER BOUND - + depends on mongodb performance. :return: The TTL document. To get its ID use `.id`. """ diff --git a/monkey/monkey_island/cc/models/test_monkey.py b/monkey/monkey_island/cc/models/test_monkey.py index d21776f6f..92ad2fb90 100644 --- a/monkey/monkey_island/cc/models/test_monkey.py +++ b/monkey/monkey_island/cc/models/test_monkey.py @@ -26,7 +26,8 @@ class TestMonkey: mia_monkey_ttl.save() mia_monkey = Monkey(guid=str(uuid.uuid4()), dead=False, ttl_ref=mia_monkey_ttl.id) mia_monkey.save() - # Emulate timeout - ttl is manually deleted here, since we're using mongomock and not a real mongo instance. + # Emulate timeout - ttl is manually deleted here, since we're using mongomock and not a + # real mongo instance. sleep(1) mia_monkey_ttl.delete() diff --git a/monkey/monkey_island/cc/models/zero_trust/event.py b/monkey/monkey_island/cc/models/zero_trust/event.py index 727ec9a2a..3ffdb02b9 100644 --- a/monkey/monkey_island/cc/models/zero_trust/event.py +++ b/monkey/monkey_island/cc/models/zero_trust/event.py @@ -7,12 +7,15 @@ import common.common_consts.zero_trust_consts as zero_trust_consts class Event(EmbeddedDocument): """ - This model represents a single event within a Finding (it is an EmbeddedDocument within Finding). It is meant to + This model represents a single event within a Finding (it is an EmbeddedDocument within + Finding). It is meant to hold a detail of the Finding. This class has 2 main section: - * The schema section defines the DB fields in the document. This is the data of the object. - * The logic section defines complex questions we can ask about a single document which are asked multiple + * The schema section defines the DB fields in the document. This is the data of the + object. + * The logic section defines complex questions we can ask about a single document which + are asked multiple times, or complex action we will perform - somewhat like an API. """ diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py index 7ddf643fe..b1508430f 100644 --- a/monkey/monkey_island/cc/models/zero_trust/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -12,20 +12,24 @@ import common.common_consts.zero_trust_consts as zero_trust_consts class Finding(Document): """ - This model represents a Zero-Trust finding: A result of a test the monkey/island might perform to see if a + This model represents a Zero-Trust finding: A result of a test the monkey/island might + perform to see if a specific principle of zero trust is upheld or broken. Findings might have the following statuses: Failed ❌ Meaning that we are sure that something is wrong (example: segmentation issue). Verify ⁉ - Meaning that we need the user to check something himself (example: 2FA logs, AV missing). + Meaning that we need the user to check something himself (example: 2FA logs, + AV missing). Passed ✔ Meaning that we are sure that something is correct (example: Monkey failed exploiting). This class has 2 main section: - * The schema section defines the DB fields in the document. This is the data of the object. - * The logic section defines complex questions we can ask about a single document which are asked multiple + * The schema section defines the DB fields in the document. This is the data of the + object. + * The logic section defines complex questions we can ask about a single document which + are asked multiple times, or complex action we will perform - somewhat like an API. """ diff --git a/monkey/monkey_island/cc/models/zero_trust/monkey_finding_details.py b/monkey/monkey_island/cc/models/zero_trust/monkey_finding_details.py index 62cfda504..3568e0ee1 100644 --- a/monkey/monkey_island/cc/models/zero_trust/monkey_finding_details.py +++ b/monkey/monkey_island/cc/models/zero_trust/monkey_finding_details.py @@ -8,7 +8,6 @@ from monkey_island.cc.models.zero_trust.event import Event class MonkeyFindingDetails(Document): - # SCHEMA events = EmbeddedDocumentListField(document_type=Event, required=False) diff --git a/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding_details.py b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding_details.py index cbc8c5f29..9f2b24d9d 100644 --- a/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding_details.py +++ b/monkey/monkey_island/cc/models/zero_trust/scoutsuite_finding_details.py @@ -4,7 +4,6 @@ from monkey_island.cc.models.zero_trust.scoutsuite_rule import ScoutSuiteRule class ScoutSuiteFindingDetails(Document): - # SCHEMA scoutsuite_rules = EmbeddedDocumentListField(document_type=ScoutSuiteRule, required=False) diff --git a/monkey/monkey_island/cc/resources/T1216_pba_file_download.py b/monkey/monkey_island/cc/resources/T1216_pba_file_download.py index 0ac69df6d..de79d2e72 100644 --- a/monkey/monkey_island/cc/resources/T1216_pba_file_download.py +++ b/monkey/monkey_island/cc/resources/T1216_pba_file_download.py @@ -8,7 +8,8 @@ from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH class T1216PBAFileDownload(flask_restful.Resource): """ - File download endpoint used by monkey to download executable file for T1216 ("Signed Script Proxy Execution" PBA) + File download endpoint used by monkey to download executable file for T1216 ("Signed Script + Proxy Execution" PBA) """ def get(self): diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index 47d68fb1a..29d2d9e89 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -25,7 +25,8 @@ def init_jwt(app): class Authenticate(flask_restful.Resource): """ - Resource for user authentication. The user provides the username and hashed password and we give them a JWT. + Resource for user authentication. The user provides the username and hashed password and we + give them a JWT. See `AuthService.js` file for the frontend counterpart for this code. """ @@ -67,7 +68,8 @@ def jwt_required(fn): try: flask_jwt_extended.verify_jwt_in_request() return fn(*args, **kwargs) - # Catch authentication related errors in the verification or inside the called function. All other exceptions propagate + # Catch authentication related errors in the verification or inside the called function. + # All other exceptions propagate except (JWTExtendedException, PyJWTError) as e: return make_response({"error": f"Authentication error: {str(e)}"}, 401) diff --git a/monkey/monkey_island/cc/resources/bootloader_test.py b/monkey/monkey_island/cc/resources/bootloader_test.py index 83d780aa4..d8fd05451 100644 --- a/monkey/monkey_island/cc/resources/bootloader_test.py +++ b/monkey/monkey_island/cc/resources/bootloader_test.py @@ -43,12 +43,18 @@ class TestBootloader(TestCase): b'{\x00"\x00s\x00y\x00s\x00t\x00e\x00m\x00"\x00:\x00"\x00w\x00i\x00n\x00d\x00o' b'\x00w\x00s\x00"\x00,\x00 \x00"\x00o\x00s\x00_\x00v\x00e\x00r\x00s\x00i\x00o\x00n' b'\x00"\x00:\x00"\x00w\x00i\x00n\x00d\x00o\x00w\x00s\x008\x00_\x00o\x00r\x00_\x00g\x00r' - b'\x00e\x00a\x00t\x00e\x00r\x00"\x00,\x00 \x00"\x00h\x00o\x00s\x00t\x00n\x00a\x00m\x00e\x00"' - b'\x00:\x00"\x00D\x00E\x00S\x00K\x00T\x00O\x00P\x00-\x00P\x00J\x00H\x00U\x003\x006\x00B\x00"' - b'\x00,\x00 \x00"\x00t\x00u\x00n\x00n\x00e\x00l\x00"\x00:\x00f\x00a\x00l\x00s\x00e\x00,\x00 ' - b'\x00"\x00i\x00p\x00s\x00"\x00:\x00 \x00[\x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x005' - b'\x006\x00.\x001\x00"\x00,\x00 \x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x002\x004\x009' - b'\x00.\x001\x00"\x00,\x00 \x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x002\x001\x007\x00.' + b'\x00e\x00a\x00t\x00e\x00r\x00"\x00,\x00 ' + b'\x00"\x00h\x00o\x00s\x00t\x00n\x00a\x00m\x00e\x00"' + b'\x00:\x00"\x00D\x00E\x00S\x00K\x00T\x00O\x00P\x00-\x00P\x00J\x00H\x00U\x003\x006' + b'\x00B\x00"' + b'\x00,\x00 \x00"\x00t\x00u\x00n\x00n\x00e\x00l\x00"\x00:\x00f\x00a\x00l\x00s\x00e' + b"\x00,\x00 " + b'\x00"\x00i\x00p\x00s\x00"\x00:\x00 \x00[' + b'\x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x005' + b'\x006\x00.\x001\x00"\x00,\x00 ' + b'\x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x002\x004\x009' + b'\x00.\x001\x00"\x00,\x00 ' + b'\x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x002\x001\x007\x00.' b'\x001\x00"\x00]\x00}\x00' ) diff --git a/monkey/monkey_island/cc/resources/environment.py b/monkey/monkey_island/cc/resources/environment.py index 03333b029..feb0c138c 100644 --- a/monkey/monkey_island/cc/resources/environment.py +++ b/monkey/monkey_island/cc/resources/environment.py @@ -16,6 +16,7 @@ class Environment(flask_restful.Resource): if env_singleton.env.needs_registration(): env_singleton.set_to_standard() logger.warning( - "No user registered, Island on standard mode - no credentials required to access." + "No user registered, Island on standard mode - no credentials required to " + "access." ) return {} diff --git a/monkey/monkey_island/cc/resources/local_run.py b/monkey/monkey_island/cc/resources/local_run.py index 727357ab3..d9dfc0e39 100644 --- a/monkey/monkey_island/cc/resources/local_run.py +++ b/monkey/monkey_island/cc/resources/local_run.py @@ -17,7 +17,6 @@ from monkey_island.cc.services.utils.network_utils import local_ip_addresses __author__ = "Barak" - logger = logging.getLogger(__name__) @@ -34,7 +33,8 @@ def run_local_monkey(): monkey_path = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "binaries", result["filename"]) target_path = os.path.join(env_singleton.env.get_config().data_dir_abs_path, result["filename"]) - # copy the executable to temp path (don't run the monkey from its current location as it may delete itself) + # copy the executable to temp path (don't run the monkey from its current location as it may + # delete itself) try: copyfile(monkey_path, target_path) os.chmod(target_path, stat.S_IRWXU | stat.S_IRWXG) diff --git a/monkey/monkey_island/cc/resources/monkey_download.py b/monkey/monkey_island/cc/resources/monkey_download.py index 5620425aa..c2b633c8e 100644 --- a/monkey/monkey_island/cc/resources/monkey_download.py +++ b/monkey/monkey_island/cc/resources/monkey_download.py @@ -65,9 +65,8 @@ def get_monkey_executable(host_os, machine): logger.info("Monkey exec found for os: {0} and machine: {1}".format(host_os, machine)) return download logger.warning( - "No monkey executables could be found for the host os or machine or both: host_os: {0}, machine: {1}".format( - host_os, machine - ) + "No monkey executables could be found for the host os or machine or both: host_os: {" + "0}, machine: {1}".format(host_os, machine) ) return None @@ -103,7 +102,8 @@ class MonkeyDownload(flask_restful.Resource): @staticmethod def log_executable_hashes(): """ - Logs all the hashes of the monkey executables for debugging ease (can check what Monkey version you have etc.). + Logs all the hashes of the monkey executables for debugging ease (can check what Monkey + version you have etc.). """ filenames = set([x["filename"] for x in MONKEY_DOWNLOADS]) for filename in filenames: diff --git a/monkey/monkey_island/cc/resources/test/clear_caches.py b/monkey/monkey_island/cc/resources/test/clear_caches.py index 04c6b31d8..b8ebeb056 100644 --- a/monkey/monkey_island/cc/resources/test/clear_caches.py +++ b/monkey/monkey_island/cc/resources/test/clear_caches.py @@ -13,7 +13,8 @@ logger = logging.getLogger(__name__) class ClearCaches(flask_restful.Resource): """ - Used for timing tests - we want to get actual execution time of functions in BlackBox without caching - + Used for timing tests - we want to get actual execution time of functions in BlackBox without + caching - so we use this to clear the caches. :note: DO NOT CALL THIS IN PRODUCTION CODE as this will slow down the user experience. """ diff --git a/monkey/monkey_island/cc/resources/test/utils/telem_store.py b/monkey/monkey_island/cc/resources/test/utils/telem_store.py index 5920c8da3..574712cda 100644 --- a/monkey/monkey_island/cc/resources/test/utils/telem_store.py +++ b/monkey/monkey_island/cc/resources/test/utils/telem_store.py @@ -12,13 +12,11 @@ from monkey_island.cc.services.config import ConfigService TELEM_SAMPLE_DIR = "./telem_sample" MAX_SAME_CATEGORY_TELEMS = 10000 - logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class TestTelemStore: - TELEMS_EXPORTED = False @staticmethod diff --git a/monkey/monkey_island/cc/services/attack/attack_config.py b/monkey/monkey_island/cc/services/attack/attack_config.py index faff5f71b..97eea098d 100644 --- a/monkey/monkey_island/cc/services/attack/attack_config.py +++ b/monkey/monkey_island/cc/services/attack/attack_config.py @@ -63,7 +63,8 @@ class AttackConfig(object): @staticmethod def set_arrays(attack_techniques, monkey_config, monkey_schema): """ - Sets exploiters/scanners/PBAs and other array type fields in monkey's config according to ATT&CK matrix + Sets exploiters/scanners/PBAs and other array type fields in monkey's config according to + ATT&CK matrix :param attack_techniques: ATT&CK techniques dict. Format: {'T1110': True, ...} :param monkey_config: Monkey island's configuration :param monkey_schema: Monkey configuration schema @@ -83,7 +84,8 @@ class AttackConfig(object): @staticmethod def set_booleans(attack_techniques, monkey_config, monkey_schema): """ - Sets boolean type fields, like "should use mimikatz?" in monkey's config according to ATT&CK matrix + Sets boolean type fields, like "should use mimikatz?" in monkey's config according to + ATT&CK matrix :param attack_techniques: ATT&CK techniques dict. Format: {'T1110': True, ...} :param monkey_config: Monkey island's configuration :param monkey_schema: Monkey configuration schema @@ -94,9 +96,11 @@ class AttackConfig(object): @staticmethod def r_set_booleans(path, value, attack_techniques, monkey_config): """ - Recursively walks trough monkey configuration (DFS) to find which boolean fields needs to be set and sets them + Recursively walks trough monkey configuration (DFS) to find which boolean fields needs to + be set and sets them according to ATT&CK matrix. - :param path: Property names that leads to current value. E.g. ['monkey', 'system_info', 'should_use_mimikatz'] + :param path: Property names that leads to current value. E.g. ['monkey', 'system_info', + 'should_use_mimikatz'] :param value: Value of config property :param attack_techniques: ATT&CK techniques dict. Format: {'T1110': True, ...} :param monkey_config: Monkey island's configuration @@ -130,7 +134,8 @@ class AttackConfig(object): def set_bool_conf_val(path, val, monkey_config): """ Changes monkey's configuration by setting one of its boolean fields value - :param path: Path to boolean value in monkey's configuration. ['monkey', 'system_info', 'should_use_mimikatz'] + :param path: Path to boolean value in monkey's configuration. ['monkey', 'system_info', + 'should_use_mimikatz'] :param val: Boolean :param monkey_config: Monkey's configuration """ diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index f19295c5a..af27808a9 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -13,7 +13,8 @@ SCHEMA = { "value": True, "necessary": True, "link": "https://attack.mitre.org/techniques/T1059", - "description": "Adversaries may use command-line interfaces to interact with systems " + "description": "Adversaries may use command-line interfaces to interact with " + "systems " "and execute other software during the course of an operation.", }, "T1129": { @@ -22,8 +23,10 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1129", - "description": "The Windows module loader can be instructed to load DLLs from arbitrary " - "local paths and arbitrary Universal Naming Convention (UNC) network paths.", + "description": "The Windows module loader can be instructed to load DLLs from " + "arbitrary " + "local paths and arbitrary Universal Naming Convention (UNC) " + "network paths.", "depends_on": ["T1078", "T1003"], }, "T1106": { @@ -60,8 +63,10 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1035", - "description": "Adversaries may execute a binary, command, or script via a method " - "that interacts with Windows services, such as the Service Control Manager.", + "description": "Adversaries may execute a binary, command, or script via a " + "method " + "that interacts with Windows services, such as the Service " + "Control Manager.", "depends_on": ["T1210"], }, "T1154": { @@ -70,7 +75,8 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1154", - "description": "Adversaries can use the trap command to register code to be executed " + "description": "Adversaries can use the trap command to register code to be " + "executed " "when the shell encounters specific interrupts.", }, }, @@ -88,7 +94,8 @@ SCHEMA = { "link": "https://attack.mitre.org/techniques/T1156", "description": "Adversaries may abuse shell scripts by " "inserting arbitrary shell commands to gain persistence, which " - "would be executed every time the user logs in or opens a new shell.", + "would be executed every time the user logs in or opens a new " + "shell.", "depends_on": ["T1504"], }, "T1136": { @@ -117,8 +124,10 @@ SCHEMA = { "necessary": False, "link": "https://attack.mitre.org/techniques/T1168/", "description": "Linux supports multiple methods for creating pre-scheduled and " - "periodic background jobs. Job scheduling can be used by adversaries to " - "schedule running malicious code at some specified date and time.", + "periodic background jobs. Job scheduling can be used by " + "adversaries to " + "schedule running malicious code at some specified date and " + "time.", "depends_on": ["T1053"], }, "T1504": { @@ -138,9 +147,12 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1053", - "description": "Windows utilities can be used to schedule programs or scripts to " - "be executed at a date and time. An adversary may use task scheduling to " - "execute programs at system startup or on a scheduled basis for persistence.", + "description": "Windows utilities can be used to schedule programs or scripts " + "to " + "be executed at a date and time. An adversary may use task " + "scheduling to " + "execute programs at system startup or on a scheduled basis for " + "persistence.", "depends_on": ["T1168"], }, "T1166": { @@ -149,7 +161,8 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1166", - "description": "Adversaries can set the setuid or setgid bits to get code running in " + "description": "Adversaries can set the setuid or setgid bits to get code " + "running in " "a different user’s context.", }, }, @@ -193,7 +206,8 @@ SCHEMA = { "value": True, "necessary": True, "link": "https://attack.mitre.org/techniques/T1222", - "description": "Adversaries may modify file permissions/attributes to evade intended DACLs.", + "description": "Adversaries may modify file permissions/attributes to evade " + "intended DACLs.", }, "T1099": { "title": "Timestomping", @@ -201,8 +215,10 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1099", - "description": "Adversaries may modify file time attributes to hide new/changes to existing " - "files to avoid attention from forensic investigators or file analysis tools.", + "description": "Adversaries may modify file time attributes to hide " + "new/changes to existing " + "files to avoid attention from forensic investigators or file " + "analysis tools.", }, "T1216": { "title": "Signed script proxy execution", @@ -210,7 +226,8 @@ SCHEMA = { "value": False, "necessary": False, "link": "https://attack.mitre.org/techniques/T1216", - "description": "Adversaries may use scripts signed with trusted certificates to " + "description": "Adversaries may use scripts signed with " + "trusted certificates to " "proxy execution of malicious files on Windows systems.", }, }, @@ -226,8 +243,10 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1110", - "description": "Adversaries may use brute force techniques to attempt access to accounts " - "when passwords are unknown or when password hashes are obtained.", + "description": "Adversaries may use brute force techniques to attempt access " + "to accounts " + "when passwords are unknown or when password hashes are " + "obtained.", "depends_on": ["T1210", "T1021"], }, "T1003": { @@ -236,10 +255,13 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1003", - "description": "Mapped with T1078 Valid Accounts because both techniques require" + "description": "Mapped with T1078 Valid Accounts because " + "both techniques require" " same credential harvesting modules. " - "Credential dumping is the process of obtaining account login and password " - "information, normally in the form of a hash or a clear text password, " + "Credential dumping is the process of obtaining account login " + "and password " + "information, normally in the form of a hash or a clear text " + "password, " "from the operating system and software.", "depends_on": ["T1078"], }, @@ -249,8 +271,10 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1145", - "description": "Adversaries may gather private keys from compromised systems for use in " - "authenticating to Remote Services like SSH or for use in decrypting " + "description": "Adversaries may gather private keys from compromised systems " + "for use in " + "authenticating to Remote Services like SSH or for use in " + "decrypting " "other collected files such as email.", "depends_on": ["T1110", "T1210"], }, @@ -267,8 +291,10 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1087", - "description": "Adversaries may attempt to get a listing of accounts on a system or " - "within an environment. This information can help adversaries determine which " + "description": "Adversaries may attempt to get a listing of accounts on a " + "system or " + "within an environment. This information can help adversaries " + "determine which " "accounts exist to aid in follow-on behavior.", }, "T1018": { @@ -277,8 +303,10 @@ SCHEMA = { "value": True, "necessary": True, "link": "https://attack.mitre.org/techniques/T1018", - "description": "Adversaries will likely attempt to get a listing of other systems by IP address, " - "hostname, or other logical identifier on a network for lateral movement.", + "description": "Adversaries will likely attempt to get a listing of other " + "systems by IP address, " + "hostname, or other logical identifier on a network for lateral" + " movement.", }, "T1082": { "title": "System information discovery", @@ -288,7 +316,8 @@ SCHEMA = { "link": "https://attack.mitre.org/techniques/T1082", "depends_on": ["T1016", "T1005"], "description": "An adversary may attempt to get detailed information about the " - "operating system and hardware, including version, patches, hotfixes, " + "operating system and hardware, including version, patches, " + "hotfixes, " "service packs, and architecture.", }, "T1016": { @@ -298,8 +327,10 @@ SCHEMA = { "necessary": False, "link": "https://attack.mitre.org/techniques/T1016", "depends_on": ["T1005", "T1082"], - "description": "Adversaries will likely look for details about the network configuration " - "and settings of systems they access or through information discovery" + "description": "Adversaries will likely look for details about the network " + "configuration " + "and settings of systems they access or through information " + "discovery" " of remote systems.", }, }, @@ -315,9 +346,12 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1210", - "description": "Exploitation of a software vulnerability occurs when an adversary " - "takes advantage of a programming error in a program, service, or within the " - "operating system software or kernel itself to execute adversary-controlled code.", + "description": "Exploitation of a software vulnerability occurs when an " + "adversary " + "takes advantage of a programming error in a program, service, " + "or within the " + "operating system software or kernel itself to execute " + "adversary-controlled code.", }, "T1075": { "title": "Pass the hash", @@ -325,7 +359,8 @@ SCHEMA = { "value": True, "necessary": False, "link": "https://attack.mitre.org/techniques/T1075", - "description": "Pass the hash (PtH) is a method of authenticating as a user without " + "description": "Pass the hash (PtH) is a method of authenticating as a user " + "without " "having access to the user's cleartext password.", }, "T1105": { @@ -361,8 +396,10 @@ SCHEMA = { "necessary": False, "link": "https://attack.mitre.org/techniques/T1005", "depends_on": ["T1016", "T1082"], - "description": "Sensitive data can be collected from local system sources, such as the file system " - "or databases of information residing on the system prior to Exfiltration.", + "description": "Sensitive data can be collected from local system sources, " + "such as the file system " + "or databases of information residing on the system prior to " + "Exfiltration.", } }, }, @@ -377,7 +414,8 @@ SCHEMA = { "value": True, "necessary": True, "link": "https://attack.mitre.org/techniques/T1090", - "description": "A connection proxy is used to direct network traffic between systems " + "description": "A connection proxy is used to direct network traffic between " + "systems " "or act as an intermediary for network communications.", }, "T1065": { @@ -387,7 +425,8 @@ SCHEMA = { "necessary": True, "link": "https://attack.mitre.org/techniques/T1065", "description": "Adversaries may conduct C2 communications over a non-standard " - "port to bypass proxies and firewalls that have been improperly configured.", + "port to bypass proxies and firewalls that have been improperly " + "configured.", }, "T1188": { "title": "Multi-hop proxy", @@ -411,7 +450,8 @@ SCHEMA = { "value": True, "necessary": True, "link": "https://attack.mitre.org/techniques/T1041", - "description": "Data exfiltration is performed over the Command and Control channel.", + "description": "Data exfiltration is performed over the Command and Control " + "channel.", } }, }, diff --git a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py index fa0707b41..1a3025233 100644 --- a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py +++ b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py @@ -4,7 +4,6 @@ from stix2 import AttackPattern, CourseOfAction, FileSystemSource, Filter class MitreApiInterface: - ATTACK_DATA_PATH = "monkey_island/cc/services/attack/attack_data/enterprise-attack" @staticmethod diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py index 0bf2e649b..27a4b55db 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -8,7 +8,10 @@ __author__ = "VakarisZ" class T1003(AttackTechnique): tech_id = "T1003" - unscanned_msg = "Monkey tried to obtain credentials from systems in the network but didn't find any or failed." + unscanned_msg = ( + "Monkey tried to obtain credentials from systems in the network but didn't " + "find any or failed." + ) scanned_msg = "" used_msg = "Monkey successfully obtained some credentials from systems on the network." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py index d11a74b31..d13e55325 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1035.py @@ -5,7 +5,10 @@ __author__ = "VakarisZ" class T1035(UsageTechnique): tech_id = "T1035" - unscanned_msg = "Monkey didn't try to interact with Windows services since it didn't run on any Windows machines." + unscanned_msg = ( + "Monkey didn't try to interact with Windows services since it didn't run on " + "any Windows machines." + ) scanned_msg = "Monkey tried to interact with Windows services, but failed." used_msg = "Monkey successfully interacted with Windows services." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py index ab482f0f6..4ed2375a5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1166.py @@ -6,7 +6,10 @@ __author__ = "shreyamalviya" class T1166(PostBreachTechnique): tech_id = "T1166" - unscanned_msg = "Monkey didn't try setting the setuid or setgid bits since it didn't run on any Linux machines." + unscanned_msg = ( + "Monkey didn't try setting the setuid or setgid bits since it didn't run on " + "any Linux machines." + ) scanned_msg = "Monkey tried setting the setuid or setgid bits but failed." used_msg = "Monkey successfully set the setuid or setgid bits." pba_names = [POST_BREACH_SETUID_SETGID] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py index 6ed73765a..62800ae20 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1216.py @@ -9,17 +9,20 @@ class T1216(PostBreachTechnique): unscanned_msg = ( "Monkey didn't attempt to execute an arbitrary program with the help of a " + "pre-existing signed script since it didn't run on any Windows machines. " - + "If successful, this behavior could be abused by adversaries to execute malicious files that could " - + "bypass application control and signature validation on systems." + + "If successful, this behavior could be abused by adversaries to execute malicious " + "files that could " + "bypass application control and signature validation on " + "systems." ) scanned_msg = ( "Monkey attempted to execute an arbitrary program with the help of a " + "pre-existing signed script on Windows but failed. " - + "If successful, this behavior could be abused by adversaries to execute malicious files that could " - + "bypass application control and signature validation on systems." + + "If successful, this behavior could be abused by adversaries to execute malicious " + "files that could " + "bypass application control and signature validation on " + "systems." ) used_msg = ( - "Monkey executed an arbitrary program with the help of a pre-existing signed script on Windows. " + "Monkey executed an arbitrary program with the help of a pre-existing signed script " + "on Windows. " + "This behavior could be abused by adversaries to execute malicious files that could " + "bypass application control and signature validation on systems." ) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py index d348c921b..f35fc970f 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1504.py @@ -6,7 +6,10 @@ __author__ = "shreyamalviya" class T1504(PostBreachTechnique): tech_id = "T1504" - unscanned_msg = "Monkey didn't try modifying powershell startup files since it didn't run on any Windows machines." + unscanned_msg = ( + "Monkey didn't try modifying powershell startup files since it didn't run on " + "any Windows machines." + ) scanned_msg = "Monkey tried modifying powershell startup files but failed." used_msg = "Monkey successfully modified powershell startup files." pba_names = [POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 7cdf9010c..40a421d74 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -9,7 +9,6 @@ from monkey_island.cc.services.attack.attack_config import AttackConfig logger = logging.getLogger(__name__) - disabled_msg = ( "This technique has been disabled. " + "You can enable it from the [configuration page](../../configure)." @@ -122,7 +121,8 @@ class AttackTechnique(object, metaclass=abc.ABCMeta): def get_tech_base_data(cls): """ Gathers basic attack technique data into a dict. - :return: dict E.g. {'message': 'Brute force used', 'status': 2, 'title': 'T1110 Brute force'} + :return: dict E.g. {'message': 'Brute force used', 'status': 2, 'title': 'T1110 Brute + force'} """ data = {} status = cls.technique_status() diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py index 1366f0d3a..5460caf4c 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/pba_technique.py @@ -20,7 +20,8 @@ class PostBreachTechnique(AttackTechnique, metaclass=abc.ABCMeta): @classmethod def get_pba_query(cls, post_breach_action_names): """ - :param post_breach_action_names: Names of post-breach actions with which the technique is associated + :param post_breach_action_names: Names of post-breach actions with which the technique is + associated (example - `["Communicate as new user", "Backdoor user"]` for T1136) :return: Mongo query that parses attack telemetries for a simple report component (gets machines and post-breach action usage). diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index d6fe0a3cb..d6b34e60b 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -49,8 +49,10 @@ class ConfigService: def get_config(is_initial_config=False, should_decrypt=True, is_island=False): """ Gets the entire global config. - :param is_initial_config: If True, the initial config will be returned instead of the current config. - :param should_decrypt: If True, all config values which are set as encrypted will be decrypted. + :param is_initial_config: If True, the initial config will be returned instead of the + current config. + :param should_decrypt: If True, all config values which are set as encrypted will be + decrypted. :param is_island: If True, will include island specific configuration parameters. :return: The entire global config. """ @@ -70,8 +72,10 @@ class ConfigService: def get_config_value(config_key_as_arr, is_initial_config=False, should_decrypt=True): """ Get a specific config value. - :param config_key_as_arr: The config key as an array. e.g. ['basic', 'credentials', 'exploit_password_list']. - :param is_initial_config: If True, returns the value of the initial config instead of the current config. + :param config_key_as_arr: The config key as an array. e.g. ['basic', 'credentials', + 'exploit_password_list']. + :param is_initial_config: If True, returns the value of the initial config instead of the + current config. :param should_decrypt: If True, the value of the config key will be decrypted (if it's in the list of encrypted config values). :return: The value of the requested config key. @@ -184,7 +188,8 @@ class ConfigService: @staticmethod def update_config(config_json, should_encrypt): - # PBA file upload happens on pba_file_upload endpoint and corresponding config options are set there + # PBA file upload happens on pba_file_upload endpoint and corresponding config options + # are set there config_json = ConfigService._filter_none_values(config_json) monkey_island.cc.services.post_breach_files.set_config_PBA_files(config_json) if should_encrypt: @@ -330,7 +335,8 @@ class ConfigService: config_arr = config parent_config_arr = None - # Because the config isn't flat, this for-loop gets the actual config value out of the config + # Because the config isn't flat, this for-loop gets the actual config value out of + # the config for config_key_part in config_arr_as_array: parent_config_arr = config_arr config_arr = config_arr[config_key_part] diff --git a/monkey/monkey_island/cc/services/config_schema/basic.py b/monkey/monkey_island/cc/services/config_schema/basic.py index aaf2e570e..aba80e08a 100644 --- a/monkey/monkey_island/cc/services/config_schema/basic.py +++ b/monkey/monkey_island/cc/services/config_schema/basic.py @@ -40,7 +40,8 @@ BASIC = { "uniqueItems": True, "items": {"type": "string"}, "default": ["Administrator", "root", "user"], - "description": "List of user names that will be used by exploiters that need credentials, like " + "description": "List of user names that will be used by exploiters that need " + "credentials, like " "SSH brute-forcing.", }, "exploit_password_list": { @@ -57,7 +58,8 @@ BASIC = { "111111", "iloveyou", ], - "description": "List of passwords that will be used by exploiters that need credentials, like " + "description": "List of passwords that will be used by exploiters that need " + "credentials, like " "SSH brute-forcing.", }, }, diff --git a/monkey/monkey_island/cc/services/config_schema/basic_network.py b/monkey/monkey_island/cc/services/config_schema/basic_network.py index c515a8cbc..4512a7cc9 100644 --- a/monkey/monkey_island/cc/services/config_schema/basic_network.py +++ b/monkey/monkey_island/cc/services/config_schema/basic_network.py @@ -20,23 +20,28 @@ BASIC_NETWORK = { "default": [], "description": "List of IPs that the Monkey will not scan.", "info": 'The Monkey scans its subnet if "Local network scan" is ticked. ' - 'Additionally the monkey scans machines according to "Scan target list".', + 'Additionally the monkey scans machines according to "Scan ' + 'target list".', }, "local_network_scan": { "title": "Local network scan", "type": "boolean", "default": True, - "description": "Determines whether the Monkey will scan the local subnets of machines it runs on, " - 'in addition to the IPs that are configured manually in the "Scan target list".', + "description": "Determines whether the Monkey will scan the local subnets of " + "machines it runs on, " + "in addition to the IPs that are configured manually in the " + '"Scan target list".', }, "depth": { "title": "Scan depth", "type": "integer", "minimum": 1, "default": 2, - "description": "Amount of hops allowed for the Monkey to spread from the Island server. \n" + "description": "Amount of hops allowed for the Monkey to spread from the " + "Island server. \n" + WARNING_SIGN - + " Note that setting this value too high may result in the Monkey propagating too far, " + + " Note that setting this value too high may result in the " + "Monkey propagating too far, " 'if the "Local network scan" is enabled.', }, "subnet_scan_list": { @@ -45,10 +50,12 @@ BASIC_NETWORK = { "uniqueItems": True, "items": {"type": "string", "format": IP_RANGE}, "default": [], - "description": "List of targets the Monkey will try to scan. Targets can be IPs, subnets or hosts." + "description": "List of targets the Monkey will try to scan. Targets can be " + "IPs, subnets or hosts." " Examples:\n" '\tTarget a specific IP: "192.168.0.1"\n' - '\tTarget a subnet using a network range: "192.168.0.5-192.168.0.20"\n' + "\tTarget a subnet using a network range: " + '"192.168.0.5-192.168.0.20"\n' '\tTarget a subnet using an IP mask: "192.168.0.5/24"\n' '\tTarget a specific host: "printer.example"', }, @@ -64,16 +71,20 @@ BASIC_NETWORK = { "uniqueItems": True, "items": {"type": "string", "format": IP_RANGE}, "default": [], - "description": "Test for network segmentation by providing a list of network segments " + "description": "Test for network segmentation by providing a list of network " + "segments " "that should NOT be accessible to each other.\n\n" "For example, if you configured the following three segments: " '"10.0.0.0/24", "11.0.0.2/32", and "12.2.3.0/24", ' - "a Monkey running on 10.0.0.5 will try to access machines in the following subnets: " - "11.0.0.2/32, 12.2.3.0/24. An alert on successful cross-segment connections " + "a Monkey running on 10.0.0.5 will try to access machines in " + "the following subnets: " + "11.0.0.2/32, 12.2.3.0/24. An alert on successful cross-segment " + "connections " "will be shown in the reports. \n\n" "Network segments can be IPs, subnets or hosts. Examples:\n" '\tDefine a single-IP segment: "192.168.0.1"\n' - '\tDefine a segment using a network range: "192.168.0.5-192.168.0.20"\n' + "\tDefine a segment using a network range: " + '"192.168.0.5-192.168.0.20"\n' '\tDefine a segment using an subnet IP mask: "192.168.0.5/24"\n' '\tDefine a single-host segment: "printer.example"', } diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py index 88186e9ed..c450f8d2a 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py @@ -4,7 +4,8 @@ EXPLOITER_CLASSES = { "title": "Exploit class", "description": "Click on exploiter to get more information about it." + WARNING_SIGN - + " Note that using unsafe exploits may cause crashes of the exploited machine/service.", + + " Note that using unsafe exploits may cause crashes of the exploited " + "machine/service.", "type": "string", "anyOf": [ { @@ -15,7 +16,8 @@ EXPLOITER_CLASSES = { "attack_techniques": ["T1110", "T1075", "T1035"], "info": "Brute forces using credentials provided by user and" " hashes gathered by mimikatz.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/smbexec/", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference" + "/exploiters/smbexec/", }, { "type": "string", @@ -24,8 +26,10 @@ EXPLOITER_CLASSES = { "safe": True, "attack_techniques": ["T1110", "T1106"], "info": "Brute forces WMI (Windows Management Instrumentation) " - "using credentials provided by user and hashes gathered by mimikatz.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/wmiexec/", + "using credentials provided by user and hashes gathered by " + "mimikatz.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference" + "/exploiters/wmiexec/", }, { "type": "string", @@ -35,16 +39,19 @@ EXPLOITER_CLASSES = { "attack_techniques": ["T1110"], "info": "Tries to brute force into MsSQL server and uses insecure " "configuration to execute commands on server.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/mssql/", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference" + "/exploiters/mssql/", }, { "type": "string", "enum": ["Ms08_067_Exploiter"], "title": "MS08-067 Exploiter", "safe": False, - "info": "Unsafe exploiter, that might cause system crash due to the use of buffer overflow. " + "info": "Unsafe exploiter, that might cause system crash due to the use of buffer " + "overflow. " "Uses MS08-067 vulnerability.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/ms08-067/", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/ms08" + "-067/", }, { "type": "string", @@ -52,8 +59,10 @@ EXPLOITER_CLASSES = { "title": "SSH Exploiter", "safe": True, "attack_techniques": ["T1110", "T1145", "T1106"], - "info": "Brute forces using credentials provided by user and SSH keys gathered from systems.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/sshexec/", + "info": "Brute forces using credentials provided by user and SSH keys " + "gathered from systems.", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference" + "/exploiters/sshexec/", }, { "type": "string", @@ -62,7 +71,8 @@ EXPLOITER_CLASSES = { "safe": True, "info": "CVE-2014-6271, based on logic from " "https://github.com/nccgroup/shocker/blob/master/shocker.py .", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/shellshock/", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters" + "/shellshock/", }, { "type": "string", @@ -70,7 +80,8 @@ EXPLOITER_CLASSES = { "title": "SambaCry Exploiter", "safe": True, "info": "Bruteforces and searches for anonymous shares. Uses Impacket.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/sambacry/", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters" + "/sambacry/", }, { "type": "string", @@ -78,7 +89,8 @@ EXPLOITER_CLASSES = { "title": "ElasticGroovy Exploiter", "safe": True, "info": "CVE-2015-1427. Logic is based on Metasploit module.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/elasticgroovy/", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters" + "/elasticgroovy/", }, { "type": "string", @@ -95,7 +107,8 @@ EXPLOITER_CLASSES = { "title": "WebLogic Exploiter", "safe": True, "info": "Exploits CVE-2017-10271 and CVE-2019-2725 vulnerabilities on WebLogic server.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/weblogic/", + "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters" + "/weblogic/", }, { "type": "string", @@ -103,7 +116,8 @@ EXPLOITER_CLASSES = { "title": "Hadoop/Yarn Exploiter", "safe": True, "info": "Remote code execution on HADOOP server with YARN and default settings. " - "Logic based on https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn.", + "Logic based on " + "https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn.", "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/hadoop/", }, { @@ -137,7 +151,8 @@ EXPLOITER_CLASSES = { "password has been restored. If Infection Monkey fails to restore the " "password automatically, you'll have to do it manually. For more " "information, see the documentation.", - "link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/zerologon/", + "link": "https://www.guardicore.com/infectionmonkey" + "/docs/reference/exploiters/zerologon/", }, ], } diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py index 88bf44130..01ebfe70c 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/finger_classes.py @@ -47,7 +47,8 @@ FINGER_CLASSES = { "enum": ["MSSQLFinger"], "title": "MSSQLFinger", "safe": True, - "info": "Checks if Microsoft SQL service is running and tries to gather information about it.", + "info": "Checks if Microsoft SQL service is running and tries to gather " + "information about it.", "attack_techniques": ["T1210"], }, { @@ -55,7 +56,7 @@ FINGER_CLASSES = { "enum": ["ElasticFinger"], "title": "ElasticFinger", "safe": True, - "info": "Checks if ElasticSearch is running and attempts to find it's version.", + "info": "Checks if ElasticSearch is running and attempts to find it's " "version.", "attack_techniques": ["T1210"], }, { @@ -63,7 +64,8 @@ FINGER_CLASSES = { "enum": ["PostgreSQLFinger"], "title": "PostgreSQLFinger", "safe": True, - "info": "Checks if PostgreSQL service is running and if its communication is encrypted.", + "info": "Checks if PostgreSQL service is running and if " + "its communication is encrypted.", "attack_techniques": ["T1210"], }, ], diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py index ea9b18aba..2c1104bcb 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py @@ -1,6 +1,7 @@ POST_BREACH_ACTIONS = { "title": "Post breach actions", - "description": "Runs scripts/commands on infected machines. These actions safely simulate what an adversary" + "description": "Runs scripts/commands on infected machines. These actions safely simulate what " + "an adversary" "might do after breaching a new machine. Used in ATT&CK and Zero trust reports.", "type": "string", "anyOf": [ @@ -9,7 +10,7 @@ POST_BREACH_ACTIONS = { "enum": ["BackdoorUser"], "title": "Back door user", "safe": True, - "info": "Attempts to create a new user on the system and delete it afterwards.", + "info": "Attempts to create a new user on the system and delete it " "afterwards.", "attack_techniques": ["T1136"], }, { @@ -17,7 +18,8 @@ POST_BREACH_ACTIONS = { "enum": ["CommunicateAsNewUser"], "title": "Communicate as new user", "safe": True, - "info": "Attempts to create a new user, create HTTPS requests as that user and delete the user " + "info": "Attempts to create a new user, create HTTPS requests as that " + "user and delete the user " "afterwards.", "attack_techniques": ["T1136"], }, @@ -26,8 +28,10 @@ POST_BREACH_ACTIONS = { "enum": ["ModifyShellStartupFiles"], "title": "Modify shell startup files", "safe": True, - "info": "Attempts to modify shell startup files, like ~/.profile, ~/.bashrc, ~/.bash_profile " - "in linux, and profile.ps1 in windows. Reverts modifications done afterwards.", + "info": "Attempts to modify shell startup files, like ~/.profile, " + "~/.bashrc, ~/.bash_profile " + "in linux, and profile.ps1 in windows. Reverts modifications done" + " afterwards.", "attack_techniques": ["T1156", "T1504"], }, { @@ -43,7 +47,8 @@ POST_BREACH_ACTIONS = { "enum": ["TrapCommand"], "title": "Trap", "safe": True, - "info": "On Linux systems, attempts to trap an interrupt signal in order to execute a command " + "info": "On Linux systems, attempts to trap an interrupt signal in order " + "to execute a command " "upon receiving that signal. Removes the trap afterwards.", "attack_techniques": ["T1154"], }, @@ -52,7 +57,8 @@ POST_BREACH_ACTIONS = { "enum": ["ChangeSetuidSetgid"], "title": "Setuid and Setgid", "safe": True, - "info": "On Linux systems, attempts to set the setuid and setgid bits of a new file. " + "info": "On Linux systems, attempts to set the setuid and setgid bits of " + "a new file. " "Removes the file afterwards.", "attack_techniques": ["T1166"], }, @@ -69,7 +75,8 @@ POST_BREACH_ACTIONS = { "enum": ["Timestomping"], "title": "Timestomping", "safe": True, - "info": "Creates a temporary file and attempts to modify its time attributes. Removes the file afterwards.", + "info": "Creates a temporary file and attempts to modify its time " + "attributes. Removes the file afterwards.", "attack_techniques": ["T1099"], }, { diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py index 487166ec6..9a4a39050 100644 --- a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py +++ b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py @@ -17,7 +17,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { "enum": [ENVIRONMENT_COLLECTOR], "title": "Environment collector", "safe": True, - "info": "Collects information about machine's environment (on premise/GCP/AWS).", + "info": "Collects information about machine's environment (on " "premise/GCP/AWS).", "attack_techniques": ["T1082"], }, { @@ -33,7 +33,8 @@ SYSTEM_INFO_COLLECTOR_CLASSES = { "enum": [AWS_COLLECTOR], "title": "AWS collector", "safe": True, - "info": "If on AWS, collects more information about the AWS instance currently running on.", + "info": "If on AWS, collects more information about the AWS instance " + "currently running on.", "attack_techniques": ["T1082"], }, { diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py index 890e74efa..d03527b89 100644 --- a/monkey/monkey_island/cc/services/config_schema/internal.py +++ b/monkey/monkey_island/cc/services/config_schema/internal.py @@ -12,19 +12,22 @@ INTERNAL = { "title": "Singleton mutex name", "type": "string", "default": "{2384ec59-0df8-4ab9-918c-843740924a28}", - "description": "The name of the mutex used to determine whether the monkey is already running", + "description": "The name of the mutex used to determine whether the monkey is " + "already running", }, "keep_tunnel_open_time": { "title": "Keep tunnel open time", "type": "integer", "default": 60, - "description": "Time to keep tunnel open before going down after last exploit (in seconds)", + "description": "Time to keep tunnel open before going down after last exploit " + "(in seconds)", }, "monkey_dir_name": { "title": "Monkey's directory name", "type": "string", "default": r"monkey_dir", - "description": "Directory name for the directory which will contain all of the monkey files", + "description": "Directory name for the directory which will contain all of the" + " monkey files", }, "started_on_island": { "title": "Started on island", @@ -43,7 +46,8 @@ INTERNAL = { "title": "Max victims to find", "type": "integer", "default": 100, - "description": "Determines the maximum number of machines the monkey is allowed to scan", + "description": "Determines the maximum number of machines the monkey is " + "allowed to scan", }, "victims_max_exploit": { "title": "Max victims to exploit", @@ -52,7 +56,8 @@ INTERNAL = { "description": "Determines the maximum number of machines the monkey" " is allowed to successfully exploit. " + WARNING_SIGN - + " Note that setting this value too high may result in the monkey propagating to " + + " Note that setting this value too high may result in the " + "monkey propagating to " "a high number of machines", }, "internet_services": { @@ -61,7 +66,8 @@ INTERNAL = { "uniqueItems": True, "items": {"type": "string"}, "default": ["monkey.guardicore.com", "www.google.com"], - "description": "List of internet services to try and communicate with to determine internet" + "description": "List of internet services to try and communicate with to " + "determine internet" " connectivity (use either ip or domain)", }, "self_delete_in_cleanup": { @@ -108,7 +114,8 @@ INTERNAL = { "uniqueItems": True, "items": {"type": "string"}, "default": ["192.0.2.0:5000"], - "description": "List of command servers/network interfaces to try to communicate with " + "description": "List of command servers/network interfaces to try to " + "communicate with " "(format is :)", }, "current_server": { @@ -133,7 +140,8 @@ INTERNAL = { "uniqueItems": True, "items": {"type": "integer"}, "default": [80, 8080, 443, 8008, 7001, 9200], - "description": "List of ports the monkey will check if are being used for HTTP", + "description": "List of ports the monkey will check if are being used " + "for HTTP", }, "tcp_target_ports": { "title": "TCP target ports", @@ -154,7 +162,8 @@ INTERNAL = { 7001, 8088, ], - "description": "List of TCP ports the monkey will check whether they're open", + "description": "List of TCP ports the monkey will check whether " + "they're open", }, "tcp_scan_interval": { "title": "TCP scan interval", @@ -166,13 +175,15 @@ INTERNAL = { "title": "TCP scan timeout", "type": "integer", "default": 3000, - "description": "Maximum time (in milliseconds) to wait for TCP response", + "description": "Maximum time (in milliseconds) " + "to wait for TCP response", }, "tcp_scan_get_banner": { "title": "TCP scan - get banner", "type": "boolean", "default": True, - "description": "Determines whether the TCP scan should try to get the banner", + "description": "Determines whether the TCP scan should try to get the " + "banner", }, }, }, @@ -184,7 +195,8 @@ INTERNAL = { "title": "Ping scan timeout", "type": "integer", "default": 1000, - "description": "Maximum time (in milliseconds) to wait for ping response", + "description": "Maximum time (in milliseconds) to wait for ping " + "response", } }, }, @@ -238,48 +250,55 @@ INTERNAL = { "title": "Dropper sets date", "type": "boolean", "default": True, - "description": "Determines whether the dropper should set the monkey's file date to be the same as" + "description": "Determines whether the dropper should set the monkey's file " + "date to be the same as" " another file", }, "dropper_date_reference_path_windows": { "title": "Dropper date reference path (Windows)", "type": "string", "default": "%windir%\\system32\\kernel32.dll", - "description": "Determines which file the dropper should copy the date from if it's configured to do" + "description": "Determines which file the dropper should copy the date from if " + "it's configured to do" " so on Windows (use fullpath)", }, "dropper_date_reference_path_linux": { "title": "Dropper date reference path (Linux)", "type": "string", "default": "/bin/sh", - "description": "Determines which file the dropper should copy the date from if it's configured to do" + "description": "Determines which file the dropper should copy the date from if " + "it's configured to do" " so on Linux (use fullpath)", }, "dropper_target_path_linux": { "title": "Dropper target path on Linux", "type": "string", "default": "/tmp/monkey", - "description": "Determines where should the dropper place the monkey on a Linux machine", + "description": "Determines where should the dropper place the monkey on a " + "Linux machine", }, "dropper_target_path_win_32": { "title": "Dropper target path on Windows (32bit)", "type": "string", "default": "C:\\Windows\\temp\\monkey32.exe", - "description": "Determines where should the dropper place the monkey on a Windows machine " + "description": "Determines where should the dropper place the monkey on a " + "Windows machine " "(32bit)", }, "dropper_target_path_win_64": { "title": "Dropper target path on Windows (64bit)", "type": "string", "default": "C:\\Windows\\temp\\monkey64.exe", - "description": "Determines where should the dropper place the monkey on a Windows machine " + "description": "Determines where should the dropper place the monkey on a " + "Windows machine " "(64 bit)", }, "dropper_try_move_first": { "title": "Try to move first", "type": "boolean", "default": True, - "description": "Determines whether the dropper should try to move itself instead of copying itself" + "description": "Determines whether the dropper should try to move itself " + "instead of copying itself" " to target path", }, }, @@ -316,7 +335,8 @@ INTERNAL = { "title": "Send log to server", "type": "boolean", "default": True, - "description": "Determines whether the monkey sends its log to the Monkey Island server", + "description": "Determines whether the monkey sends its log to the Monkey " + "Island server", }, }, }, @@ -356,7 +376,8 @@ INTERNAL = { "title": "Skip exploit if file exists", "type": "boolean", "default": False, - "description": "Determines whether the monkey should skip the exploit if the monkey's file" + "description": "Determines whether the monkey should skip the exploit " + "if the monkey's file" " is already on the remote machine", } }, @@ -410,7 +431,8 @@ INTERNAL = { "/shares", "/home", ], - "description": "List of full paths to share folder for SambaCry to guess", + "description": "List of full paths to share folder for SambaCry to " + "guess", }, "sambacry_shares_not_to_check": { "title": "SambaCry shares not to check", @@ -418,7 +440,8 @@ INTERNAL = { "uniqueItems": True, "items": {"type": "string"}, "default": ["IPC$", "print$"], - "description": "These shares won't be checked when exploiting with SambaCry", + "description": "These shares won't be checked when exploiting with " + "SambaCry", }, }, }, @@ -431,13 +454,15 @@ INTERNAL = { "title": "SMB download timeout", "type": "integer", "default": 300, - "description": "Timeout (in seconds) for SMB download operation (used in various exploits using SMB)", + "description": "Timeout (in seconds) for SMB download operation (used in " + "various exploits using SMB)", }, "smb_service_name": { "title": "SMB service name", "type": "string", "default": "InfectionMonkey", - "description": "Name of the SMB service that will be set up to download monkey", + "description": "Name of the SMB service that will be set up to download " + "monkey", }, }, }, @@ -450,7 +475,8 @@ INTERNAL = { "title": "Export monkey telemetries", "type": "boolean", "default": False, - "description": "Exports unencrypted telemetries that can be used for tests in development." + "description": "Exports unencrypted telemetries that " + "can be used for tests in development." " Do not turn on!", } }, diff --git a/monkey/monkey_island/cc/services/config_schema/monkey.py b/monkey/monkey_island/cc/services/config_schema/monkey.py index 0d69c5aa4..e745da582 100644 --- a/monkey/monkey_island/cc/services/config_schema/monkey.py +++ b/monkey/monkey_island/cc/services/config_schema/monkey.py @@ -109,7 +109,8 @@ MONKEY = { "type": "integer", "default": 1, "minimum": 1, - "description": "Determines how many iterations of the monkey's full lifecycle should occur " + "description": "Determines how many iterations of the monkey's full lifecycle " + "should occur " "(how many times to do the scan)", }, "timeout_between_iterations": { @@ -117,7 +118,8 @@ MONKEY = { "type": "integer", "default": 100, "minimum": 0, - "description": "Determines for how long (in seconds) should the monkey wait before starting another scan", + "description": "Determines for how long (in seconds) should the monkey wait " + "before starting another scan", }, "retry_failed_explotation": { "title": "Retry failed exploitation", diff --git a/monkey/monkey_island/cc/services/edge/displayed_edge.py b/monkey/monkey_island/cc/services/edge/displayed_edge.py index 67d42a3ab..3e038a088 100644 --- a/monkey/monkey_island/cc/services/edge/displayed_edge.py +++ b/monkey/monkey_island/cc/services/edge/displayed_edge.py @@ -36,8 +36,8 @@ class DisplayedEdgeService: displayed_edge["ip_address"] = edge.ip_address displayed_edge["services"] = services displayed_edge["os"] = os - # we need to deepcopy all mutable edge properties, because weak-reference link is made otherwise, - # which is destroyed after method is exited and causes an error later. + # we need to deepcopy all mutable edge properties, because weak-reference link is made + # otherwise, which is destroyed after method is exited and causes an error later. displayed_edge["exploits"] = deepcopy(edge.exploits) displayed_edge["_label"] = edge.get_label() return displayed_edge diff --git a/monkey/monkey_island/cc/services/edge/test_displayed_edge.py b/monkey/monkey_island/cc/services/edge/test_displayed_edge.py index 2938909c2..4c7ca36a7 100644 --- a/monkey/monkey_island/cc/services/edge/test_displayed_edge.py +++ b/monkey/monkey_island/cc/services/edge/test_displayed_edge.py @@ -44,7 +44,6 @@ EXPLOIT_DATA_MOCK = [ class TestDisplayedEdgeService: def test_get_displayed_edges_by_to(self): - dst_id = ObjectId() src_id = ObjectId() diff --git a/monkey/monkey_island/cc/services/infection_lifecycle.py b/monkey/monkey_island/cc/services/infection_lifecycle.py index 1f4c0e87e..e921adc3e 100644 --- a/monkey/monkey_island/cc/services/infection_lifecycle.py +++ b/monkey/monkey_island/cc/services/infection_lifecycle.py @@ -48,7 +48,8 @@ class InfectionLifecycle: @staticmethod def _on_finished_infection(): - # Checking is_report_being_generated here, because we don't want to wait to generate a report; rather, + # Checking is_report_being_generated here, because we don't want to wait to generate a + # report; rather, # we want to skip and reply. if not is_report_being_generated() and not ReportService.is_latest_report_exists(): safe_generate_reports() diff --git a/monkey/monkey_island/cc/services/island_logs.py b/monkey/monkey_island/cc/services/island_logs.py index 846b2e844..a145d09ad 100644 --- a/monkey/monkey_island/cc/services/island_logs.py +++ b/monkey/monkey_island/cc/services/island_logs.py @@ -13,7 +13,8 @@ class IslandLogService: def get_log_file(): """ This static function is a helper function for the monkey island log download function. - It finds the logger handlers and checks if one of them is a fileHandler of any kind by checking if the handler + It finds the logger handlers and checks if one of them is a fileHandler of any kind by + checking if the handler has the property handler.baseFilename. :return: a dict with the log file content. diff --git a/monkey/monkey_island/cc/services/netmap/net_edge.py b/monkey/monkey_island/cc/services/netmap/net_edge.py index 008fa5b54..1c0b649d0 100644 --- a/monkey/monkey_island/cc/services/netmap/net_edge.py +++ b/monkey/monkey_island/cc/services/netmap/net_edge.py @@ -28,7 +28,8 @@ class NetEdgeService: count = 0 for monkey_id in monkey_ids: count += 1 - # generating fake ID, because front end requires unique ID's for each edge. Collision improbable + # generating fake ID, because front end requires unique ID's for each edge. Collision + # improbable fake_id = ObjectId(hex(count)[2:].zfill(24)) island_id = ObjectId("000000000000000000000000") monkey_label = NodeService.get_label_for_endpoint(monkey_id) @@ -61,7 +62,8 @@ class NetEdgeService: count = 0 for monkey_id in monkey_ids: count += 1 - # generating fake ID, because front end requires unique ID's for each edge. Collision improbable + # generating fake ID, because front end requires unique ID's for each edge. Collision + # improbable fake_id = ObjectId(hex(count)[2:].zfill(24)) src_label = NodeService.get_label_for_endpoint(monkey_id) dst_label = NodeService.get_label_for_endpoint(monkey_island_monkey["_id"]) diff --git a/monkey/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py index 2c1fe731a..78c165503 100644 --- a/monkey/monkey_island/cc/services/node.py +++ b/monkey/monkey_island/cc/services/node.py @@ -160,7 +160,8 @@ class NodeService: "label": label, "group": monkey_group, "os": NodeService.get_monkey_os(monkey), - # The monkey is running IFF the group contains "_running". Therefore it's dead IFF the group does NOT + # The monkey is running IFF the group contains "_running". Therefore it's dead IFF + # the group does NOT # contain "_running". This is a small optimisation, to not call "is_dead" twice. "dead": "_running" not in monkey_group, "domain_name": "", diff --git a/monkey/monkey_island/cc/services/post_breach_files.py b/monkey/monkey_island/cc/services/post_breach_files.py index 4215227ea..660d48487 100644 --- a/monkey/monkey_island/cc/services/post_breach_files.py +++ b/monkey/monkey_island/cc/services/post_breach_files.py @@ -15,7 +15,6 @@ PBA_WINDOWS_FILENAME_PATH = ["monkey", "post_breach", "PBA_windows_filename"] PBA_LINUX_FILENAME_PATH = ["monkey", "post_breach", "PBA_linux_filename"] UPLOADS_DIR_NAME = "userUploads" - ABS_UPLOAD_PATH = Path(MONKEY_ISLAND_ABS_PATH, "cc", UPLOADS_DIR_NAME) diff --git a/monkey/monkey_island/cc/services/remote_run_aws.py b/monkey/monkey_island/cc/services/remote_run_aws.py index e640110e0..553f4c72e 100644 --- a/monkey/monkey_island/cc/services/remote_run_aws.py +++ b/monkey/monkey_island/cc/services/remote_run_aws.py @@ -72,7 +72,8 @@ class RemoteRunAwsService: """ For all given instances, checks whether they're 32 or 64 bit. :param instances: List of instances to check - :return: Dictionary with instance ids as keys, and True/False as values. True if 64bit, False otherwise + :return: Dictionary with instance ids as keys, and True/False as values. True if 64bit, + False otherwise """ return CmdRunner.run_multiple_commands( instances, diff --git a/monkey/monkey_island/cc/services/reporting/aws_exporter.py b/monkey/monkey_island/cc/services/reporting/aws_exporter.py index 1505b63aa..d08cdccd1 100644 --- a/monkey/monkey_island/cc/services/reporting/aws_exporter.py +++ b/monkey/monkey_island/cc/services/reporting/aws_exporter.py @@ -101,7 +101,8 @@ class AWSExporter(Exporter): logger.debug("Client acquired: {0}".format(repr(security_hub_client))) # Assumes the machine has the correct IAM role to do this, @see - # https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances + # https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS + # -EC2-instances import_response = security_hub_client.batch_import_findings(Findings=findings_list) logger.debug("Import findings response: {0}".format(repr(import_response))) @@ -111,9 +112,8 @@ class AWSExporter(Exporter): return False except UnknownServiceError as e: logger.warning( - "AWS exporter called but AWS-CLI security hub service is not installed. Error: {}".format( - e - ) + "AWS exporter called but AWS-CLI security hub service is not installed. " + "Error: {}".format(e) ) return False except Exception as e: @@ -147,7 +147,8 @@ class AWSExporter(Exporter): 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.", + 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, @@ -160,10 +161,12 @@ class AWSExporter(Exporter): 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 " + 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 " + 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, @@ -175,10 +178,13 @@ class AWSExporter(Exporter): 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 " + 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 " + 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"] ), @@ -191,10 +197,13 @@ class AWSExporter(Exporter): 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 " + 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" + 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, @@ -206,11 +215,13 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=1, - title="Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.", + 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 " + 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"] ), @@ -225,10 +236,10 @@ class AWSExporter(Exporter): 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"] - ), + 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, ) @@ -238,8 +249,10 @@ class AWSExporter(Exporter): 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 " + 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} \ @@ -256,7 +269,8 @@ class AWSExporter(Exporter): 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.", + 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"] ), @@ -272,7 +286,8 @@ class AWSExporter(Exporter): 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 " + "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"] ), @@ -285,10 +300,13 @@ class AWSExporter(Exporter): 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 " + 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 " + 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"] ), @@ -301,10 +319,13 @@ class AWSExporter(Exporter): 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 " + 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 " + 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"] ), @@ -317,10 +338,13 @@ class AWSExporter(Exporter): 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 " + 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 " + 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"] ), @@ -334,7 +358,8 @@ class AWSExporter(Exporter): 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.", + 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"] ), @@ -347,10 +372,13 @@ class AWSExporter(Exporter): 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 " + 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: " + 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"] ), @@ -363,9 +391,12 @@ class AWSExporter(Exporter): 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 " + 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"] ), @@ -380,8 +411,10 @@ class AWSExporter(Exporter): 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 " + 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"] ), @@ -395,10 +428,14 @@ class AWSExporter(Exporter): 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 " + 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"] ), @@ -413,8 +450,10 @@ class AWSExporter(Exporter): 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.", + 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/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py index e60886b34..03e5ce8b1 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py @@ -2,16 +2,16 @@ from dataclasses import dataclass from enum import Enum from typing import Type -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.cred_exploit import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.cred_exploit import ( # noqa: E501 CredExploitProcessor, ) -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( # noqa: E501 ExploitProcessor, ) -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.shellshock_exploit import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.shellshock_exploit import ( # noqa: E501 ShellShockExploitProcessor, ) -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.zerologon import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.zerologon import ( # noqa: E501 ZerologonExploitProcessor, ) diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/cred_exploit.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/cred_exploit.py index 842fe9eb2..05c9233fe 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/cred_exploit.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/cred_exploit.py @@ -1,8 +1,8 @@ -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_report_info import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_report_info import ( # noqa: E501 CredentialType, ExploiterReportInfo, ) -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( # noqa: E501 ExploitProcessor, ) diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/exploit.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/exploit.py index 1b29fc773..ad249d58a 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/exploit.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/exploit.py @@ -1,5 +1,5 @@ from monkey_island.cc.services.node import NodeService -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_report_info import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_report_info import ( # noqa: E501 ExploiterReportInfo, ) diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/shellshock_exploit.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/shellshock_exploit.py index cd627eb5c..bd047fbf5 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/shellshock_exploit.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/shellshock_exploit.py @@ -1,4 +1,4 @@ -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( # noqa: E501 ExploiterReportInfo, ExploitProcessor, ) diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py index d9c9d7d49..0b99fc87d 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py @@ -1,4 +1,4 @@ -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( # noqa: E501 ExploiterReportInfo, ExploitProcessor, ) diff --git a/monkey/monkey_island/cc/services/reporting/pth_report.py b/monkey/monkey_island/cc/services/reporting/pth_report.py index 99c5a7467..69b3d9677 100644 --- a/monkey/monkey_island/cc/services/reporting/pth_report.py +++ b/monkey/monkey_island/cc/services/reporting/pth_report.py @@ -19,7 +19,8 @@ class PTHReportService(object): @staticmethod def __dup_passwords_mongoquery(): """ - This function builds and queries the mongoDB for users that are using the same passwords. this is done + This function builds and queries the mongoDB for users that are using the same + passwords. this is done by comparing the NTLM hash found for each user by mimikatz. :return: A list of mongo documents (dicts in python) that look like this: @@ -53,7 +54,8 @@ class PTHReportService(object): @staticmethod def __get_admin_on_machines_format(admin_on_machines, domain_name): """ - This function finds for each admin user, which machines its an admin of, and compile them to a list. + This function finds for each admin user, which machines its an admin of, and compile them + to a list. :param admin_on_machines: A list of "monkey" documents "_id"s :param domain_name: The admins' domain name :return: @@ -65,8 +67,10 @@ class PTHReportService(object): @staticmethod def __strong_users_on_crit_query(): """ - This function build and query the mongoDB for users that mimikatz was able to find cached NTLM hashes and - are administrators on machines with services predefined as important services thus making these machines + This function build and query the mongoDB for users that mimikatz was able to find + cached NTLM hashes and + are administrators on machines with services predefined as important services thus + making these machines critical. :return: A list of said users @@ -140,8 +144,10 @@ class PTHReportService(object): def get_shared_admins_nodes(): # This mongo queries users the best solution to figure out if an array - # object has at least two objects in it, by making sure any value exists in the array index 1. - # Excluding the name Administrator - its spamming the lists and not a surprise the domain Administrator account + # object has at least two objects in it, by making sure any value exists in the array + # index 1. + # Excluding the name Administrator - its spamming the lists and not a surprise the domain + # Administrator account # is shared. admins = mongo.db.groupsandusers.find( { diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 87a99a2ad..ade56e64e 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -22,13 +22,13 @@ from monkey_island.cc.services.configuration.utils import ( get_config_network_segments_as_subnet_groups, ) from monkey_island.cc.services.node import NodeService -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_descriptor_enum import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_descriptor_enum import ( # noqa: E501 ExploiterDescriptorEnum, ) -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.cred_exploit import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.cred_exploit import ( # noqa: E501 CredentialType, ) -from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( # noqa: E501 ExploiterReportInfo, ) from monkey_island.cc.services.reporting.pth_report import PTHReportService @@ -384,10 +384,13 @@ class ReportService: @staticmethod def get_cross_segment_issues_of_single_machine(source_subnet_range, target_subnet_range): """ - Gets list of cross segment issues of a single machine. Meaning a machine has an interface for each of the + Gets list of cross segment issues of a single machine. Meaning a machine has an interface + for each of the subnets. - :param source_subnet_range: The subnet range which shouldn't be able to access target_subnet. - :param target_subnet_range: The subnet range which shouldn't be accessible from source_subnet. + :param source_subnet_range: The subnet range which shouldn't be able to access + target_subnet. + :param target_subnet_range: The subnet range which shouldn't be accessible from + source_subnet. :return: """ cross_segment_issues = [] @@ -426,7 +429,8 @@ class ReportService: def get_cross_segment_issues_per_subnet_pair(scans, source_subnet, target_subnet): """ Gets list of cross segment issues from source_subnet to target_subnet. - :param scans: List of all scan telemetry entries. Must have monkey_guid, ip_addr and services. + :param scans: List of all scan telemetry entries. Must have monkey_guid, + ip_addr and services. This should be a PyMongo cursor object. :param source_subnet: The subnet which shouldn't be able to access target_subnet. :param target_subnet: The subnet which shouldn't be accessible from source_subnet. @@ -468,7 +472,8 @@ class ReportService: def get_cross_segment_issues_per_subnet_group(scans, subnet_group): """ Gets list of cross segment issues within given subnet_group. - :param scans: List of all scan telemetry entries. Must have monkey_guid, ip_addr and services. + :param scans: List of all scan telemetry entries. Must have monkey_guid, + ip_addr and services. This should be a PyMongo cursor object. :param subnet_group: List of subnets which shouldn't be accessible from each other. :return: Cross segment issues regarding the subnets in the group. @@ -708,7 +713,8 @@ class ReportService: @staticmethod def encode_dot_char_before_mongo_insert(report_dict): """ - mongodb doesn't allow for '.' and '$' in a key's name, this function replaces the '.' char with the unicode + mongodb doesn't allow for '.' and '$' in a key's name, this function replaces the '.' + char with the unicode ,,, combo instead. :return: dict with formatted keys with no dots. """ @@ -719,7 +725,8 @@ class ReportService: def is_latest_report_exists(): """ This function checks if a monkey report was already generated and if it's the latest one. - :return: True if report is the latest one, False if there isn't a report or its not the latest. + :return: True if report is the latest one, False if there isn't a report or its not the + latest. """ latest_report_doc = mongo.db.report.find_one({}, {"meta.latest_monkey_modifytime": 1}) diff --git a/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py b/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py index dec13e6d6..38f7ee9cb 100644 --- a/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py +++ b/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py @@ -4,8 +4,10 @@ from gevent.lock import BoundedSemaphore logger = logging.getLogger(__name__) -# These are pseudo-singletons - global Locks. These locks will allow only one thread to generate a report at a time. -# Report generation can be quite slow if there is a lot of data, and the UI queries the Root service often; without +# These are pseudo-singletons - global Locks. These locks will allow only one thread to generate +# a report at a time. +# Report generation can be quite slow if there is a lot of data, and the UI queries the Root +# service often; without # the locks, these requests would accumulate, overload the server, eventually causing it to crash. logger.debug("Initializing report generation locks.") __report_generating_lock = BoundedSemaphore() diff --git a/monkey/monkey_island/cc/services/telemetry/processing/state.py b/monkey/monkey_island/cc/services/telemetry/processing/state.py index 8749cc730..87e7797c2 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/state.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/state.py @@ -23,5 +23,6 @@ def process_state_telemetry(telemetry_json): if telemetry_json["data"]["version"]: logger.info( - f"monkey {telemetry_json['monkey_guid']} has version {telemetry_json['data']['version']}" + f"monkey {telemetry_json['monkey_guid']} has version " + f"{telemetry_json['data']['version']}" ) 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 3313b763d..73a81e332 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -3,7 +3,7 @@ import logging from monkey_island.cc.server_utils.encryptor import get_encryptor from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.node import NodeService -from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import ( +from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import ( # noqa: E501 SystemInfoTelemetryDispatcher, ) from monkey_island.cc.services.wmi_handler import WMIHandler @@ -20,7 +20,8 @@ def process_system_info_telemetry(telemetry_json): dispatcher.dispatch_collector_results_to_relevant_processors, ] - # Calling safe_process_telemetry so if one of the stages fail, we log and move on instead of failing the rest of + # 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) @@ -44,7 +45,8 @@ def process_ssh_info(telemetry_json): ssh_info = telemetry_json["data"]["ssh_info"] encrypt_system_info_ssh_keys(ssh_info) if telemetry_json["data"]["network_info"]["networks"]: - # We use user_name@machine_ip as the name of the ssh key stolen, thats why we need ip from telemetry + # We use user_name@machine_ip as the name of the ssh key stolen, thats why we need ip + # from telemetry add_ip_to_ssh_keys(telemetry_json["data"]["network_info"]["networks"][0], ssh_info) add_system_info_ssh_keys_to_config(ssh_info) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py index 894bdce75..7ce4b6fcf 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py @@ -47,7 +47,8 @@ class SystemInfoTelemetryDispatcher(object): def dispatch_collector_results_to_relevant_processors(self, telemetry_json): """ - If the telemetry has collectors' results, dispatches the results to the relevant processing functions. + If the telemetry has collectors' results, dispatches the results to the relevant + processing functions. :param telemetry_json: Telemetry sent from the Monkey """ if "collectors" in telemetry_json["data"]: diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_environment.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_environment.py index f1e53d5f4..042f5b874 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_environment.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_environment.py @@ -1,7 +1,7 @@ import uuid from monkey_island.cc.models import Monkey -from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import ( +from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import ( # noqa: E501 SystemInfoTelemetryDispatcher, ) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_system_info_telemetry_dispatcher.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_system_info_telemetry_dispatcher.py index 0335c6e65..6829daf4b 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_system_info_telemetry_dispatcher.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_system_info_telemetry_dispatcher.py @@ -3,7 +3,7 @@ import uuid import pytest from monkey_island.cc.models import Monkey -from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import ( +from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import ( # noqa: E501 SystemInfoTelemetryDispatcher, process_aws_telemetry, ) @@ -15,7 +15,6 @@ TEST_SYS_INFO_TO_PROCESSING = { class TestSystemInfoTelemetryDispatcher: def test_dispatch_to_relevant_collector_bad_inputs(self): - dispatcher = SystemInfoTelemetryDispatcher(TEST_SYS_INFO_TO_PROCESSING) # Bad format telem JSONs - throws diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_new_user.py index 74007b5fd..6a3ec30aa 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_new_user.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/communicate_as_new_user.py @@ -5,7 +5,10 @@ from monkey_island.cc.services.zero_trust.monkey_findings.monkey_zt_finding_serv ) COMM_AS_NEW_USER_FAILED_FORMAT = "Monkey on {} couldn't communicate as new user. Details: {}" -COMM_AS_NEW_USER_SUCCEEDED_FORMAT = "New user created by Monkey on {} successfully tried to communicate with the internet. Details: {}" +COMM_AS_NEW_USER_SUCCEEDED_FORMAT = ( + "New user created by Monkey on {} successfully tried to " + "communicate with the internet. Details: {}" +) def check_new_user_communication(current_monkey, success, message): diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/data_endpoints.py index e4accdff7..d2634ae86 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/data_endpoints.py @@ -43,7 +43,8 @@ def check_open_data_endpoints(telemetry_json): events.append( Event.create_event( title="Scan telemetry analysis", - message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( + message="Service {} on {} recognized as an open data endpoint! " + "Service details: {}".format( service_data["display_name"], telemetry_json["data"]["machine"]["ip_addr"], json.dumps(service_data), @@ -56,7 +57,8 @@ def check_open_data_endpoints(telemetry_json): events.append( Event.create_event( title="Scan telemetry analysis", - message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( + message="Service {} on {} recognized as an open data endpoint! " + "Service details: {}".format( service_data["display_name"], telemetry_json["data"]["machine"]["ip_addr"], json.dumps(service_data), @@ -69,7 +71,8 @@ def check_open_data_endpoints(telemetry_json): events.append( Event.create_event( title="Scan telemetry analysis", - message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( + message="Service {} on {} recognized as an open data endpoint! " + "Service details: {}".format( service_data["display_name"], telemetry_json["data"]["machine"]["ip_addr"], json.dumps(service_data), diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/segmentation.py index acc3e6bfa..d26e2bd69 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_checks/segmentation.py @@ -18,7 +18,8 @@ SEGMENTATION_DONE_EVENT_TEXT = ( ) SEGMENTATION_VIOLATION_EVENT_TEXT = ( - "Segmentation violation! Monkey on '{hostname}', with the {source_ip} IP address (in segment {source_seg}) " + "Segmentation violation! Monkey on '{hostname}', with the {source_ip} IP address (in segment " + "{source_seg}) " "managed to communicate cross segment to {target_ip} (in segment {target_seg})." ) @@ -51,7 +52,8 @@ def is_segmentation_violation( :param target_ip: The target with which the current monkey communicated with. :param source_subnet: The segment the monkey belongs to. :param target_subnet: Another segment which the monkey isn't supposed to communicate with. - :return: True if this is a violation of segmentation between source_subnet and target_subnet; Otherwise, False. + :return: True if this is a violation of segmentation between source_subnet and + target_subnet; Otherwise, False. """ if source_subnet == target_subnet: return False @@ -102,7 +104,8 @@ def create_or_add_findings_for_all_pairs(all_subnets, current_monkey): # Get all the other subnets. other_subnets = list(set(all_subnets) - set(this_monkey_subnets)) - # Calculate the cartesian product - (this monkey subnets X other subnets). These pairs are the pairs that the monkey + # Calculate the cartesian product - (this monkey subnets X other subnets). These pairs are + # the pairs that the monkey # should have tested. all_subnets_pairs_for_this_monkey = itertools.product(this_monkey_subnets, other_subnets) diff --git a/monkey/monkey_island/cc/services/tests/reporting/test_report.py b/monkey/monkey_island/cc/services/tests/reporting/test_report.py index 6cdc9befd..65f5d2758 100644 --- a/monkey/monkey_island/cc/services/tests/reporting/test_report.py +++ b/monkey/monkey_island/cc/services/tests/reporting/test_report.py @@ -43,7 +43,6 @@ EXPLOIT_TELEMETRY_TELEM = { }, } - SYSTEM_INFO_TELEMETRY_TELEM = { "_id": TELEM_ID["system_info_creds"], "monkey_guid": MONKEY_GUID, diff --git a/monkey/monkey_island/cc/services/tests/test_config.py b/monkey/monkey_island/cc/services/tests/test_config.py index c43a13be9..c5e30dbd2 100644 --- a/monkey/monkey_island/cc/services/tests/test_config.py +++ b/monkey/monkey_island/cc/services/tests/test_config.py @@ -6,6 +6,7 @@ from monkey_island.cc.services.config import ConfigService IPS = ["0.0.0.0", "9.9.9.9"] PORT = 9999 + # If tests fail because config path is changed, sync with # monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js diff --git a/monkey/monkey_island/cc/services/utils/network_utils.py b/monkey/monkey_island/cc/services/utils/network_utils.py index ba3c76939..997fc815c 100644 --- a/monkey/monkey_island/cc/services/utils/network_utils.py +++ b/monkey/monkey_island/cc/services/utils/network_utils.py @@ -60,10 +60,13 @@ def is_local_ips(ips: List) -> bool: return collections.Counter(ips) == collections.Counter(filtered_local_ips) -# The local IP addresses list should not change often. Therefore, we can cache the result and never call this function -# more than once. This stopgap measure is here since this function is called a lot of times during the report +# The local IP addresses list should not change often. Therefore, we can cache the result and +# never call this function +# more than once. This stopgap measure is here since this function is called a lot of times +# during the report # generation. -# This means that if the interfaces of the Island machine change, the Island process needs to be restarted. +# This means that if the interfaces of the Island machine change, the Island process needs to be +# restarted. @lru(maxsize=1) def local_ip_addresses(): ip_list = [] @@ -73,10 +76,13 @@ def local_ip_addresses(): return ip_list -# The subnets list should not change often. Therefore, we can cache the result and never call this function -# more than once. This stopgap measure is here since this function is called a lot of times during the report +# The subnets list should not change often. Therefore, we can cache the result and never call +# this function +# more than once. This stopgap measure is here since this function is called a lot of times +# during the report # generation. -# This means that if the interfaces or subnets of the Island machine change, the Island process needs to be restarted. +# This means that if the interfaces or subnets of the Island machine change, the Island process +# needs to be restarted. @lru(maxsize=1) def get_subnets(): subnets = [] diff --git a/monkey/monkey_island/cc/services/wmi_handler.py b/monkey/monkey_island/cc/services/wmi_handler.py index fe401ce38..ede0bc167 100644 --- a/monkey/monkey_island/cc/services/wmi_handler.py +++ b/monkey/monkey_island/cc/services/wmi_handler.py @@ -148,7 +148,8 @@ class WMIHandler(object): if not mongo.db.groupsandusers.find_one({"SID": entity["SID"]}): mongo.db.groupsandusers.insert_one(entity) else: - # if entity is domain entity, add the monkey id of current machine to secrets_location. + # if entity is domain entity, add the monkey id of current machine to + # secrets_location. # (found on this machine) if entity.get("NTLM_secret"): mongo.db.groupsandusers.update_one( diff --git a/monkey/monkey_island/cc/services/zero_trust/monkey_findings/monkey_zt_finding_service.py b/monkey/monkey_island/cc/services/zero_trust/monkey_findings/monkey_zt_finding_service.py index 68f09fbe9..6c8063eca 100644 --- a/monkey/monkey_island/cc/services/zero_trust/monkey_findings/monkey_zt_finding_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/monkey_findings/monkey_zt_finding_service.py @@ -12,10 +12,12 @@ class MonkeyZTFindingService: @staticmethod def create_or_add_to_existing(test: str, status: str, events: List[Event]): """ - Create a new finding or add the events to an existing one if it's the same (same meaning same status and same + Create a new finding or add the events to an existing one if it's the same (same meaning + same status and same test). - :raises: Assertion error if this is used when there's more then one finding which fits the query - this is not + :raises: Assertion error if this is used when there's more then one finding which fits + the query - this is not when this function should be used. """ existing_findings = list(MonkeyFinding.objects(test=test, status=status)) diff --git a/monkey/monkey_island/cc/services/zero_trust/monkey_findings/test_monkey_zt_details_service.py b/monkey/monkey_island/cc/services/zero_trust/monkey_findings/test_monkey_zt_details_service.py index 191685779..4440d822e 100644 --- a/monkey/monkey_island/cc/services/zero_trust/monkey_findings/test_monkey_zt_details_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/monkey_findings/test_monkey_zt_details_service.py @@ -7,7 +7,8 @@ from monkey_island.cc.services.zero_trust.monkey_findings.monkey_zt_details_serv def test__remove_redundant_events(monkeypatch): monkeypatch.setattr(monkey_zt_details_service, "MAX_EVENT_FETCH_CNT", 6) - # No events are redundant, 8 events in the database, but we display only 6 (3 latest and 3 oldest) + # No events are redundant, 8 events in the database, but we display only 6 (3 latest and 3 + # oldest) latest_events = ["6", "7", "8"] _do_redundant_event_removal_test(latest_events, 8, ["6", "7", "8"]) diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/cloudformation_rules.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/cloudformation_rules.py index c08c7b614..c8dbffb46 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/cloudformation_rules.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/cloudformation_rules.py @@ -4,6 +4,5 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.rule_name class CloudformationRules(RuleNameEnum): - # Service Security CLOUDFORMATION_STACK_WITH_ROLE = "cloudformation-stack-with-role" diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/ses_rules.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/ses_rules.py index d1894144d..a73e00478 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/ses_rules.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/ses_rules.py @@ -4,7 +4,6 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.rule_name class SESRules(RuleNameEnum): - # Permissive policies SES_IDENTITY_WORLD_SENDRAWEMAIL_POLICY = "ses-identity-world-SendRawEmail-policy" SES_IDENTITY_WORLD_SENDEMAIL_POLICY = "ses-identity-world-SendEmail-policy" diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/sns_rules.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/sns_rules.py index 47e49a0d1..09d410239 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/sns_rules.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/sns_rules.py @@ -4,7 +4,6 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.rule_name class SNSRules(RuleNameEnum): - # Permissive policies SNS_TOPIC_WORLD_SUBSCRIBE_POLICY = "sns-topic-world-Subscribe-policy" SNS_TOPIC_WORLD_SETTOPICATTRIBUTES_POLICY = "sns-topic-world-SetTopicAttributes-policy" diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/sqs_rules.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/sqs_rules.py index 84190ceb3..44e666f96 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/sqs_rules.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/consts/rule_names/sqs_rules.py @@ -4,7 +4,6 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.rule_name class SQSRules(RuleNameEnum): - # Permissive policies SQS_QUEUE_WORLD_SENDMESSAGE_POLICY = "sqs-queue-world-SendMessage-policy" SQS_QUEUE_WORLD_RECEIVEMESSAGE_POLICY = "sqs-queue-world-ReceiveMessage-policy" diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_parser.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_parser.py index 134ed3500..7db9a5988 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_parser.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_parser.py @@ -2,7 +2,7 @@ from enum import Enum from common.utils.code_utils import get_value_from_dict from common.utils.exceptions import RulePathCreatorNotFound -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators_list import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators_list import ( # noqa: E501 RULE_PATH_CREATORS_LIST, ) diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudformation_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudformation_rule_path_creator.py index 40e438eba..55f718608 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudformation_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudformation_rule_path_creator.py @@ -2,12 +2,11 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.cloudform CloudformationRules, ) from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class CloudformationRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.CLOUDFORMATION supported_rules = CloudformationRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudtrail_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudtrail_rule_path_creator.py index 928cd138e..1f764ec8b 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudtrail_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudtrail_rule_path_creator.py @@ -2,12 +2,11 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.cloudtrai CloudTrailRules, ) from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class CloudTrailRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.CLOUDTRAIL supported_rules = CloudTrailRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudwatch_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudwatch_rule_path_creator.py index 4d45c878e..573d129ee 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudwatch_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/cloudwatch_rule_path_creator.py @@ -2,12 +2,11 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.cloudwatc CloudWatchRules, ) from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class CloudWatchRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.CLOUDWATCH supported_rules = CloudWatchRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/config_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/config_rule_path_creator.py index b5607cbe8..45cc2e3d6 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/config_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/config_rule_path_creator.py @@ -2,12 +2,11 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.config_ru ConfigRules, ) from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class ConfigRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.CONFIG supported_rules = ConfigRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/ec2_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/ec2_rule_path_creator.py index 8d951f656..41e42180b 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/ec2_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/ec2_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.ec2_rules import EC2Rules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class EC2RulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.EC2 supported_rules = EC2Rules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/elb_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/elb_rule_path_creator.py index 4af6e351b..65b320292 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/elb_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/elb_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.elb_rules import ELBRules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class ELBRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.ELB supported_rules = ELBRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/elbv2_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/elbv2_rule_path_creator.py index 935a8678e..8a560f401 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/elbv2_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/elbv2_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.elbv2_rules import ELBv2Rules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class ELBv2RulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.ELB_V2 supported_rules = ELBv2Rules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/iam_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/iam_rule_path_creator.py index f355dd8e2..0ab9e686f 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/iam_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/iam_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.iam_rules import IAMRules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class IAMRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.IAM supported_rules = IAMRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/rds_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/rds_rule_path_creator.py index be4b043d7..56252a3f6 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/rds_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/rds_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.rds_rules import RDSRules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class RDSRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.RDS supported_rules = RDSRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/redshift_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/redshift_rule_path_creator.py index dfa954638..90ba44308 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/redshift_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/redshift_rule_path_creator.py @@ -2,12 +2,11 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.redshift_ RedshiftRules, ) from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class RedshiftRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.REDSHIFT supported_rules = RedshiftRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/s3_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/s3_rule_path_creator.py index f06b2554f..aa6f101aa 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/s3_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/s3_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.s3_rules import S3Rules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class S3RulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.S3 supported_rules = S3Rules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/ses_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/ses_rule_path_creator.py index 7ded2918f..4530aa097 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/ses_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/ses_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.ses_rules import SESRules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class SESRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.SES supported_rules = SESRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/sns_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/sns_rule_path_creator.py index 6eda4fcef..bb619f92f 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/sns_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/sns_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.sns_rules import SNSRules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class SNSRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.SNS supported_rules = SNSRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/sqs_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/sqs_rule_path_creator.py index e4979caf5..19229c1d6 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/sqs_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/sqs_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.sqs_rules import SQSRules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class SQSRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.SQS supported_rules = SQSRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/vpc_rule_path_creator.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/vpc_rule_path_creator.py index 9daad607e..7f3cfecde 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/vpc_rule_path_creator.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators/vpc_rule_path_creator.py @@ -1,11 +1,10 @@ from monkey_island.cc.services.zero_trust.scoutsuite.consts.rule_names.vpc_rules import VPCRules from monkey_island.cc.services.zero_trust.scoutsuite.consts.service_consts import SERVICE_TYPES -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.abstract_rule_path_creator import ( # noqa: E501 AbstractRulePathCreator, ) class VPCRulePathCreator(AbstractRulePathCreator): - service_type = SERVICE_TYPES.VPC supported_rules = VPCRules diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators_list.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators_list.py index 8ad561ece..d724ca584 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators_list.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/rule_path_building/rule_path_creators_list.py @@ -1,46 +1,46 @@ -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.cloudformation_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.cloudformation_rule_path_creator import ( # noqa: E501 CloudformationRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.cloudtrail_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.cloudtrail_rule_path_creator import ( # noqa: E501 CloudTrailRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.cloudwatch_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.cloudwatch_rule_path_creator import ( # noqa: E501 CloudWatchRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.config_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.config_rule_path_creator import ( # noqa: E501 ConfigRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.ec2_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.ec2_rule_path_creator import ( # noqa: E501 EC2RulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.elb_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.elb_rule_path_creator import ( # noqa: E501 ELBRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.elbv2_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.elbv2_rule_path_creator import ( # noqa: E501 ELBv2RulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.iam_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.iam_rule_path_creator import ( # noqa: E501 IAMRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.rds_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.rds_rule_path_creator import ( # noqa: E501 RDSRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.redshift_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.redshift_rule_path_creator import ( # noqa: E501 RedshiftRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.s3_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.s3_rule_path_creator import ( # noqa: E501 S3RulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.ses_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.ses_rule_path_creator import ( # noqa: E501 SESRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.sns_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.sns_rule_path_creator import ( # noqa: E501 SNSRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.sqs_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.sqs_rule_path_creator import ( # noqa: E501 SQSRulePathCreator, ) -from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.vpc_rule_path_creator import ( +from monkey_island.cc.services.zero_trust.scoutsuite.data_parsing.rule_path_building.rule_path_creators.vpc_rule_path_creator import ( # noqa: E501 VPCRulePathCreator, ) diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/test_rule_parser.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/test_rule_parser.py index 15a0b4b11..daaabf430 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/test_rule_parser.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/data_parsing/test_rule_parser.py @@ -17,7 +17,8 @@ ALL_PORTS_OPEN = EC2Rules.SECURITY_GROUP_ALL_PORTS_TO_ALL EXPECTED_RESULT = { "description": "Security Group Opens All Ports to All", - "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", + "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id" + ".cidrs.id.CIDR", "level": "danger", "display_path": "ec2.regions.id.vpcs.id.security_groups.id", "items": [ diff --git a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_rule_service.py b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_rule_service.py index 04e085eb1..fa3ca9835 100644 --- a/monkey/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_rule_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/scoutsuite/test_scoutsuite_rule_service.py @@ -22,10 +22,14 @@ example_scoutsuite_data = { ".rules.ingress.protocols.ALL.ports.1-65535.cidrs.0.CIDR", ], "level": "danger", - "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", - "rationale": "It was detected that all ports in the security group are open, and any source IP address" - " could send traffic to these ports, which creates a wider attack surface for resources " - "assigned to it. Open ports should be reduced to the minimum needed to correctly", + "path": "ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id" + ".cidrs.id.CIDR", + "rationale": "It was detected that all ports in the security group are open, " + "and any source IP address" + " could send traffic to these ports, which creates a wider attack surface " + "for resources " + "assigned to it. Open ports should be reduced to the minimum needed to " + "correctly", "references": [], "remediation": None, "service": "EC2", diff --git a/monkey/monkey_island/cc/services/zero_trust/test_common/raw_scoutsute_data.py b/monkey/monkey_island/cc/services/zero_trust/test_common/raw_scoutsute_data.py index 978209671..9905868af 100644 --- a/monkey/monkey_island/cc/services/zero_trust/test_common/raw_scoutsute_data.py +++ b/monkey/monkey_island/cc/services/zero_trust/test_common/raw_scoutsute_data.py @@ -127,7 +127,8 @@ RAW_SCOUTSUITE_DATA = { "checked_items": 179, "flagged_items": 2, "service": "EC2", - "rationale": "It was detected that all ports in the security group are open <...>", + "rationale": "It was detected that all ports in the security group are " + "open <...>", "remediation": None, "compliance": None, "references": None, diff --git a/monkey/monkey_island/cc/services/zero_trust/test_common/scoutsuite_finding_data.py b/monkey/monkey_island/cc/services/zero_trust/test_common/scoutsuite_finding_data.py index 4e428794d..2302b68e9 100644 --- a/monkey/monkey_island/cc/services/zero_trust/test_common/scoutsuite_finding_data.py +++ b/monkey/monkey_island/cc/services/zero_trust/test_common/scoutsuite_finding_data.py @@ -15,16 +15,22 @@ RULES = [ description="Security Group Opens All Ports to All", flagged_items=2, items=[ - "ec2.regions.eu-central-1.vpcs.vpc-0ee259b1a13c50229.security_groups.sg-035779fe5c293fc72" + "ec2.regions.eu-central-1.vpcs.vpc-0ee259b1a13c50229.security_groups.sg" + "-035779fe5c293fc72" ".rules.ingress.protocols.ALL.ports.1-65535.cidrs.2.CIDR", - "ec2.regions.eu-central-1.vpcs.vpc-00015526b6695f9aa.security_groups.sg-019eb67135ec81e65" + "ec2.regions.eu-central-1.vpcs.vpc-00015526b6695f9aa.security_groups.sg" + "-019eb67135ec81e65" ".rules.ingress.protocols.ALL.ports.1-65535.cidrs.0.CIDR", ], level="danger", - path="ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", - rationale="It was detected that all ports in the security group are open, and any source IP address" - " could send traffic to these ports, which creates a wider attack surface for resources " - "assigned to it. Open ports should be reduced to the minimum needed to correctly", + path="ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs" + ".id.CIDR", + rationale="It was detected that all ports in the security group are open, " + "and any source IP address" + " could send traffic to these ports, which creates a wider attack surface " + "for resources " + "assigned to it. Open ports should be reduced to the minimum needed to " + "correctly", references=[], remediation=None, service="EC2", @@ -43,7 +49,8 @@ RULES = [ description="Security Group Opens RDP Port to All", flagged_items=7, items=[ - "ec2.regions.eu-central-1.vpcs.vpc-076500a2138ee09da.security_groups.sg-00bdef5951797199c" + "ec2.regions.eu-central-1.vpcs.vpc-076500a2138ee09da.security_groups.sg" + "-00bdef5951797199c" ".rules.ingress.protocols.TCP.ports.3389.cidrs.0.CIDR", "ec2.regions.eu-central-1.vpcs.vpc-d33026b8.security_groups.sg-007931ba8a364e330" ".rules.ingress.protocols.TCP.ports.3389.cidrs.0.CIDR", @@ -55,14 +62,19 @@ RULES = [ ".rules.ingress.protocols.TCP.ports.3389.cidrs.0.CIDR", "ec2.regions.us-east-1.vpcs.vpc-9e56cae4.security_groups.sg-0dc253aa79062835a" ".rules.ingress.protocols.TCP.ports.3389.cidrs.0.CIDR", - "ec2.regions.us-east-1.vpcs.vpc-002d543353cd4e97d.security_groups.sg-01902f153d4f938da" + "ec2.regions.us-east-1.vpcs.vpc-002d543353cd4e97d.security_groups.sg" + "-01902f153d4f938da" ".rules.ingress.protocols.TCP.ports.3389.cidrs.0.CIDR", ], level="danger", - path="ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs.id.CIDR", - rationale="The security group was found to be exposing a well-known port to all source addresses." - " Well-known ports are commonly probed by automated scanning tools, and could be an indicator " - "of sensitive services exposed to Internet. If such services need to be expos", + path="ec2.regions.id.vpcs.id.security_groups.id.rules.id.protocols.id.ports.id.cidrs" + ".id.CIDR", + rationale="The security group was found to be exposing a well-known port to all " + "source addresses." + " Well-known ports are commonly probed by automated scanning tools, " + "and could be an indicator " + "of sensitive services exposed to Internet. If such services need to be " + "expos", references=[], remediation="Remove the inbound rules that expose open ports", service="EC2", diff --git a/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/principle_service.py b/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/principle_service.py index 671d1da44..3fdc4aee1 100644 --- a/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/principle_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/principle_service.py @@ -56,7 +56,8 @@ class PrincipleService: @staticmethod def __get_lcd_worst_status_for_test(all_findings_for_test): """ - :param all_findings_for_test: All findings of a specific test (get this using Finding.objects(test={A_TEST})) + :param all_findings_for_test: All findings of a specific test (get this using + Finding.objects(test={A_TEST})) :return: the "worst" (i.e. most severe) status out of the given findings. lcd stands for lowest common denominator. """ diff --git a/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/test_pillar_service.py b/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/test_pillar_service.py index 36691e00e..3b6da848f 100644 --- a/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/test_pillar_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/test_pillar_service.py @@ -13,7 +13,7 @@ from common.common_consts.zero_trust_consts import ( WORKLOADS, ) from monkey_island.cc.services.zero_trust.zero_trust_report.pillar_service import PillarService -from monkey_island.cc.services.zero_trust.zero_trust_report.test_common.example_finding_data import ( +from monkey_island.cc.services.zero_trust.zero_trust_report.test_common.example_finding_data import ( # noqa: E501 save_example_findings, ) from monkey_island.cc.test_common.fixtures import FixtureEnum diff --git a/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py index e5e7ecb5a..785d6a36b 100644 --- a/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py +++ b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py @@ -1,4 +1,5 @@ -# Workaround for packaging Monkey Island using PyInstaller. See https://github.com/oasis-open/cti-python-stix2/issues/218 +# Workaround for packaging Monkey Island using PyInstaller. See +# https://github.com/oasis-open/cti-python-stix2/issues/218 import os