From eb6b06e6a28c1d7eeadeacf20958cb6c9da91040 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 8 Aug 2022 15:34:53 +0300 Subject: [PATCH 1/6] Agent: Rename credential_store to credential_repository --- .../credential_repository/__init__.py | 4 ++++ ...ing_propagation_credentials_repository.py} | 4 ++-- .../i_propagation_credentials_repository.py} | 2 +- .../credential_store/__init__.py | 2 -- .../master/automated_master.py | 4 ++-- monkey/infection_monkey/monkey.py | 9 +++++--- ...ntials_intercepting_telemetry_messenger.py | 10 +++++---- ...ing_propagation_credentials_repository.py} | 22 +++++++++---------- ...ntials_intercepting_telemetry_messenger.py | 12 +++++----- 9 files changed, 38 insertions(+), 31 deletions(-) create mode 100644 monkey/infection_monkey/credential_repository/__init__.py rename monkey/infection_monkey/{credential_store/aggregating_credentials_store.py => credential_repository/aggregating_propagation_credentials_repository.py} (95%) rename monkey/infection_monkey/{credential_store/i_credentials_store.py => credential_repository/i_propagation_credentials_repository.py} (91%) delete mode 100644 monkey/infection_monkey/credential_store/__init__.py rename monkey/tests/unit_tests/infection_monkey/credential_store/{test_aggregating_credentials_store.py => test_aggregating_propagation_credentials_repository.py} (79%) diff --git a/monkey/infection_monkey/credential_repository/__init__.py b/monkey/infection_monkey/credential_repository/__init__.py new file mode 100644 index 000000000..62eae389b --- /dev/null +++ b/monkey/infection_monkey/credential_repository/__init__.py @@ -0,0 +1,4 @@ +from .i_propagation_credentials_repository import IPropagationCredentialsRepository +from .aggregating_propagation_credentials_repository import ( + AggregatingPropagationCredentialsRepository, +) diff --git a/monkey/infection_monkey/credential_store/aggregating_credentials_store.py b/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py similarity index 95% rename from monkey/infection_monkey/credential_store/aggregating_credentials_store.py rename to monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py index cef6f1bf4..054428d59 100644 --- a/monkey/infection_monkey/credential_store/aggregating_credentials_store.py +++ b/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py @@ -6,14 +6,14 @@ from infection_monkey.custom_types import PropagationCredentials from infection_monkey.i_control_channel import IControlChannel from infection_monkey.utils.decorators import request_cache -from .i_credentials_store import ICredentialsStore +from .i_propagation_credentials_repository import IPropagationCredentialsRepository logger = logging.getLogger(__name__) CREDENTIALS_POLL_PERIOD_SEC = 10 -class AggregatingCredentialsStore(ICredentialsStore): +class AggregatingPropagationCredentialsRepository(IPropagationCredentialsRepository): def __init__(self, control_channel: IControlChannel): self._stored_credentials = { "exploit_user_list": set(), diff --git a/monkey/infection_monkey/credential_store/i_credentials_store.py b/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py similarity index 91% rename from monkey/infection_monkey/credential_store/i_credentials_store.py rename to monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py index 15486afb4..be110476f 100644 --- a/monkey/infection_monkey/credential_store/i_credentials_store.py +++ b/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py @@ -5,7 +5,7 @@ from common.credentials import Credentials from infection_monkey.custom_types import PropagationCredentials -class ICredentialsStore(metaclass=abc.ABCMeta): +class IPropagationCredentialsRepository(metaclass=abc.ABCMeta): @abc.abstractmethod def add_credentials(self, credentials_to_add: Iterable[Credentials]): """ diff --git a/monkey/infection_monkey/credential_store/__init__.py b/monkey/infection_monkey/credential_store/__init__.py deleted file mode 100644 index e05ce3160..000000000 --- a/monkey/infection_monkey/credential_store/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .i_credentials_store import ICredentialsStore -from .aggregating_credentials_store import AggregatingCredentialsStore diff --git a/monkey/infection_monkey/master/automated_master.py b/monkey/infection_monkey/master/automated_master.py index 004bf05a2..55afad11a 100644 --- a/monkey/infection_monkey/master/automated_master.py +++ b/monkey/infection_monkey/master/automated_master.py @@ -5,7 +5,7 @@ from typing import Any, Callable, Iterable, List, Optional from common.agent_configuration import CustomPBAConfiguration, PluginConfiguration from common.utils import Timer -from infection_monkey.credential_store import ICredentialsStore +from infection_monkey.credential_repository import IPropagationCredentialsRepository from infection_monkey.i_control_channel import IControlChannel, IslandCommunicationError from infection_monkey.i_master import IMaster from infection_monkey.i_puppet import IPuppet @@ -40,7 +40,7 @@ class AutomatedMaster(IMaster): victim_host_factory: VictimHostFactory, control_channel: IControlChannel, local_network_interfaces: List[NetworkInterface], - credentials_store: ICredentialsStore, + credentials_store: IPropagationCredentialsRepository, ): self._current_depth = current_depth self._puppet = puppet diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index 749803c7b..c70a030ff 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -17,7 +17,10 @@ from infection_monkey.credential_collectors import ( MimikatzCredentialCollector, SSHCredentialCollector, ) -from infection_monkey.credential_store import AggregatingCredentialsStore, ICredentialsStore +from infection_monkey.credential_store import ( + AggregatingPropagationCredentialsRepository, + IPropagationCredentialsRepository, +) from infection_monkey.exploit import CachingAgentRepository, ExploiterWrapper from infection_monkey.exploit.hadoop import HadoopExploiter from infection_monkey.exploit.log4shell import Log4ShellExploiter @@ -195,7 +198,7 @@ class InfectionMonkey: control_channel = ControlChannel( self._control_client.server_address, GUID, self._control_client.proxies ) - credentials_store = AggregatingCredentialsStore(control_channel) + credentials_store = AggregatingPropagationCredentialsRepository(control_channel) puppet = self._build_puppet(credentials_store) @@ -226,7 +229,7 @@ class InfectionMonkey: return local_network_interfaces - def _build_puppet(self, credentials_store: ICredentialsStore) -> IPuppet: + def _build_puppet(self, credentials_store: IPropagationCredentialsRepository) -> IPuppet: puppet = Puppet() puppet.load_plugin( diff --git a/monkey/infection_monkey/telemetry/messengers/credentials_intercepting_telemetry_messenger.py b/monkey/infection_monkey/telemetry/messengers/credentials_intercepting_telemetry_messenger.py index 541800577..25500644b 100644 --- a/monkey/infection_monkey/telemetry/messengers/credentials_intercepting_telemetry_messenger.py +++ b/monkey/infection_monkey/telemetry/messengers/credentials_intercepting_telemetry_messenger.py @@ -1,6 +1,6 @@ from functools import singledispatch -from infection_monkey.credential_store import ICredentialsStore +from infection_monkey.credential_repository import IPropagationCredentialsRepository from infection_monkey.telemetry.credentials_telem import CredentialsTelem from infection_monkey.telemetry.i_telem import ITelem from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger @@ -8,7 +8,9 @@ from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemet class CredentialsInterceptingTelemetryMessenger(ITelemetryMessenger): def __init__( - self, telemetry_messenger: ITelemetryMessenger, credentials_store: ICredentialsStore + self, + telemetry_messenger: ITelemetryMessenger, + credentials_store: IPropagationCredentialsRepository, ): self._telemetry_messenger = telemetry_messenger self._credentials_store = credentials_store @@ -23,7 +25,7 @@ class CredentialsInterceptingTelemetryMessenger(ITelemetryMessenger): def _send_telemetry( telemetry: ITelem, telemetry_messenger: ITelemetryMessenger, - credentials_store: ICredentialsStore, + credentials_store: IPropagationCredentialsRepository, ): telemetry_messenger.send_telemetry(telemetry) @@ -32,7 +34,7 @@ def _send_telemetry( def _( telemetry: CredentialsTelem, telemetry_messenger: ITelemetryMessenger, - credentials_store: ICredentialsStore, + credentials_store: IPropagationCredentialsRepository, ): credentials_store.add_credentials(telemetry.credentials) telemetry_messenger.send_telemetry(telemetry) diff --git a/monkey/tests/unit_tests/infection_monkey/credential_store/test_aggregating_credentials_store.py b/monkey/tests/unit_tests/infection_monkey/credential_store/test_aggregating_propagation_credentials_repository.py similarity index 79% rename from monkey/tests/unit_tests/infection_monkey/credential_store/test_aggregating_credentials_store.py rename to monkey/tests/unit_tests/infection_monkey/credential_store/test_aggregating_propagation_credentials_repository.py index 49bdb2b88..f2cc0dcfa 100644 --- a/monkey/tests/unit_tests/infection_monkey/credential_store/test_aggregating_credentials_store.py +++ b/monkey/tests/unit_tests/infection_monkey/credential_store/test_aggregating_propagation_credentials_repository.py @@ -15,7 +15,7 @@ from tests.data_for_tests.propagation_credentials import ( ) from common.credentials import Credentials, LMHash, NTHash, Password, SSHKeypair, Username -from infection_monkey.credential_store import AggregatingCredentialsStore +from infection_monkey.credential_repository import AggregatingPropagationCredentialsRepository CONTROL_CHANNEL_CREDENTIALS = PROPAGATION_CREDENTIALS TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS = { @@ -67,24 +67,24 @@ STOLEN_SSH_KEYS_CREDENTIALS = [ @pytest.fixture -def aggregating_credentials_store() -> AggregatingCredentialsStore: +def aggregating_credentials_repository() -> AggregatingPropagationCredentialsRepository: control_channel = MagicMock() control_channel.get_credentials_for_propagation.return_value = CONTROL_CHANNEL_CREDENTIALS - return AggregatingCredentialsStore(control_channel) + return AggregatingPropagationCredentialsRepository(control_channel) @pytest.mark.parametrize("key", TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS.keys()) -def test_get_credentials_from_store(aggregating_credentials_store, key): - actual_stored_credentials = aggregating_credentials_store.get_credentials() +def test_get_credentials_from_repository(aggregating_credentials_repository, key): + actual_stored_credentials = aggregating_credentials_repository.get_credentials() assert actual_stored_credentials[key] == TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS[key] -def test_add_credentials_to_store(aggregating_credentials_store): - aggregating_credentials_store.add_credentials(STOLEN_CREDENTIALS) - aggregating_credentials_store.add_credentials(STOLEN_SSH_KEYS_CREDENTIALS) +def test_add_credentials_to_repository(aggregating_credentials_repository): + aggregating_credentials_repository.add_credentials(STOLEN_CREDENTIALS) + aggregating_credentials_repository.add_credentials(STOLEN_SSH_KEYS_CREDENTIALS) - actual_stored_credentials = aggregating_credentials_store.get_credentials() + actual_stored_credentials = aggregating_credentials_repository.get_credentials() assert actual_stored_credentials["exploit_user_list"] == set( [ @@ -113,9 +113,9 @@ def test_add_credentials_to_store(aggregating_credentials_store): def test_all_keys_if_credentials_empty(): control_channel = MagicMock() control_channel.get_credentials_for_propagation.return_value = EMPTY_CHANNEL_CREDENTIALS - credentials_store = AggregatingCredentialsStore(control_channel) + credentials_repository = AggregatingPropagationCredentialsRepository(control_channel) - actual_stored_credentials = credentials_store.get_credentials() + actual_stored_credentials = credentials_repository.get_credentials() print(type(actual_stored_credentials)) assert "exploit_user_list" in actual_stored_credentials diff --git a/monkey/tests/unit_tests/infection_monkey/telemetry/messengers/test_credentials_intercepting_telemetry_messenger.py b/monkey/tests/unit_tests/infection_monkey/telemetry/messengers/test_credentials_intercepting_telemetry_messenger.py index eda8954d7..81bd9e30a 100644 --- a/monkey/tests/unit_tests/infection_monkey/telemetry/messengers/test_credentials_intercepting_telemetry_messenger.py +++ b/monkey/tests/unit_tests/infection_monkey/telemetry/messengers/test_credentials_intercepting_telemetry_messenger.py @@ -25,28 +25,28 @@ class MockCredentialsTelem(CredentialsTelem): def test_credentials_generic_telemetry(TestTelem): mock_telemetry_messenger = MagicMock() - mock_credentials_store = MagicMock() + mock_credentials_repository = MagicMock() telemetry_messenger = CredentialsInterceptingTelemetryMessenger( - mock_telemetry_messenger, mock_credentials_store + mock_telemetry_messenger, mock_credentials_repository ) telemetry_messenger.send_telemetry(TestTelem()) assert mock_telemetry_messenger.send_telemetry.called - assert not mock_credentials_store.add_credentials.called + assert not mock_credentials_repository.add_credentials.called def test_successful_intercepting_credentials_telemetry(): mock_telemetry_messenger = MagicMock() - mock_credentials_store = MagicMock() + mock_credentials_repository = MagicMock() mock_empty_credentials_telem = MockCredentialsTelem(TELEM_CREDENTIALS) telemetry_messenger = CredentialsInterceptingTelemetryMessenger( - mock_telemetry_messenger, mock_credentials_store + mock_telemetry_messenger, mock_credentials_repository ) telemetry_messenger.send_telemetry(mock_empty_credentials_telem) assert mock_telemetry_messenger.send_telemetry.called - assert mock_credentials_store.add_credentials.called + assert mock_credentials_repository.add_credentials.called From 4f6500ad8348603a799416a6235e99e021b71cc9 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 8 Aug 2022 15:41:45 +0300 Subject: [PATCH 2/6] Island: Use python json encoder instead of flask Flask json acts strange and doesn't trigger the "default" method when it encounters CredentialTypeEnum, even though it can't serialize it --- monkey/monkey_island/cc/services/representations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/representations.py b/monkey/monkey_island/cc/services/representations.py index 2fbdf6299..118e80aca 100644 --- a/monkey/monkey_island/cc/services/representations.py +++ b/monkey/monkey_island/cc/services/representations.py @@ -5,7 +5,7 @@ from typing import Any import bson from flask import make_response -from flask.json import JSONEncoder, dumps +from json import JSONEncoder, dumps from common.utils import IJSONSerializable From 022daf762b2abd97dce696f0fc49725c670af80d Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 8 Aug 2022 16:09:59 +0300 Subject: [PATCH 3/6] Agent: Improve documentation of credential repository --- ...gating_propagation_credentials_repository.py | 17 +++++++++++++++++ .../i_propagation_credentials_repository.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) 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 054428d59..f347b901c 100644 --- a/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py +++ b/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py @@ -33,10 +33,18 @@ class AggregatingPropagationCredentialsRepository(IPropagationCredentialsReposit self._add_secret(credentials.secret) def _add_identity(self, identity: ICredentialComponent): + """ + Stores identity component to the repository + :param identity: Identity credential component + """ if identity.credential_type is CredentialComponentType.USERNAME: self._stored_credentials.setdefault("exploit_user_list", set()).add(identity.username) def _add_secret(self, secret: ICredentialComponent): + """ + Stores secret component to the repository + :param secret: Secret credential component + """ if secret.credential_type is CredentialComponentType.PASSWORD: self._stored_credentials.setdefault("exploit_password_list", set()).add(secret.password) elif secret.credential_type is CredentialComponentType.LM_HASH: @@ -61,9 +69,18 @@ class AggregatingPropagationCredentialsRepository(IPropagationCredentialsReposit @request_cache(CREDENTIALS_POLL_PERIOD_SEC) def _get_credentials_from_control_channel(self) -> Sequence[Credentials]: + """ + Fetches credentials from control channel + :return: Credentials that can be used for propagation + """ return self._control_channel.get_credentials_for_propagation() def _set_attribute(self, attribute_to_be_set: str, credentials_values: Iterable[Any]): + """ + Sets a value for a credential type + :param attribute_to_be_set: Key of self._stored_credentials (credential container key) + :param credentials_values: Value we want to set the container to + """ if not credentials_values: return diff --git a/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py b/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py index be110476f..7e5d20dab 100644 --- a/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py +++ b/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py @@ -10,7 +10,7 @@ class IPropagationCredentialsRepository(metaclass=abc.ABCMeta): def add_credentials(self, credentials_to_add: Iterable[Credentials]): """ Adds credentials to the CredentialStore - :param Iterable[Credentials] credentials: The credentials that will be added + :param Iterable[Credentials] credentials_to_add: The credentials that will be added """ @abc.abstractmethod From 9c5a81105f5c4629be19b3ee971ff8903a72f3e9 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 8 Aug 2022 16:54:40 +0300 Subject: [PATCH 4/6] Island: Sort imports in representations.py --- monkey/monkey_island/cc/services/representations.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/services/representations.py b/monkey/monkey_island/cc/services/representations.py index 118e80aca..e679231c1 100644 --- a/monkey/monkey_island/cc/services/representations.py +++ b/monkey/monkey_island/cc/services/representations.py @@ -1,11 +1,10 @@ from datetime import datetime from enum import Enum -from json import loads +from json import JSONEncoder, dumps, loads from typing import Any import bson from flask import make_response -from json import JSONEncoder, dumps from common.utils import IJSONSerializable From 223474d983ac7d3fc834f9463a48027d014a4937 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 8 Aug 2022 17:20:27 +0300 Subject: [PATCH 5/6] Island: Document IPropagationRepository and child classes --- ...ting_propagation_credentials_repository.py | 22 +++++-------------- .../i_propagation_credentials_repository.py | 4 ++++ 2 files changed, 9 insertions(+), 17 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 f347b901c..3050e5f85 100644 --- a/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py +++ b/monkey/infection_monkey/credential_repository/aggregating_propagation_credentials_repository.py @@ -14,6 +14,11 @@ CREDENTIALS_POLL_PERIOD_SEC = 10 class AggregatingPropagationCredentialsRepository(IPropagationCredentialsRepository): + """ + Repository that stores credentials on the island and saves/gets credentials by using + command and control channel + """ + def __init__(self, control_channel: IControlChannel): self._stored_credentials = { "exploit_user_list": set(), @@ -33,18 +38,10 @@ class AggregatingPropagationCredentialsRepository(IPropagationCredentialsReposit self._add_secret(credentials.secret) def _add_identity(self, identity: ICredentialComponent): - """ - Stores identity component to the repository - :param identity: Identity credential component - """ if identity.credential_type is CredentialComponentType.USERNAME: self._stored_credentials.setdefault("exploit_user_list", set()).add(identity.username) def _add_secret(self, secret: ICredentialComponent): - """ - Stores secret component to the repository - :param secret: Secret credential component - """ if secret.credential_type is CredentialComponentType.PASSWORD: self._stored_credentials.setdefault("exploit_password_list", set()).add(secret.password) elif secret.credential_type is CredentialComponentType.LM_HASH: @@ -69,18 +66,9 @@ class AggregatingPropagationCredentialsRepository(IPropagationCredentialsReposit @request_cache(CREDENTIALS_POLL_PERIOD_SEC) def _get_credentials_from_control_channel(self) -> Sequence[Credentials]: - """ - Fetches credentials from control channel - :return: Credentials that can be used for propagation - """ return self._control_channel.get_credentials_for_propagation() def _set_attribute(self, attribute_to_be_set: str, credentials_values: Iterable[Any]): - """ - Sets a value for a credential type - :param attribute_to_be_set: Key of self._stored_credentials (credential container key) - :param credentials_values: Value we want to set the container to - """ if not credentials_values: return diff --git a/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py b/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py index 7e5d20dab..eb5fb1cba 100644 --- a/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py +++ b/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py @@ -6,6 +6,10 @@ from infection_monkey.custom_types import PropagationCredentials class IPropagationCredentialsRepository(metaclass=abc.ABCMeta): + """ + Repository that stores and provides credentials for the Agent to use in propagation + """ + @abc.abstractmethod def add_credentials(self, credentials_to_add: Iterable[Credentials]): """ From aa32b5059aad2b90c783155fc8dc1ae6840391d6 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 8 Aug 2022 10:58:20 -0400 Subject: [PATCH 6/6] Agent: Remove superfluous types from IPropCredRepo docstrings --- .../i_propagation_credentials_repository.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py b/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py index eb5fb1cba..37eea79ab 100644 --- a/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py +++ b/monkey/infection_monkey/credential_repository/i_propagation_credentials_repository.py @@ -14,7 +14,7 @@ class IPropagationCredentialsRepository(metaclass=abc.ABCMeta): def add_credentials(self, credentials_to_add: Iterable[Credentials]): """ Adds credentials to the CredentialStore - :param Iterable[Credentials] credentials_to_add: The credentials that will be added + :param credentials_to_add: The credentials that will be added """ @abc.abstractmethod @@ -22,5 +22,4 @@ class IPropagationCredentialsRepository(metaclass=abc.ABCMeta): """ Retrieves credentials from the store :return: Credentials that can be used for propagation - :type: PropagationCredentials """