From 4f04cf1c18ee5e51a36a736bb002e6ee599baa75 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:07:11 +0000 Subject: [PATCH 01/30] Common: Fix mypy error in encoding.py --- monkey/common/credentials/encoding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/common/credentials/encoding.py b/monkey/common/credentials/encoding.py index e76ff8599..b5b58ec53 100644 --- a/monkey/common/credentials/encoding.py +++ b/monkey/common/credentials/encoding.py @@ -5,7 +5,7 @@ from typing import Optional, Union from pydantic import SecretBytes, SecretStr -def get_plaintext(secret: Union[SecretStr, SecretBytes, None, str]) -> Optional[str]: +def get_plaintext(secret: Union[SecretStr, SecretBytes, None, str]) -> Optional[Union[str, bytes]]: if isinstance(secret, (SecretStr, SecretBytes)): return secret.get_secret_value() else: From 60c1eb9cc1a1b5faeaf0ef06de845780aff3887d Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:09:06 +0000 Subject: [PATCH 02/30] Agent: Fix mypy errors in monkey.py --- monkey/infection_monkey/monkey.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 50ed3a3cb..3cb5e515e 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -142,7 +142,7 @@ class InfectionMonkey: return opts # TODO: By the time we finish 2292, _connect_to_island_api() may not need to return `server` - def _connect_to_island_api(self) -> Tuple[str, IIslandAPIClient]: + def _connect_to_island_api(self) -> Tuple[Optional[str], Optional[IIslandAPIClient]]: logger.debug(f"Trying to wake up with servers: {', '.join(self._opts.servers)}") server_clients = find_available_island_apis( self._opts.servers, HTTPIslandAPIClientFactory(self._agent_event_serializer_registry) @@ -166,7 +166,7 @@ class InfectionMonkey: return server, island_api_client def _select_server( - self, server_clients: Mapping[str, IIslandAPIClient] + self, server_clients: Mapping[str, Optional[IIslandAPIClient]] ) -> Tuple[Optional[str], Optional[IIslandAPIClient]]: for server in self._opts.servers: if server_clients[server]: From be4c5e73126365fb47edc874796f23bbed8415ba Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:42:52 +0000 Subject: [PATCH 03/30] Agent: Fix mypy issues in propagation credentials Updated aggregating_propagation_credentials_repository.py --- .../aggregating_propagation_credentials_repository.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py b/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py index f7c86c30d..46cf1b9fe 100644 --- a/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py +++ b/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Iterable +from typing import Any, Dict, Iterable, Sequence from common.credentials import Credentials, LMHash, NTHash, Password, SSHKeypair, Username from common.credentials.credentials import Identity, Secret @@ -21,7 +21,7 @@ class AggregatingPropagationCredentialsRepository(IPropagationCredentialsReposit """ def __init__(self, control_channel: IControlChannel): - self._stored_credentials = { + self._stored_credentials: Dict = { "exploit_user_list": set(), "exploit_password_list": set(), "exploit_lm_hash_list": set(), @@ -72,7 +72,7 @@ class AggregatingPropagationCredentialsRepository(IPropagationCredentialsReposit return self._stored_credentials - def _set_attribute(self, attribute_to_be_set: str, credentials_values: Iterable[Any]): + def _set_attribute(self, attribute_to_be_set: str, credentials_values: Sequence[Any]): if not credentials_values: return From 0a9d221ad8cf79e5c82c510aed4085aa863547ac Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:47:11 +0000 Subject: [PATCH 04/30] Agent: Fix mypy issues in HostExploiter.py --- monkey/infection_monkey/exploit/HostExploiter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 6c7128677..6291d98d3 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -7,6 +7,7 @@ from typing import Dict, Sequence from common.event_queue import IAgentEventQueue from common.utils.exceptions import FailedExploitationError from infection_monkey.i_puppet import ExploiterResultData +from infection_monkey.model import VictimHost from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger from . import IAgentBinaryRepository @@ -58,7 +59,7 @@ class HostExploiter: # TODO: host should be VictimHost, at the moment it can't because of circular dependency def exploit_host( self, - host, + host: VictimHost, servers: Sequence[str], current_depth: int, telemetry_messenger: ITelemetryMessenger, From 2fab84636e887cd7e19d7638a68c6763ed7ef573 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:47:56 +0000 Subject: [PATCH 05/30] Agent: Fix mypy issues in exploit/tools/helpers.py --- monkey/infection_monkey/exploit/tools/helpers.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py index e268fe4c3..c81da81d9 100644 --- a/monkey/infection_monkey/exploit/tools/helpers.py +++ b/monkey/infection_monkey/exploit/tools/helpers.py @@ -15,12 +15,13 @@ AGENT_BINARY_PATH_WIN64 = PureWindowsPath(r"C:\Windows\temp\monkey64.exe") def get_agent_dst_path(host: VictimHost) -> PurePath: - if host.is_windows(): - path = PureWindowsPath(AGENT_BINARY_PATH_WIN64) - else: - path = PurePosixPath(AGENT_BINARY_PATH_LINUX) + return _add_random_suffix(_get_agent_path(host)) - return _add_random_suffix(path) + +def _get_agent_path(host: VictimHost) -> PurePath: + if host.is_windows(): + return PureWindowsPath(AGENT_BINARY_PATH_WIN64) + return PurePosixPath(AGENT_BINARY_PATH_LINUX) def get_random_file_suffix() -> str: From fc82715262d14b8336f4d8140b742012762d37b6 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:49:04 +0000 Subject: [PATCH 06/30] Agent: Fix mypy issues in vuln_assessment.py --- .../exploit/zerologon_utils/vuln_assessment.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/zerologon_utils/vuln_assessment.py b/monkey/infection_monkey/exploit/zerologon_utils/vuln_assessment.py index 5ba40f7c8..e5fd32df0 100644 --- a/monkey/infection_monkey/exploit/zerologon_utils/vuln_assessment.py +++ b/monkey/infection_monkey/exploit/zerologon_utils/vuln_assessment.py @@ -1,17 +1,18 @@ import logging -from typing import Optional +from typing import Optional, Tuple import nmb.NetBIOS from impacket.dcerpc.v5 import nrpc, rpcrt from common.common_consts.timeouts import MEDIUM_REQUEST_TIMEOUT from common.utils.exceptions import DomainControllerNameFetchError +from infection_monkey.model import VictimHost from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) -def get_dc_details(host: object) -> (str, str, str): +def get_dc_details(host: VictimHost) -> Tuple[str, str, str]: dc_ip = host.ip_addr dc_name = _get_dc_name(dc_ip=dc_ip) dc_handle = "\\\\" + dc_name @@ -35,7 +36,7 @@ def _get_dc_name(dc_ip: str) -> str: ) -def is_exploitable(zerologon_exploiter_object) -> (bool, Optional[rpcrt.DCERPC_v5]): +def is_exploitable(zerologon_exploiter_object) -> Tuple[bool, Optional[rpcrt.DCERPC_v5]]: # Connect to the DC's Netlogon service. try: rpc_con = zerologon_exploiter_object.connect_to_dc(zerologon_exploiter_object.dc_ip) From ccfc41fc2f35f4f7e8ccff7c855711b4d8a8efac Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:54:33 +0000 Subject: [PATCH 07/30] Agent: Fix mypy issues in IslandAPIClient --- .../island_api_client/http_island_api_client.py | 8 ++++---- .../island_api_client/i_island_api_client.py | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/island_api_client/http_island_api_client.py b/monkey/infection_monkey/island_api_client/http_island_api_client.py index a0bebc5a0..3072a54b7 100644 --- a/monkey/infection_monkey/island_api_client/http_island_api_client.py +++ b/monkey/infection_monkey/island_api_client/http_island_api_client.py @@ -94,7 +94,7 @@ class HTTPIslandAPIClient(IIslandAPIClient): return response.content @handle_island_errors - def get_agent_binary(self, operating_system: OperatingSystem): + def get_agent_binary(self, operating_system: OperatingSystem) -> bytes: os_name = operating_system.value response = requests.get( # noqa: DUO123 f"{self._api_url}/agent-binaries/{os_name}", @@ -106,7 +106,7 @@ class HTTPIslandAPIClient(IIslandAPIClient): return response.content @handle_island_errors - def send_events(self, events: Sequence[JSONSerializable]): + def send_events(self, events: Sequence[AbstractAgentEvent]): response = requests.post( # noqa: DUO123 f"{self._api_url}/agent-events", json=self._serialize_events(events), @@ -132,9 +132,9 @@ class HTTPIslandAPIClient(IIslandAPIClient): class HTTPIslandAPIClientFactory(AbstractIslandAPIClientFactory): def __init__( self, - agent_event_serializer_registry: AgentEventSerializerRegistry = None, + agent_event_serializer_registry: AgentEventSerializerRegistry, ): self._agent_event_serializer_registry = agent_event_serializer_registry - def create_island_api_client(self): + def create_island_api_client(self) -> IIslandAPIClient: return HTTPIslandAPIClient(self._agent_event_serializer_registry) diff --git a/monkey/infection_monkey/island_api_client/i_island_api_client.py b/monkey/infection_monkey/island_api_client/i_island_api_client.py index 5bebc79c1..006a2103f 100644 --- a/monkey/infection_monkey/island_api_client/i_island_api_client.py +++ b/monkey/infection_monkey/island_api_client/i_island_api_client.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import Optional, Sequence +from typing import Sequence from common import OperatingSystem from common.agent_events import AbstractAgentEvent @@ -60,7 +60,7 @@ class IIslandAPIClient(ABC): """ @abstractmethod - def get_agent_binary(self, operating_system: OperatingSystem) -> Optional[bytes]: + def get_agent_binary(self, operating_system: OperatingSystem) -> bytes: """ Get an agent binary for the given OS from the island @@ -74,7 +74,6 @@ class IIslandAPIClient(ABC): :raises IslandAPITimeoutError: If a timeout occurs while attempting to connect to the island :raises IslandAPIError: If an unexpected error occurs while attempting to retrieve the agent binary - """ @abstractmethod From 1621b494f49cc3e104061ab3819b5b9490b5a81f Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:55:41 +0000 Subject: [PATCH 08/30] Agent: Fix mypy issues in capture_output.py --- monkey/infection_monkey/utils/capture_output.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/utils/capture_output.py b/monkey/infection_monkey/utils/capture_output.py index 898bd6a7e..f509fa908 100644 --- a/monkey/infection_monkey/utils/capture_output.py +++ b/monkey/infection_monkey/utils/capture_output.py @@ -1,9 +1,11 @@ +from __future__ import annotations + import io import sys class StdoutCapture: - def __enter__(self) -> None: + def __enter__(self) -> StdoutCapture: self._orig_stdout = sys.stdout self._new_stdout = io.StringIO() sys.stdout = self._new_stdout From c78e6333f8b84e596f6996ed7f7bc013dd1d891b Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:57:44 +0000 Subject: [PATCH 09/30] Agent: Fix mypy issues in pba.py --- monkey/infection_monkey/post_breach/pba.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index 0ef8e0ecb..c381f6ad9 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -1,6 +1,6 @@ import logging import subprocess -from typing import Dict, Iterable +from typing import Dict, Iterable, List, Tuple from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.utils.attack_utils import ScanStatus @@ -33,7 +33,7 @@ class PBA: """ self.command = PBA.choose_command(linux_cmd, windows_cmd) self.name = name - self.pba_data = [] + self.pba_data: List[PostBreachData] = [] self.telemetry_messenger = telemetry_messenger self.timeout = timeout @@ -73,7 +73,7 @@ class PBA: pba_execution_succeeded = pba_execution_result[1] return pba_execution_succeeded and self.is_script() - def _execute_default(self): + def _execute_default(self) -> Tuple[str, bool]: """ Default post breach command execution routine :return: Tuple of command's output string and boolean, indicating if it succeeded @@ -84,7 +84,7 @@ class PBA: ).decode() return output, True except subprocess.CalledProcessError as err: - return err.output.decode(), False + return bytes(err.output).decode(), False except subprocess.TimeoutExpired as err: return str(err), False From 97d53dba3cd6761a1a7b4c4bb8ad3f9864ff54c9 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 17:58:41 +0000 Subject: [PATCH 10/30] Agent: Fix mypy issues in ransomware_options.py --- .../infection_monkey/payload/ransomware/ransomware_options.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/payload/ransomware/ransomware_options.py b/monkey/infection_monkey/payload/ransomware/ransomware_options.py index 505974ae0..e1e2d55e5 100644 --- a/monkey/infection_monkey/payload/ransomware/ransomware_options.py +++ b/monkey/infection_monkey/payload/ransomware/ransomware_options.py @@ -1,4 +1,6 @@ import logging +from pathlib import Path +from typing import Optional from common.utils.file_utils import InvalidPath, expand_path from infection_monkey.utils.environment import is_windows_os @@ -12,7 +14,7 @@ class RansomwareOptions: self.file_extension = options["encryption"]["file_extension"] self.readme_enabled = options["other_behaviors"]["readme"] - self.target_directory = None + self.target_directory: Optional[Path] = None self._set_target_directory(options["encryption"]["directories"]) def _set_target_directory(self, os_target_directories: dict): From 95839caf9cca3ea679731ed47245110d5466f62b Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:06:19 +0000 Subject: [PATCH 11/30] Island: Fix mypy issues in app.py --- monkey/monkey_island/cc/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 22a8497e9..c56e13322 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -2,7 +2,7 @@ import os import re import uuid from datetime import timedelta -from typing import Iterable, Type +from typing import Iterable, Set, Type import flask_restful from flask import Flask, Response, send_from_directory @@ -120,7 +120,7 @@ class FlaskDIWrapper: def __init__(self, api: flask_restful.Api, container: DIContainer): self._api = api self._container = container - self._reserved_urls = set() + self._reserved_urls: Set[str] = set() def add_resource(self, resource: Type[AbstractResource]): if len(resource.urls) == 0: From 3e95ec6ee464f6dd1a09a7f16f73ad14a2b92bf5 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:06:53 +0000 Subject: [PATCH 12/30] Island: Fix mypy issues in server_setup.py --- monkey/monkey_island/cc/server_setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 58e5adb40..7b1216419 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -4,7 +4,7 @@ import logging import sys from pathlib import Path from threading import Thread -from typing import Sequence, Tuple +from typing import Optional, Sequence, Tuple import gevent.hub import requests @@ -151,7 +151,7 @@ def _start_mongodb(data_dir: Path) -> MongoDbProcess: return mongo_db_process -def _connect_to_mongodb(mongo_db_process: MongoDbProcess): +def _connect_to_mongodb(mongo_db_process: Optional[MongoDbProcess]): try: mongo_setup.connect_to_mongodb(MONGO_CONNECTION_TIMEOUT) except mongo_setup.MongoDBTimeOutError as err: From ecd2cbbe6efeef443cb9956c9620924ec9306b0e Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:08:25 +0000 Subject: [PATCH 13/30] Island: Fix mypy issues in i_log_repository.py --- monkey/monkey_island/cc/repository/i_log_repository.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/repository/i_log_repository.py b/monkey/monkey_island/cc/repository/i_log_repository.py index b3ac2ae48..61e5137be 100644 --- a/monkey/monkey_island/cc/repository/i_log_repository.py +++ b/monkey/monkey_island/cc/repository/i_log_repository.py @@ -2,8 +2,12 @@ from abc import ABC from typing import Optional, Sequence +# TODO: Actually define the Log class +class Log: + pass + + class ILogRepository(ABC): - # Define log object def get_logs(self, agent_id: Optional[str] = None) -> Sequence[Log]: # noqa: F821 pass From 421ed942feb33d8ea167d4f6a767aa6fa2509a00 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:09:18 +0000 Subject: [PATCH 14/30] Island: Fix mypy issues in AbstractResource.py --- monkey/monkey_island/cc/resources/AbstractResource.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/resources/AbstractResource.py b/monkey/monkey_island/cc/resources/AbstractResource.py index 799cd5a23..8482926be 100644 --- a/monkey/monkey_island/cc/resources/AbstractResource.py +++ b/monkey/monkey_island/cc/resources/AbstractResource.py @@ -1,6 +1,8 @@ +from typing import List + import flask_restful # The purpose of this class is to decouple resources from flask class AbstractResource(flask_restful.Resource): - urls = [] + urls: List[str] = [] From b8230ffb734fcce49779883708aeefc769adf179 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:19:58 +0000 Subject: [PATCH 15/30] Island: Fix mypy issues for encryptors --- .../encryption/data_store_encryptor.py | 6 ++--- .../encryption/repository_encryptor.py | 25 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py index ed491d888..8027e252b 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/data_store_encryptor.py @@ -1,7 +1,7 @@ import os import secrets from pathlib import Path -from typing import Union +from typing import Optional from monkey_island.cc.server_utils.encryption.encryption_key_types import EncryptionKey32Bytes from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file @@ -12,7 +12,7 @@ from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor _KEY_FILE_NAME = "mongo_key.bin" -_encryptor: Union[None, IEncryptor] = None +_encryptor: Optional[IEncryptor] = None # NOTE: This class is being replaced by RepositoryEncryptor @@ -73,5 +73,5 @@ def _initialize_datastore_encryptor(key_file: Path, secret: str): _encryptor = DataStoreEncryptor(secret, key_file) -def get_datastore_encryptor() -> IEncryptor: +def get_datastore_encryptor() -> Optional[IEncryptor]: return _encryptor diff --git a/monkey/monkey_island/cc/server_utils/encryption/repository_encryptor.py b/monkey/monkey_island/cc/server_utils/encryption/repository_encryptor.py index 48970fa81..097d460ef 100644 --- a/monkey/monkey_island/cc/server_utils/encryption/repository_encryptor.py +++ b/monkey/monkey_island/cc/server_utils/encryption/repository_encryptor.py @@ -1,10 +1,11 @@ import secrets from pathlib import Path +from typing import Optional from monkey_island.cc.server_utils.encryption.encryption_key_types import EncryptionKey32Bytes from monkey_island.cc.server_utils.file_utils import open_new_securely_permissioned_file -from . import ILockableEncryptor, LockedKeyError, ResetKeyError, UnlockError +from . import IEncryptor, ILockableEncryptor, LockedKeyError, ResetKeyError, UnlockError from .key_based_encryptor import KeyBasedEncryptor from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor @@ -12,33 +13,32 @@ from .password_based_bytes_encryptor import PasswordBasedBytesEncryptor class RepositoryEncryptor(ILockableEncryptor): def __init__(self, key_file: Path): self._key_file = key_file - self._password_based_encryptor = None - self._key_based_encryptor = None + self._key_based_encryptor: Optional[IEncryptor] = None def unlock(self, secret: bytes): try: - self._password_based_encryptor = PasswordBasedBytesEncryptor(secret.decode()) - self._key_based_encryptor = self._initialize_key_based_encryptor() + encryptor = PasswordBasedBytesEncryptor(secret.decode()) + self._key_based_encryptor = self._initialize_key_based_encryptor(encryptor) except Exception as err: raise UnlockError(err) - def _initialize_key_based_encryptor(self): + def _initialize_key_based_encryptor(self, encryptor: IEncryptor) -> KeyBasedEncryptor: if self._key_file.is_file(): - return self._load_key() + return self._load_key(encryptor) - return self._create_key() + return self._create_key(encryptor) - def _load_key(self) -> KeyBasedEncryptor: + def _load_key(self, encryptor: IEncryptor) -> KeyBasedEncryptor: with open(self._key_file, "rb") as f: encrypted_key = f.read() - plaintext_key = EncryptionKey32Bytes(self._password_based_encryptor.decrypt(encrypted_key)) + plaintext_key = EncryptionKey32Bytes(encryptor.decrypt(encrypted_key)) return KeyBasedEncryptor(plaintext_key) - def _create_key(self) -> KeyBasedEncryptor: + def _create_key(self, encryptor: IEncryptor) -> KeyBasedEncryptor: plaintext_key = EncryptionKey32Bytes(secrets.token_bytes(32)) - encrypted_key = self._password_based_encryptor.encrypt(plaintext_key) + encrypted_key = encryptor.encrypt(plaintext_key) with open_new_securely_permissioned_file(str(self._key_file), "wb") as f: f.write(encrypted_key) @@ -54,7 +54,6 @@ class RepositoryEncryptor(ILockableEncryptor): except Exception as err: raise ResetKeyError(err) - self._password_based_encryptor = None self._key_based_encryptor = None def encrypt(self, plaintext: bytes) -> bytes: From 9c49cdafa36a899d575b86fe6e21b801a24a49af Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:21:03 +0000 Subject: [PATCH 16/30] Island: Fix mypy issues in exploit.py --- .../cc/services/telemetry/processing/exploit.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index b7b362a51..214f64b56 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -1,6 +1,6 @@ import copy -import dateutil +from dateutil import parser as dateutil_parser from monkey_island.cc.models import Monkey from monkey_island.cc.server_utils.encryption import get_datastore_encryptor @@ -29,10 +29,10 @@ def process_exploit_telemetry(telemetry_json, _): def update_network_with_exploit(edge: EdgeService, telemetry_json): - telemetry_json["data"]["info"]["started"] = dateutil.parser.parse( + telemetry_json["data"]["info"]["started"] = dateutil_parser.parse( telemetry_json["data"]["info"]["started"] ) - telemetry_json["data"]["info"]["finished"] = dateutil.parser.parse( + telemetry_json["data"]["info"]["finished"] = dateutil_parser.parse( telemetry_json["data"]["info"]["finished"] ) new_exploit = copy.deepcopy(telemetry_json["data"]) From 22b12080b6a1c1904e0cb49a83965530b913d999 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:21:42 +0000 Subject: [PATCH 17/30] Island: Fix mypy issues in ransomware_report.py --- .../monkey_island/cc/services/ransomware/ransomware_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/services/ransomware/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware/ransomware_report.py index 5dd384511..0e71a978b 100644 --- a/monkey/monkey_island/cc/services/ransomware/ransomware_report.py +++ b/monkey/monkey_island/cc/services/ransomware/ransomware_report.py @@ -18,8 +18,8 @@ def get_propagation_stats() -> Dict: } -def _get_exploit_counts(exploited: List[MonkeyExploitation]) -> Dict: - exploit_counts = {} +def _get_exploit_counts(exploited: List[MonkeyExploitation]) -> Dict[str, int]: + exploit_counts: Dict[str, int] = {} for node in exploited: for exploit in node.exploits: From 85dbda8ba99a1f7f710137eccd1a8444af2441fc Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:22:20 +0000 Subject: [PATCH 18/30] Island: Fix mypy issues in cred_exploit.py --- .../exploit_processing/exploiter_report_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py index 069d93a8d..e1bf55edd 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py @@ -15,7 +15,7 @@ class ExploiterReportInfo: ip_address: str type: str username: Union[str, None] = None - credential_type: Union[CredentialType, None] = None + credential_type: Union[str, None] = None ssh_key: Union[str, None] = None password: Union[str, None] = None port: Union[str, None] = None From 0578219549acb046cdaa7415b2a7680baf14574e Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:23:33 +0000 Subject: [PATCH 19/30] Island: Fix mypy issues in finding_service.py --- .../zero_trust_report/finding_service.py | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/finding_service.py b/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/finding_service.py index 8c70130c7..d5df1372b 100644 --- a/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/finding_service.py +++ b/monkey/monkey_island/cc/services/zero_trust/zero_trust_report/finding_service.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import List, Union +from typing import Dict, List, Union, cast from bson import SON @@ -30,21 +30,26 @@ class FindingService: @staticmethod def get_all_findings_for_ui() -> List[EnrichedFinding]: findings = FindingService.get_all_findings_from_db() - for i in range(len(findings)): - details = FindingService._get_finding_details(findings[i]) - findings[i] = findings[i].to_mongo() - findings[i] = FindingService._get_enriched_finding(findings[i]) - findings[i].details = details - return findings + enriched_findings: List[EnrichedFinding] = [] + for finding in findings: + finding_data = finding.to_mongo() + enriched_finding = FindingService._get_enriched_finding(finding_data) + details = FindingService._get_finding_details(finding) + enriched_finding.details = details + enriched_findings.append(enriched_finding) + + return enriched_findings @staticmethod - def _get_enriched_finding(finding: Finding) -> EnrichedFinding: + def _get_enriched_finding(finding: SON) -> EnrichedFinding: test_info = zero_trust_consts.TESTS_MAP[finding["test"]] enriched_finding = EnrichedFinding( finding_id=str(finding["_id"]), - test=test_info[zero_trust_consts.FINDING_EXPLANATION_BY_STATUS_KEY][finding["status"]], + test=cast( + Dict[str, str], test_info[zero_trust_consts.FINDING_EXPLANATION_BY_STATUS_KEY] + )[finding["status"]], test_key=finding["test"], - pillars=test_info[zero_trust_consts.PILLARS_KEY], + pillars=cast(List[str], test_info[zero_trust_consts.PILLARS_KEY]), status=finding["status"], details=None, ) From 5f3d757965c9e428a260a43786c95c8c0a4111f2 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:24:47 +0000 Subject: [PATCH 20/30] Island: Fix mypy issues in mongo_db_process.py --- monkey/monkey_island/cc/setup/mongo/mongo_db_process.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/setup/mongo/mongo_db_process.py b/monkey/monkey_island/cc/setup/mongo/mongo_db_process.py index db3f5c0ca..3da2697ea 100644 --- a/monkey/monkey_island/cc/setup/mongo/mongo_db_process.py +++ b/monkey/monkey_island/cc/setup/mongo/mongo_db_process.py @@ -49,7 +49,7 @@ class MongoDbProcess: self._process.kill() def is_running(self) -> bool: - if self._process.poll() is None: + if self._process and self._process.poll() is None: return True return False From c0e98bc0d1b1120322ec9b62ade6c71b5b43130c Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:25:38 +0000 Subject: [PATCH 21/30] Island: Fix mypy issues in T1065.py --- .../cc/services/attack/technique_reports/T1065.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py index d28c5e9e0..9c273cca0 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py @@ -25,4 +25,8 @@ class T1065(AttackTechnique): @staticmethod def get_tunnel_ports() -> Sequence[str]: telems = Telemetry.objects(telem_category="tunnel", data__proxy__ne=None) - return [address_to_ip_port(telem["data"]["proxy"])[1] for telem in telems] + return [ + p + for p in (address_to_ip_port(telem["data"]["proxy"])[1] for telem in telems) + if p is not None + ] From e478ba80e04fb33eb8c1e1f6ec0e576892f07c03 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:28:17 +0000 Subject: [PATCH 22/30] Island: Fix mypy issues in version.py --- monkey/monkey_island/cc/resources/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/resources/version.py b/monkey/monkey_island/cc/resources/version.py index 69a9cb851..9da127e09 100644 --- a/monkey/monkey_island/cc/resources/version.py +++ b/monkey/monkey_island/cc/resources/version.py @@ -1,6 +1,6 @@ import logging -from monkey_island.cc import Version +from monkey_island.cc import Version as IslandVersion from monkey_island.cc.resources.AbstractResource import AbstractResource from monkey_island.cc.resources.request_authentication import jwt_required @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) class Version(AbstractResource): urls = ["/api/island/version"] - def __init__(self, version: Version): + def __init__(self, version: IslandVersion): self._version = version @jwt_required From 1defad72fd7414578179692e73c5222f53042c1f Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:28:58 +0000 Subject: [PATCH 23/30] Island: Fix mypy issues in networkmap.py --- monkey/monkey_island/cc/models/networkmap.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/models/networkmap.py b/monkey/monkey_island/cc/models/networkmap.py index e249a19fc..8a3816e1a 100644 --- a/monkey/monkey_island/cc/models/networkmap.py +++ b/monkey/monkey_island/cc/models/networkmap.py @@ -1,6 +1,14 @@ from dataclasses import dataclass from typing import Mapping, Sequence +from monkey_island.cc.models import Machine + + +@dataclass +class Arc: + dst_machine: Machine # noqa: F821 + status: str + # This is the most concise way to represent a graph: # Machine id as key, Arch list as a value @@ -9,9 +17,3 @@ from typing import Mapping, Sequence @dataclass class NetworkMap: nodes: Mapping[str, Sequence[Arc]] # noqa: F821 - - -@dataclass -class Arc: - dst_machine: Machine # noqa: F821 - status: str From bba6386efa7f0f9d1dce41591ab9f840c13189af Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 18:29:34 +0000 Subject: [PATCH 24/30] Island: Fix mypy issues in segmentation.py --- .../cc/services/telemetry/zero_trust_checks/segmentation.py | 2 ++ 1 file changed, 2 insertions(+) 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 3386e2b3c..667027988 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 @@ -67,6 +67,8 @@ def is_segmentation_violation( return cross_segment_ip is not None + return False + def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet): return Event.create_event( From 06965abe5ddbbad8d05787008120bc149d31ad79 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 22:17:32 +0000 Subject: [PATCH 25/30] Island: Remove networkmap.py --- monkey/monkey_island/cc/models/networkmap.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 monkey/monkey_island/cc/models/networkmap.py diff --git a/monkey/monkey_island/cc/models/networkmap.py b/monkey/monkey_island/cc/models/networkmap.py deleted file mode 100644 index 8a3816e1a..000000000 --- a/monkey/monkey_island/cc/models/networkmap.py +++ /dev/null @@ -1,19 +0,0 @@ -from dataclasses import dataclass -from typing import Mapping, Sequence - -from monkey_island.cc.models import Machine - - -@dataclass -class Arc: - dst_machine: Machine # noqa: F821 - status: str - - -# This is the most concise way to represent a graph: -# Machine id as key, Arch list as a value -# Not sure how compatible this will be with ORM objects though, -# might require more complex casting logic -@dataclass -class NetworkMap: - nodes: Mapping[str, Sequence[Arc]] # noqa: F821 From 5e7689345f20518716b21a3d169d9004abaf56f0 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 21 Sep 2022 22:18:09 +0000 Subject: [PATCH 26/30] Island: Remove i_log_repository.py --- .../cc/repository/i_log_repository.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 monkey/monkey_island/cc/repository/i_log_repository.py diff --git a/monkey/monkey_island/cc/repository/i_log_repository.py b/monkey/monkey_island/cc/repository/i_log_repository.py deleted file mode 100644 index 61e5137be..000000000 --- a/monkey/monkey_island/cc/repository/i_log_repository.py +++ /dev/null @@ -1,18 +0,0 @@ -from abc import ABC -from typing import Optional, Sequence - - -# TODO: Actually define the Log class -class Log: - pass - - -class ILogRepository(ABC): - def get_logs(self, agent_id: Optional[str] = None) -> Sequence[Log]: # noqa: F821 - pass - - def save_log(self, log: Log): # noqa: F821 - pass - - def delete_log(self, agent_id: str): - pass From f591f150fcee3ecbc2ba34d410d4ddf7ab8b708e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 21 Sep 2022 20:43:09 -0400 Subject: [PATCH 27/30] Revert "Island: Fix mypy issues in exploit.py" This reverts commit 9c49cdafa36a899d575b86fe6e21b801a24a49af. --- .../cc/services/telemetry/processing/exploit.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index 214f64b56..b7b362a51 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -1,6 +1,6 @@ import copy -from dateutil import parser as dateutil_parser +import dateutil from monkey_island.cc.models import Monkey from monkey_island.cc.server_utils.encryption import get_datastore_encryptor @@ -29,10 +29,10 @@ def process_exploit_telemetry(telemetry_json, _): def update_network_with_exploit(edge: EdgeService, telemetry_json): - telemetry_json["data"]["info"]["started"] = dateutil_parser.parse( + telemetry_json["data"]["info"]["started"] = dateutil.parser.parse( telemetry_json["data"]["info"]["started"] ) - telemetry_json["data"]["info"]["finished"] = dateutil_parser.parse( + telemetry_json["data"]["info"]["finished"] = dateutil.parser.parse( telemetry_json["data"]["info"]["finished"] ) new_exploit = copy.deepcopy(telemetry_json["data"]) From 13f7301db99efb83e5db74adb76fa2564ec2dee9 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 21 Sep 2022 20:43:50 -0400 Subject: [PATCH 28/30] Island: Add types-python-dateutil as dev dependency --- monkey/monkey_island/Pipfile | 1 + monkey/monkey_island/Pipfile.lock | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/Pipfile b/monkey/monkey_island/Pipfile index 212af9bad..27811eb58 100644 --- a/monkey/monkey_island/Pipfile +++ b/monkey/monkey_island/Pipfile @@ -50,6 +50,7 @@ tqdm = "*" # Used in BB tests sphinx = "*" # Used in documentation sphinx_rtd_theme = "*" sphinx_autodoc_typehints = "*" +types-python-dateutil = "*" [requires] python_version = "3.7" diff --git a/monkey/monkey_island/Pipfile.lock b/monkey/monkey_island/Pipfile.lock index 16908ab87..5911952f0 100644 --- a/monkey/monkey_island/Pipfile.lock +++ b/monkey/monkey_island/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "7a3d0acc52ca38402412bd299c9cef2f387b7abe8803a3a4c839cbe8e3091195" + "sha256": "0cacab24c5242c8a27fa9ee3c7e91117f26234dd952d3e25bc7d5b1cf34cffff" }, "pipfile-spec": 6, "requires": { @@ -847,7 +847,7 @@ "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" ], - "markers": "python_version < '3.8'", + "markers": "python_version >= '3.7'", "version": "==4.3.0" }, "urllib3": { @@ -1505,12 +1505,20 @@ "markers": "python_version < '3.8' and implementation_name == 'cpython'", "version": "==1.5.4" }, + "types-python-dateutil": { + "hashes": [ + "sha256:6284df1e4783d8fc6e587f0317a81333856b872a6669a282f8a325342bce7fa8", + "sha256:bfd3eb39c7253aea4ba23b10f69b017d30b013662bb4be4ab48b20bbd763f309" + ], + "index": "pypi", + "version": "==2.8.19" + }, "typing-extensions": { "hashes": [ "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" ], - "markers": "python_version < '3.8'", + "markers": "python_version >= '3.7'", "version": "==4.3.0" }, "urllib3": { From 38d9ccc9f0d056ec33471d09761896945dda594a Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Thu, 22 Sep 2022 13:11:08 +0000 Subject: [PATCH 29/30] Agent: Remove stale comment --- monkey/infection_monkey/exploit/HostExploiter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 6291d98d3..c9b312fb9 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -56,7 +56,6 @@ class HostExploiter: } ) - # TODO: host should be VictimHost, at the moment it can't because of circular dependency def exploit_host( self, host: VictimHost, From 4226d9029fce527aa86a7470129c753c7d109896 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Thu, 22 Sep 2022 13:38:45 +0000 Subject: [PATCH 30/30] Island: Simplify T1065.get_tunnel_ports() --- .../cc/services/attack/technique_reports/T1065.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py index 9c273cca0..b6bfd0acb 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py @@ -1,4 +1,4 @@ -from typing import Sequence +from typing import Iterable from common.network.network_utils import address_to_ip_port from common.utils.attack_utils import ScanStatus @@ -17,16 +17,11 @@ class T1065(AttackTechnique): @staticmethod def get_report_data(): - tunneling_ports = T1065.get_tunnel_ports() - non_standard_ports = [*tunneling_ports, str(ISLAND_PORT)] + non_standard_ports = [*T1065.get_tunnel_ports(), str(ISLAND_PORT)] T1065.used_msg = T1065.message % ", ".join(non_standard_ports) return T1065.get_base_data_by_status(ScanStatus.USED.value) @staticmethod - def get_tunnel_ports() -> Sequence[str]: + def get_tunnel_ports() -> Iterable[str]: telems = Telemetry.objects(telem_category="tunnel", data__proxy__ne=None) - return [ - p - for p in (address_to_ip_port(telem["data"]["proxy"])[1] for telem in telems) - if p is not None - ] + return filter(None, [address_to_ip_port(telem["data"]["proxy"])[1] for telem in telems])