From 68fe2edd08901162b40f88770d25caa75016e81d Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 7 Jul 2022 17:34:49 +0200 Subject: [PATCH 01/20] Island: Init MongoCredentialsRepository --- .../mongo_credentials_repository.py | 66 +++++++++++++++++++ .../test_mongo_credentials_repository.py | 31 +++++++++ 2 files changed, 97 insertions(+) create mode 100644 monkey/monkey_island/cc/repository/mongo_credentials_repository.py create mode 100644 monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py diff --git a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py new file mode 100644 index 000000000..c124490a4 --- /dev/null +++ b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py @@ -0,0 +1,66 @@ +from typing import Sequence + +from common.credentials import Credentials +from monkey_island.cc.database import mongo +from monkey_island.cc.repository import RemovalError, RetrievalError, StorageError +from monkey_island.cc.repository.i_credentials_repository import ICredentialsRepository + + +class MongoCredentialsRepository(ICredentialsRepository): + """ + Store credentials in a mongo database that can be used to propagate around the network. + """ + + def get_configured_credentials(self) -> Sequence[Credentials]: + try: + configured_credentials = list(mongo.db.configured_credentials.find({})) + return configured_credentials + except Exception as err: + raise RetrievalError(err) + + def get_stolen_credentials(self) -> Sequence[Credentials]: + try: + stolen_credentials = list(mongo.db.stolen_credentials.find({})) + return stolen_credentials + except Exception as err: + raise RetrievalError(err) + + def get_all_credentials(self) -> Sequence[Credentials]: + try: + configured_credentials = self.get_configured_credentials() + stolen_credentials = self.get_stolen_credentials() + + return [*configured_credentials, *stolen_credentials] + except RetrievalError as err: + raise err + + def save_configured_credentials(self, credentials: Credentials): + try: + mongo.db.configured_credentials.insert_one(credentials) + except Exception as err: + raise StorageError(err) + + def save_stolen_credentials(self, credentials: Credentials): + try: + mongo.db.stolen_credentials.insert_one(credentials) + except Exception as err: + raise StorageError(err) + + def remove_configured_credentials(self): + try: + mongo.db.configured_credentials.remove({}) + except Exception as err: + raise RemovalError(err) + + def remove_stolen_credentials(self): + try: + mongo.db.stolen_credentials.remove({}) + except Exception as err: + raise RemovalError(err) + + def remove_all_credentials(self): + try: + self.remove_configured_credentials() + self.remove_stolen_credentials() + except RemovalError as err: + raise err diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py new file mode 100644 index 000000000..28b1c2eab --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py @@ -0,0 +1,31 @@ +import mongoengine +import pytest + +from monkey_island.cc.repository import MongoCredentialsRepository + + +@pytest.fixture +def fake_mongo(monkeypatch): + mongo = mongoengine.connection.get_connection() + monkeypatch.setattr("monkey_island.cc.repository.mongo_credentials_repository.mongo", mongo) + + +def test_mongo_repository_get_configured(fake_mongo): + + actual_configured_credentials = MongoCredentialsRepository().get_configured_credentials() + + assert actual_configured_credentials == [] + + +def test_mongo_repository_get_stolen(fake_mongo): + + actual_stolen_credentials = MongoCredentialsRepository().get_stolen_credentials() + + assert actual_stolen_credentials == [] + + +def test_mongo_repository_get_all(fake_mongo): + + actual_credentials = MongoCredentialsRepository().get_all_credentials() + + assert actual_credentials == [] From 7bcf49daf98166c93825f87bbaa49268062fe764 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 7 Jul 2022 13:06:38 -0400 Subject: [PATCH 02/20] Island: Modify ICredentialsRepository to save a Sequence of Credentials --- .../cc/repository/i_credentials_repository.py | 4 ++-- .../cc/repository/mongo_credentials_repository.py | 8 ++++---- .../stub_propagation_credentials_repository.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/repository/i_credentials_repository.py b/monkey/monkey_island/cc/repository/i_credentials_repository.py index 42fc3898f..381782533 100644 --- a/monkey/monkey_island/cc/repository/i_credentials_repository.py +++ b/monkey/monkey_island/cc/repository/i_credentials_repository.py @@ -43,7 +43,7 @@ class ICredentialsRepository(ABC): """ pass - def save_configured_credentials(self, credentials: Credentials): + def save_configured_credentials(self, credentials: Sequence[Credentials]): """ Save credentials that were configured. @@ -52,7 +52,7 @@ class ICredentialsRepository(ABC): """ pass - def save_stolen_credentials(self, credentials: Credentials): + def save_stolen_credentials(self, credentials: Sequence[Credentials]): """ Save credentials that were stolen during a simulation. diff --git a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py index c124490a4..df5d0e417 100644 --- a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py @@ -34,15 +34,15 @@ class MongoCredentialsRepository(ICredentialsRepository): except RetrievalError as err: raise err - def save_configured_credentials(self, credentials: Credentials): + def save_configured_credentials(self, credentials: Sequence[Credentials]): try: - mongo.db.configured_credentials.insert_one(credentials) + mongo.db.configured_credentials.insert_many(credentials) except Exception as err: raise StorageError(err) - def save_stolen_credentials(self, credentials: Credentials): + def save_stolen_credentials(self, credentials: Sequence[Credentials]): try: - mongo.db.stolen_credentials.insert_one(credentials) + mongo.db.stolen_credentials.insert_many(credentials) except Exception as err: raise StorageError(err) diff --git a/monkey/tests/monkey_island/stub_propagation_credentials_repository.py b/monkey/tests/monkey_island/stub_propagation_credentials_repository.py index 7bf6f85a7..7a2375c6c 100644 --- a/monkey/tests/monkey_island/stub_propagation_credentials_repository.py +++ b/monkey/tests/monkey_island/stub_propagation_credentials_repository.py @@ -47,10 +47,10 @@ class StubPropagationCredentialsRepository(ICredentialsRepository): Credentials.from_mapping(PROPAGATION_CREDENTIALS_2, monkey_guid="second_guid"), ] - def save_configured_credentials(self, credentials: Credentials): + def save_configured_credentials(self, credentials: Sequence[Credentials]): pass - def save_stolen_credentials(self, credentials: Credentials): + def save_stolen_credentials(self, credentials: Sequence[Credentials]): pass def remove_configured_credentials(self): From d0cb0d63cca55b2fc6139538ca3e560dfc25958b Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 7 Jul 2022 13:10:04 -0400 Subject: [PATCH 03/20] Island: Export MongoCredentialsRepository from repository package --- monkey/monkey_island/cc/repository/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monkey/monkey_island/cc/repository/__init__.py b/monkey/monkey_island/cc/repository/__init__.py index 344644f3c..9e1311c87 100644 --- a/monkey/monkey_island/cc/repository/__init__.py +++ b/monkey/monkey_island/cc/repository/__init__.py @@ -11,3 +11,4 @@ from .file_agent_configuration_repository import FileAgentConfigurationRepositor from .i_simulation_repository import ISimulationRepository from .file_simulation_repository import FileSimulationRepository from .i_credentials_repository import ICredentialsRepository +from .mongo_credentials_repository import MongoCredentialsRepository From 938aec9d49d917101d8c21090bce541793e68ad7 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 7 Jul 2022 13:11:21 -0400 Subject: [PATCH 04/20] Island: Reorder repository/__init.py Group the interfaces and implementations together to make it easier to understand what functionality has been implemented so far. --- monkey/monkey_island/cc/repository/__init__.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/repository/__init__.py b/monkey/monkey_island/cc/repository/__init__.py index 9e1311c87..d67515ee6 100644 --- a/monkey/monkey_island/cc/repository/__init__.py +++ b/monkey/monkey_island/cc/repository/__init__.py @@ -1,14 +1,19 @@ from .errors import RemovalError, RetrievalError, StorageError + + from .i_file_repository import FileNotFoundError, IFileRepository +from .i_agent_binary_repository import IAgentBinaryRepository +from .i_agent_configuration_repository import IAgentConfigurationRepository +from .i_simulation_repository import ISimulationRepository +from .i_credentials_repository import ICredentialsRepository + + from .local_storage_file_repository import LocalStorageFileRepository from .file_repository_caching_decorator import FileRepositoryCachingDecorator from .file_repository_locking_decorator import FileRepositoryLockingDecorator from .file_repository_logging_decorator import FileRepositoryLoggingDecorator -from .i_agent_binary_repository import IAgentBinaryRepository + from .agent_binary_repository import AgentBinaryRepository -from .i_agent_configuration_repository import IAgentConfigurationRepository from .file_agent_configuration_repository import FileAgentConfigurationRepository -from .i_simulation_repository import ISimulationRepository from .file_simulation_repository import FileSimulationRepository -from .i_credentials_repository import ICredentialsRepository from .mongo_credentials_repository import MongoCredentialsRepository From 77143ee76588ed6decd26c471b0050666a0f21fc Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 7 Jul 2022 15:33:45 -0400 Subject: [PATCH 05/20] Island: Use common.credentials.Credentials in resource --- .../cc/resources/propagation_credentials.py | 5 ++- ...stub_propagation_credentials_repository.py | 40 ++++++------------- .../resources/test_propagation_credentials.py | 9 +---- 3 files changed, 17 insertions(+), 37 deletions(-) diff --git a/monkey/monkey_island/cc/resources/propagation_credentials.py b/monkey/monkey_island/cc/resources/propagation_credentials.py index 8b14e9b1f..8d72fe427 100644 --- a/monkey/monkey_island/cc/resources/propagation_credentials.py +++ b/monkey/monkey_island/cc/resources/propagation_credentials.py @@ -1,5 +1,6 @@ -from flask import jsonify +from flask import make_response +from common.credentials import Credentials from monkey_island.cc.repository import ICredentialsRepository from monkey_island.cc.resources.AbstractResource import AbstractResource @@ -13,4 +14,4 @@ class PropagationCredentials(AbstractResource): def get(self): propagation_credentials = self._credentials_repository.get_all_credentials() - return jsonify(propagation_credentials) + return make_response(Credentials.to_json_array(propagation_credentials), 200) diff --git a/monkey/tests/monkey_island/stub_propagation_credentials_repository.py b/monkey/tests/monkey_island/stub_propagation_credentials_repository.py index 7a2375c6c..237d28b85 100644 --- a/monkey/tests/monkey_island/stub_propagation_credentials_repository.py +++ b/monkey/tests/monkey_island/stub_propagation_credentials_repository.py @@ -1,38 +1,25 @@ from typing import Sequence +from common.credentials import Credentials, LMHash, NTHash, Password, Username from monkey_island.cc.repository import ICredentialsRepository -from monkey_island.cc.services.telemetry.processing.credentials import Credentials fake_username = "m0nk3y_user" fake_special_username = "m0nk3y.user" -fake_nt_hash = "c1c58f96cdf212b50837bc11a00be47c" -fake_lm_hash = "299BD128C1101FD6" +fake_nt_hash = "C1C58F96CDF212B50837BC11A00BE47C" +fake_lm_hash = "299BD128C1101FD6299BD128C1101FD6" fake_password_1 = "trytostealthis" fake_password_2 = "password" fake_password_3 = "12345678" -PROPAGATION_CREDENTIALS_1 = { - "identities": [{"username": fake_username, "credential_type": "USERNAME"}], - "secrets": [ - {"nt_hash": fake_nt_hash, "credential_type": "NT_HASH"}, - {"lm_hash": fake_lm_hash, "credential_type": "LM_HASH"}, - {"password": fake_password_1, "credential_type": "PASSWORD"}, - ], -} - -PROPAGATION_CREDENTIALS_2 = { - "identities": [ - {"username": fake_username, "credential_type": "USERNAME"}, - {"username": fake_special_username, "credential_type": "USERNAME"}, - ], - "secrets": [ - {"password": fake_password_1, "credential_type": "PASSWORD"}, - {"password": fake_password_2, "credential_type": "PASSWORD"}, - {"password": fake_password_3, "credential_type": "PASSWORD"}, - ], -} +PROPAGATION_CREDENTIALS_1 = Credentials( + identities=(Username(fake_username),), + secrets=(NTHash(fake_nt_hash), LMHash(fake_lm_hash), Password(fake_password_1)), +) +PROPAGATION_CREDENTIALS_2 = Credentials( + identities=(Username(fake_username), Username(fake_special_username)), + secrets=(Password(fake_password_1), Password(fake_password_2), Password(fake_password_3)), +) -# TODO: Use Credentials from common.credentials when serialization is implemented class StubPropagationCredentialsRepository(ICredentialsRepository): def get_configured_credentials(self) -> Sequence[Credentials]: pass @@ -42,10 +29,7 @@ class StubPropagationCredentialsRepository(ICredentialsRepository): def get_all_credentials(self) -> Sequence[Credentials]: - return [ - Credentials.from_mapping(PROPAGATION_CREDENTIALS_1, monkey_guid="some_guid"), - Credentials.from_mapping(PROPAGATION_CREDENTIALS_2, monkey_guid="second_guid"), - ] + return [PROPAGATION_CREDENTIALS_1, PROPAGATION_CREDENTIALS_2] def save_configured_credentials(self, credentials: Sequence[Credentials]): pass diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py index 08b8e942f..728aef1c3 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py @@ -1,5 +1,3 @@ -import json - import pytest from tests.common import StubDIContainer from tests.monkey_island import ( @@ -9,6 +7,7 @@ from tests.monkey_island import ( ) from tests.unit_tests.monkey_island.conftest import get_url_for_resource +from common.credentials import Credentials from monkey_island.cc.repository import ICredentialsRepository from monkey_island.cc.resources.propagation_credentials import PropagationCredentials @@ -27,13 +26,9 @@ def test_propagation_credentials_endpoint_get(flask_client): propagation_credentials_url = get_url_for_resource(PropagationCredentials) resp = flask_client.get(propagation_credentials_url) + actual_propagation_credentials = Credentials.from_json_array(resp.text) assert resp.status_code == 200 - actual_propagation_credentials = json.loads(resp.data) assert len(actual_propagation_credentials) == 2 - - # TODO: delete the removal of monkey_guid key when the serialization of credentials - del actual_propagation_credentials[0]["monkey_guid"] assert actual_propagation_credentials[0] == PROPAGATION_CREDENTIALS_1 - del actual_propagation_credentials[1]["monkey_guid"] assert actual_propagation_credentials[1] == PROPAGATION_CREDENTIALS_2 From 7062aaf261e9d87d132e04616e4014f437a63de9 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 8 Jul 2022 17:26:23 +0200 Subject: [PATCH 06/20] Common: Add to_mapping in Credentials --- monkey/common/credentials/credentials.py | 10 ++++++++++ .../unit_tests/common/credentials/test_credentials.py | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/monkey/common/credentials/credentials.py b/monkey/common/credentials/credentials.py index 846137af1..e027f5bfc 100644 --- a/monkey/common/credentials/credentials.py +++ b/monkey/common/credentials/credentials.py @@ -200,3 +200,13 @@ class Credentials: :return: A JSON string representing an array of Credentials objects """ return "[" + ",".join([Credentials.to_json(c) for c in credentials]) + "]" + + @staticmethod + def to_mapping(credentials: Credentials) -> Mapping: + """ + Serialize a Credentials object to Mapping + + :param credentials: A Credentials object + :return: A mapping representing a Credentials object + """ + return CredentialsSchema().dump(credentials) diff --git a/monkey/tests/unit_tests/common/credentials/test_credentials.py b/monkey/tests/unit_tests/common/credentials/test_credentials.py index def11896b..ac8953096 100644 --- a/monkey/tests/unit_tests/common/credentials/test_credentials.py +++ b/monkey/tests/unit_tests/common/credentials/test_credentials.py @@ -56,6 +56,12 @@ def test_credentials_serialization_json(): assert json.loads(serialized_credentials) == CREDENTIALS_DICT +def test_credentials_serialization_mapping(): + serialized_credentials = Credentials.to_mapping(CREDENTIALS_OBJECT) + + assert serialized_credentials == CREDENTIALS_DICT + + def test_credentials_deserialization__from_mapping(): deserialized_credentials = Credentials.from_mapping(CREDENTIALS_DICT) From 8ff8ad1f17e73eb47740bb260d8704df3864e258 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 8 Jul 2022 17:27:05 +0200 Subject: [PATCH 07/20] Island: Modify MongoCredentialsRepository --- .../mongo_credentials_repository.py | 23 +++- .../test_mongo_credentials_repository.py | 115 ++++++++++++++++++ 2 files changed, 132 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py index df5d0e417..971a2bf5d 100644 --- a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py @@ -13,14 +13,23 @@ class MongoCredentialsRepository(ICredentialsRepository): def get_configured_credentials(self) -> Sequence[Credentials]: try: - configured_credentials = list(mongo.db.configured_credentials.find({})) + configured_credentials = [] + list_configured_credentials = list(mongo.db.configured_credentials.find({})) + for c in list_configured_credentials: + del c["_id"] + configured_credentials.append(Credentials.from_mapping(c)) + return configured_credentials except Exception as err: raise RetrievalError(err) def get_stolen_credentials(self) -> Sequence[Credentials]: try: - stolen_credentials = list(mongo.db.stolen_credentials.find({})) + stolen_credentials = [] + list_stolen_credentials = list(mongo.db.stolen_credentials.find({})) + for c in list_stolen_credentials: + del c["_id"] + stolen_credentials.append(Credentials.from_mapping(c)) return stolen_credentials except Exception as err: raise RetrievalError(err) @@ -36,25 +45,27 @@ class MongoCredentialsRepository(ICredentialsRepository): def save_configured_credentials(self, credentials: Sequence[Credentials]): try: - mongo.db.configured_credentials.insert_many(credentials) + for c in credentials: + mongo.db.configured_credentials.insert_one(Credentials.to_mapping(c)) except Exception as err: raise StorageError(err) def save_stolen_credentials(self, credentials: Sequence[Credentials]): try: - mongo.db.stolen_credentials.insert_many(credentials) + for c in credentials: + mongo.db.stolen_credentials.insert_one(Credentials.to_mapping(c)) except Exception as err: raise StorageError(err) def remove_configured_credentials(self): try: - mongo.db.configured_credentials.remove({}) + mongo.db.configured_credentials.delete_many({}) except Exception as err: raise RemovalError(err) def remove_stolen_credentials(self): try: - mongo.db.stolen_credentials.remove({}) + mongo.db.stolen_credentials.delete_many({}) except Exception as err: raise RemovalError(err) diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py index 28b1c2eab..8d32b961b 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py @@ -1,8 +1,47 @@ import mongoengine import pytest +from common.credentials import Credentials from monkey_island.cc.repository import MongoCredentialsRepository +USER1 = "test_user_1" +USER2 = "test_user_2" +USER3 = "test_user_3" +PASSWORD = "12435" +PASSWORD2 = "password" +PASSWORD3 = "lozinka" +LM_HASH = "AEBD4DE384C7EC43AAD3B435B51404EE" +NT_HASH = "7A21990FCD3D759941E45C490F143D5F" +PUBLIC_KEY = "MY_PUBLIC_KEY" +PRIVATE_KEY = "MY_PRIVATE_KEY" + +CREDENTIALS_DICT_1 = { + "identities": [ + {"credential_type": "USERNAME", "username": USER1}, + {"credential_type": "USERNAME", "username": USER2}, + ], + "secrets": [ + {"credential_type": "PASSWORD", "password": PASSWORD}, + {"credential_type": "LM_HASH", "lm_hash": LM_HASH}, + {"credential_type": "NT_HASH", "nt_hash": NT_HASH}, + { + "credential_type": "SSH_KEYPAIR", + "public_key": PUBLIC_KEY, + "private_key": PRIVATE_KEY, + }, + ], +} + +CREDENTIALS_DICT_2 = { + "identities": [ + {"credential_type": "USERNAME", "username": USER3}, + ], + "secrets": [ + {"credential_type": "PASSWORD", "password": PASSWORD2}, + {"credential_type": "PASSWORD", "password": PASSWORD3}, + ], +} + @pytest.fixture def fake_mongo(monkeypatch): @@ -29,3 +68,79 @@ def test_mongo_repository_get_all(fake_mongo): actual_credentials = MongoCredentialsRepository().get_all_credentials() assert actual_credentials == [] + + +def test_mongo_repository_configured(fake_mongo): + + credentials = [ + Credentials.from_mapping(CREDENTIALS_DICT_1), + Credentials.from_mapping(CREDENTIALS_DICT_2), + ] + + mongo_repository = MongoCredentialsRepository() + + mongo_repository.save_configured_credentials(credentials) + + actual_configured_credentials = mongo_repository.get_configured_credentials() + + assert actual_configured_credentials == credentials + + mongo_repository.remove_configured_credentials() + + actual_configured_credentials = mongo_repository.get_configured_credentials() + + assert actual_configured_credentials == [] + + +def test_mongo_repository_stolen(fake_mongo): + + stolen_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_1)] + + configured_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_2)] + + mongo_repository = MongoCredentialsRepository() + + mongo_repository.save_configured_credentials(configured_credentials) + mongo_repository.save_stolen_credentials(stolen_credentials) + + actual_stolen_credentials = mongo_repository.get_stolen_credentials() + + assert actual_stolen_credentials == stolen_credentials + + mongo_repository.remove_stolen_credentials() + + actual_stolen_credentials = mongo_repository.get_stolen_credentials() + + assert actual_stolen_credentials == [] + + # Must remove configured also for the next tests + mongo_repository.remove_configured_credentials() + + +def test_mongo_repository_all(fake_mongo): + + configured_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_1)] + stolen_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_2)] + all_credentials = [ + Credentials.from_mapping(CREDENTIALS_DICT_1), + Credentials.from_mapping(CREDENTIALS_DICT_2), + ] + + mongo_repository = MongoCredentialsRepository() + + mongo_repository.save_configured_credentials(configured_credentials) + mongo_repository.save_stolen_credentials(stolen_credentials) + + actual_credentials = mongo_repository.get_all_credentials() + + assert actual_credentials == all_credentials + + mongo_repository.remove_all_credentials() + + actual_credentials = mongo_repository.get_all_credentials() + actual_stolen_credentials = mongo_repository.get_stolen_credentials() + actual_configured_credentials = mongo_repository.get_configured_credentials() + + assert actual_credentials == [] + assert actual_stolen_credentials == [] + assert actual_configured_credentials == [] From 45a66932637fbaf6f6536c25c6dbb5cbf36a2e9b Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 8 Jul 2022 17:34:45 +0200 Subject: [PATCH 08/20] Island: Register MongoCredentialsRepository in DI container --- monkey/monkey_island/cc/services/initialize.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index b5a16ec9f..9642b29de 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -18,9 +18,11 @@ from monkey_island.cc.repository import ( FileSimulationRepository, IAgentBinaryRepository, IAgentConfigurationRepository, + ICredentialsRepository, IFileRepository, ISimulationRepository, LocalStorageFileRepository, + MongoCredentialsRepository, RetrievalError, ) from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH @@ -73,6 +75,9 @@ def _register_repositories(container: DIContainer, data_dir: Path): IAgentConfigurationRepository, container.resolve(FileAgentConfigurationRepository) ) container.register_instance(ISimulationRepository, container.resolve(FileSimulationRepository)) + container.register_instance( + ICredentialsRepository, container.resolve(MongoCredentialsRepository) + ) def _decorate_file_repository(file_repository: IFileRepository) -> IFileRepository: From c808d5094866a7bf7f7958a17750bc6989b04fe3 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 8 Jul 2022 17:50:18 +0200 Subject: [PATCH 09/20] Island: Add note to fix duplication of Credentials in database --- .../cc/repository/mongo_credentials_repository.py | 3 +++ .../cc/repository/test_mongo_credentials_repository.py | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py index 971a2bf5d..4ef4acda2 100644 --- a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py @@ -30,6 +30,7 @@ class MongoCredentialsRepository(ICredentialsRepository): for c in list_stolen_credentials: del c["_id"] stolen_credentials.append(Credentials.from_mapping(c)) + return stolen_credentials except Exception as err: raise RetrievalError(err) @@ -44,6 +45,7 @@ class MongoCredentialsRepository(ICredentialsRepository): raise err def save_configured_credentials(self, credentials: Sequence[Credentials]): + # TODO: Fix deduplication of Credentials in mongo try: for c in credentials: mongo.db.configured_credentials.insert_one(Credentials.to_mapping(c)) @@ -51,6 +53,7 @@ class MongoCredentialsRepository(ICredentialsRepository): raise StorageError(err) def save_stolen_credentials(self, credentials: Sequence[Credentials]): + # TODO: Fix deduplication of Credentials in mongo try: for c in credentials: mongo.db.stolen_credentials.insert_one(Credentials.to_mapping(c)) diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py index 8d32b961b..0a96bc9c6 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py @@ -78,7 +78,6 @@ def test_mongo_repository_configured(fake_mongo): ] mongo_repository = MongoCredentialsRepository() - mongo_repository.save_configured_credentials(credentials) actual_configured_credentials = mongo_repository.get_configured_credentials() @@ -99,7 +98,6 @@ def test_mongo_repository_stolen(fake_mongo): configured_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_2)] mongo_repository = MongoCredentialsRepository() - mongo_repository.save_configured_credentials(configured_credentials) mongo_repository.save_stolen_credentials(stolen_credentials) @@ -127,7 +125,6 @@ def test_mongo_repository_all(fake_mongo): ] mongo_repository = MongoCredentialsRepository() - mongo_repository.save_configured_credentials(configured_credentials) mongo_repository.save_stolen_credentials(stolen_credentials) From c48b38fb01a56bd725dd19a41dd2526ab7f59bf2 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 8 Jul 2022 19:20:25 +0200 Subject: [PATCH 10/20] Island: Refactor MongoCredentialsRepository * Remove code duplication * Init with PyMongo object --- .../mongo_credentials_repository.py | 58 +++++++----- .../test_mongo_credentials_repository.py | 90 +++++++++---------- 2 files changed, 78 insertions(+), 70 deletions(-) diff --git a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py index 4ef4acda2..b0acf405e 100644 --- a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py @@ -1,7 +1,8 @@ from typing import Sequence +from flask_pymongo import PyMongo + from common.credentials import Credentials -from monkey_island.cc.database import mongo from monkey_island.cc.repository import RemovalError, RetrievalError, StorageError from monkey_island.cc.repository.i_credentials_repository import ICredentialsRepository @@ -11,27 +12,23 @@ class MongoCredentialsRepository(ICredentialsRepository): Store credentials in a mongo database that can be used to propagate around the network. """ + def __init__(self, mongo_db: PyMongo): + self._mongo = mongo_db + def get_configured_credentials(self) -> Sequence[Credentials]: try: - configured_credentials = [] - list_configured_credentials = list(mongo.db.configured_credentials.find({})) - for c in list_configured_credentials: - del c["_id"] - configured_credentials.append(Credentials.from_mapping(c)) - return configured_credentials + return MongoCredentialsRepository._get_credentials_from_collection( + self._mongo.db.configured_credentials + ) except Exception as err: raise RetrievalError(err) def get_stolen_credentials(self) -> Sequence[Credentials]: try: - stolen_credentials = [] - list_stolen_credentials = list(mongo.db.stolen_credentials.find({})) - for c in list_stolen_credentials: - del c["_id"] - stolen_credentials.append(Credentials.from_mapping(c)) - - return stolen_credentials + return MongoCredentialsRepository._get_credentials_from_collection( + self._mongo.db.stolen_credentials + ) except Exception as err: raise RetrievalError(err) @@ -47,28 +44,30 @@ class MongoCredentialsRepository(ICredentialsRepository): def save_configured_credentials(self, credentials: Sequence[Credentials]): # TODO: Fix deduplication of Credentials in mongo try: - for c in credentials: - mongo.db.configured_credentials.insert_one(Credentials.to_mapping(c)) + MongoCredentialsRepository._save_credentials_to_collection( + credentials, self._mongo.db.configured_credentials + ) except Exception as err: raise StorageError(err) def save_stolen_credentials(self, credentials: Sequence[Credentials]): # TODO: Fix deduplication of Credentials in mongo try: - for c in credentials: - mongo.db.stolen_credentials.insert_one(Credentials.to_mapping(c)) + MongoCredentialsRepository._save_credentials_to_collection( + credentials, self._mongo.db.stolen_credentials + ) except Exception as err: raise StorageError(err) def remove_configured_credentials(self): try: - mongo.db.configured_credentials.delete_many({}) + MongoCredentialsRepository._delete_collection(self._mongo.db.configured_credentials) except Exception as err: raise RemovalError(err) def remove_stolen_credentials(self): try: - mongo.db.stolen_credentials.delete_many({}) + MongoCredentialsRepository._delete_collection(self._mongo.db.stolen_credentials) except Exception as err: raise RemovalError(err) @@ -78,3 +77,22 @@ class MongoCredentialsRepository(ICredentialsRepository): self.remove_stolen_credentials() except RemovalError as err: raise err + + @staticmethod + def _get_credentials_from_collection(collection) -> Sequence[Credentials]: + collection_result = [] + list_collection_result = list(collection.find({})) + for c in list_collection_result: + del c["_id"] + collection_result.append(Credentials.from_mapping(c)) + + return collection_result + + @staticmethod + def _save_credentials_to_collection(credentials: Sequence[Credentials], collection): + for c in credentials: + collection.insert_one(Credentials.to_mapping(c)) + + @staticmethod + def _delete_collection(collection): + collection.delete_many({}) diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py index 0a96bc9c6..8148780d3 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py @@ -42,101 +42,91 @@ CREDENTIALS_DICT_2 = { ], } +CONFIGURED_CREDENTIALS = [Credentials.from_mapping(CREDENTIALS_DICT_1)] + +STOLEN_CREDENTIALS = [Credentials.from_mapping(CREDENTIALS_DICT_2)] + +CREDENTIALS_LIST = [ + Credentials.from_mapping(CREDENTIALS_DICT_1), + Credentials.from_mapping(CREDENTIALS_DICT_2), +] + @pytest.fixture -def fake_mongo(monkeypatch): +def fake_mongo_repository(monkeypatch): mongo = mongoengine.connection.get_connection() - monkeypatch.setattr("monkey_island.cc.repository.mongo_credentials_repository.mongo", mongo) + return MongoCredentialsRepository(mongo) -def test_mongo_repository_get_configured(fake_mongo): +def test_mongo_repository_get_configured(fake_mongo_repository): - actual_configured_credentials = MongoCredentialsRepository().get_configured_credentials() + actual_configured_credentials = fake_mongo_repository.get_configured_credentials() assert actual_configured_credentials == [] -def test_mongo_repository_get_stolen(fake_mongo): +def test_mongo_repository_get_stolen(fake_mongo_repository): - actual_stolen_credentials = MongoCredentialsRepository().get_stolen_credentials() + actual_stolen_credentials = fake_mongo_repository.get_stolen_credentials() assert actual_stolen_credentials == [] -def test_mongo_repository_get_all(fake_mongo): +def test_mongo_repository_get_all(fake_mongo_repository): - actual_credentials = MongoCredentialsRepository().get_all_credentials() + actual_credentials = fake_mongo_repository.get_all_credentials() assert actual_credentials == [] -def test_mongo_repository_configured(fake_mongo): +def test_mongo_repository_configured(fake_mongo_repository): - credentials = [ - Credentials.from_mapping(CREDENTIALS_DICT_1), - Credentials.from_mapping(CREDENTIALS_DICT_2), - ] + fake_mongo_repository.save_configured_credentials(CREDENTIALS_LIST) - mongo_repository = MongoCredentialsRepository() - mongo_repository.save_configured_credentials(credentials) + actual_configured_credentials = fake_mongo_repository.get_configured_credentials() - actual_configured_credentials = mongo_repository.get_configured_credentials() + assert actual_configured_credentials == CREDENTIALS_LIST - assert actual_configured_credentials == credentials + fake_mongo_repository.remove_configured_credentials() - mongo_repository.remove_configured_credentials() - - actual_configured_credentials = mongo_repository.get_configured_credentials() + actual_configured_credentials = fake_mongo_repository.get_configured_credentials() assert actual_configured_credentials == [] -def test_mongo_repository_stolen(fake_mongo): +def test_mongo_repository_stolen(fake_mongo_repository): - stolen_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_1)] + fake_mongo_repository.save_configured_credentials(CONFIGURED_CREDENTIALS) + fake_mongo_repository.save_stolen_credentials(STOLEN_CREDENTIALS) - configured_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_2)] + actual_stolen_credentials = fake_mongo_repository.get_stolen_credentials() - mongo_repository = MongoCredentialsRepository() - mongo_repository.save_configured_credentials(configured_credentials) - mongo_repository.save_stolen_credentials(stolen_credentials) + assert actual_stolen_credentials == STOLEN_CREDENTIALS - actual_stolen_credentials = mongo_repository.get_stolen_credentials() + fake_mongo_repository.remove_stolen_credentials() - assert actual_stolen_credentials == stolen_credentials - - mongo_repository.remove_stolen_credentials() - - actual_stolen_credentials = mongo_repository.get_stolen_credentials() + actual_stolen_credentials = fake_mongo_repository.get_stolen_credentials() assert actual_stolen_credentials == [] # Must remove configured also for the next tests - mongo_repository.remove_configured_credentials() + fake_mongo_repository.remove_configured_credentials() -def test_mongo_repository_all(fake_mongo): +def test_mongo_repository_all(fake_mongo_repository): - configured_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_1)] - stolen_credentials = [Credentials.from_mapping(CREDENTIALS_DICT_2)] - all_credentials = [ - Credentials.from_mapping(CREDENTIALS_DICT_1), - Credentials.from_mapping(CREDENTIALS_DICT_2), - ] + fake_mongo_repository.save_configured_credentials(CONFIGURED_CREDENTIALS) + fake_mongo_repository.save_stolen_credentials(STOLEN_CREDENTIALS) - mongo_repository = MongoCredentialsRepository() - mongo_repository.save_configured_credentials(configured_credentials) - mongo_repository.save_stolen_credentials(stolen_credentials) + actual_credentials = fake_mongo_repository.get_all_credentials() - actual_credentials = mongo_repository.get_all_credentials() + assert actual_credentials == CREDENTIALS_LIST - assert actual_credentials == all_credentials + fake_mongo_repository.remove_all_credentials() - mongo_repository.remove_all_credentials() - - actual_credentials = mongo_repository.get_all_credentials() - actual_stolen_credentials = mongo_repository.get_stolen_credentials() - actual_configured_credentials = mongo_repository.get_configured_credentials() + actual_credentials = fake_mongo_repository.get_all_credentials() + actual_stolen_credentials = fake_mongo_repository.get_stolen_credentials() + actual_configured_credentials = fake_mongo_repository.get_configured_credentials() assert actual_credentials == [] assert actual_stolen_credentials == [] From 4226cb5b9ee4bb6a31f4c1bd71d626a499424a4e Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 11 Jul 2022 12:25:08 +0200 Subject: [PATCH 11/20] Island: Move error handling to private methods in MongoCredentialsRepository --- .../mongo_credentials_repository.py | 97 ++++++++----------- 1 file changed, 40 insertions(+), 57 deletions(-) diff --git a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py index b0acf405e..4d9566420 100644 --- a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py @@ -1,6 +1,6 @@ from typing import Sequence -from flask_pymongo import PyMongo +from pymongo import MongoClient from common.credentials import Credentials from monkey_island.cc.repository import RemovalError, RetrievalError, StorageError @@ -12,87 +12,70 @@ class MongoCredentialsRepository(ICredentialsRepository): Store credentials in a mongo database that can be used to propagate around the network. """ - def __init__(self, mongo_db: PyMongo): - self._mongo = mongo_db + def __init__(self, mongo: MongoClient): + self._mongo = mongo def get_configured_credentials(self) -> Sequence[Credentials]: - try: - - return MongoCredentialsRepository._get_credentials_from_collection( - self._mongo.db.configured_credentials - ) - except Exception as err: - raise RetrievalError(err) + return MongoCredentialsRepository._get_credentials_from_collection( + self._mongo.db.configured_credentials + ) def get_stolen_credentials(self) -> Sequence[Credentials]: - try: - return MongoCredentialsRepository._get_credentials_from_collection( - self._mongo.db.stolen_credentials - ) - except Exception as err: - raise RetrievalError(err) + return MongoCredentialsRepository._get_credentials_from_collection( + self._mongo.db.stolen_credentials + ) def get_all_credentials(self) -> Sequence[Credentials]: - try: - configured_credentials = self.get_configured_credentials() - stolen_credentials = self.get_stolen_credentials() + configured_credentials = self.get_configured_credentials() + stolen_credentials = self.get_stolen_credentials() - return [*configured_credentials, *stolen_credentials] - except RetrievalError as err: - raise err + return [*configured_credentials, *stolen_credentials] def save_configured_credentials(self, credentials: Sequence[Credentials]): # TODO: Fix deduplication of Credentials in mongo - try: - MongoCredentialsRepository._save_credentials_to_collection( - credentials, self._mongo.db.configured_credentials - ) - except Exception as err: - raise StorageError(err) + MongoCredentialsRepository._save_credentials_to_collection( + credentials, self._mongo.db.configured_credentials + ) def save_stolen_credentials(self, credentials: Sequence[Credentials]): - # TODO: Fix deduplication of Credentials in mongo - try: - MongoCredentialsRepository._save_credentials_to_collection( - credentials, self._mongo.db.stolen_credentials - ) - except Exception as err: - raise StorageError(err) + MongoCredentialsRepository._save_credentials_to_collection( + credentials, self._mongo.db.stolen_credentials + ) def remove_configured_credentials(self): - try: - MongoCredentialsRepository._delete_collection(self._mongo.db.configured_credentials) - except Exception as err: - raise RemovalError(err) + MongoCredentialsRepository._delete_collection(self._mongo.db.configured_credentials) def remove_stolen_credentials(self): - try: - MongoCredentialsRepository._delete_collection(self._mongo.db.stolen_credentials) - except Exception as err: - raise RemovalError(err) + MongoCredentialsRepository._delete_collection(self._mongo.db.stolen_credentials) def remove_all_credentials(self): - try: - self.remove_configured_credentials() - self.remove_stolen_credentials() - except RemovalError as err: - raise err + self.remove_configured_credentials() + self.remove_stolen_credentials() @staticmethod def _get_credentials_from_collection(collection) -> Sequence[Credentials]: - collection_result = [] - list_collection_result = list(collection.find({})) - for c in list_collection_result: - del c["_id"] - collection_result.append(Credentials.from_mapping(c)) + try: + collection_result = [] + list_collection_result = list(collection.find({})) + for c in list_collection_result: + del c["_id"] + collection_result.append(Credentials.from_mapping(c)) - return collection_result + return collection_result + except Exception as err: + raise RetrievalError(err) @staticmethod def _save_credentials_to_collection(credentials: Sequence[Credentials], collection): - for c in credentials: - collection.insert_one(Credentials.to_mapping(c)) + try: + for c in credentials: + collection.insert_one(Credentials.to_mapping(c)) + except Exception as err: + raise StorageError(err) @staticmethod def _delete_collection(collection): - collection.delete_many({}) + try: + collection.delete_many({}) + except RemovalError as err: + raise err From 1b1bd7fcc2b31a0993aecb3ec3aa852248aac460 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 11 Jul 2022 12:26:00 +0200 Subject: [PATCH 12/20] UT: Use mongomock in MongoCredentialsRepository --- .../test_mongo_credentials_repository.py | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py index 8148780d3..6debde1bb 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py @@ -1,4 +1,4 @@ -import mongoengine +import mongomock import pytest from common.credentials import Credentials @@ -53,80 +53,81 @@ CREDENTIALS_LIST = [ @pytest.fixture -def fake_mongo_repository(monkeypatch): - mongo = mongoengine.connection.get_connection() +def mongo_repository(): + mongo = mongomock.MongoClient() + return MongoCredentialsRepository(mongo) -def test_mongo_repository_get_configured(fake_mongo_repository): +def test_mongo_repository_get_configured(mongo_repository): - actual_configured_credentials = fake_mongo_repository.get_configured_credentials() + actual_configured_credentials = mongo_repository.get_configured_credentials() assert actual_configured_credentials == [] -def test_mongo_repository_get_stolen(fake_mongo_repository): +def test_mongo_repository_get_stolen(mongo_repository): - actual_stolen_credentials = fake_mongo_repository.get_stolen_credentials() + actual_stolen_credentials = mongo_repository.get_stolen_credentials() assert actual_stolen_credentials == [] -def test_mongo_repository_get_all(fake_mongo_repository): +def test_mongo_repository_get_all(mongo_repository): - actual_credentials = fake_mongo_repository.get_all_credentials() + actual_credentials = mongo_repository.get_all_credentials() assert actual_credentials == [] -def test_mongo_repository_configured(fake_mongo_repository): +def test_mongo_repository_configured(mongo_repository): - fake_mongo_repository.save_configured_credentials(CREDENTIALS_LIST) + mongo_repository.save_configured_credentials(CREDENTIALS_LIST) - actual_configured_credentials = fake_mongo_repository.get_configured_credentials() + actual_configured_credentials = mongo_repository.get_configured_credentials() assert actual_configured_credentials == CREDENTIALS_LIST - fake_mongo_repository.remove_configured_credentials() + mongo_repository.remove_configured_credentials() - actual_configured_credentials = fake_mongo_repository.get_configured_credentials() + actual_configured_credentials = mongo_repository.get_configured_credentials() assert actual_configured_credentials == [] -def test_mongo_repository_stolen(fake_mongo_repository): +def test_mongo_repository_stolen(mongo_repository): - fake_mongo_repository.save_configured_credentials(CONFIGURED_CREDENTIALS) - fake_mongo_repository.save_stolen_credentials(STOLEN_CREDENTIALS) + mongo_repository.save_configured_credentials(CONFIGURED_CREDENTIALS) + mongo_repository.save_stolen_credentials(STOLEN_CREDENTIALS) - actual_stolen_credentials = fake_mongo_repository.get_stolen_credentials() + actual_stolen_credentials = mongo_repository.get_stolen_credentials() assert actual_stolen_credentials == STOLEN_CREDENTIALS - fake_mongo_repository.remove_stolen_credentials() + mongo_repository.remove_stolen_credentials() - actual_stolen_credentials = fake_mongo_repository.get_stolen_credentials() + actual_stolen_credentials = mongo_repository.get_stolen_credentials() assert actual_stolen_credentials == [] # Must remove configured also for the next tests - fake_mongo_repository.remove_configured_credentials() + mongo_repository.remove_configured_credentials() -def test_mongo_repository_all(fake_mongo_repository): +def test_mongo_repository_all(mongo_repository): - fake_mongo_repository.save_configured_credentials(CONFIGURED_CREDENTIALS) - fake_mongo_repository.save_stolen_credentials(STOLEN_CREDENTIALS) + mongo_repository.save_configured_credentials(CONFIGURED_CREDENTIALS) + mongo_repository.save_stolen_credentials(STOLEN_CREDENTIALS) - actual_credentials = fake_mongo_repository.get_all_credentials() + actual_credentials = mongo_repository.get_all_credentials() assert actual_credentials == CREDENTIALS_LIST - fake_mongo_repository.remove_all_credentials() + mongo_repository.remove_all_credentials() - actual_credentials = fake_mongo_repository.get_all_credentials() - actual_stolen_credentials = fake_mongo_repository.get_stolen_credentials() - actual_configured_credentials = fake_mongo_repository.get_configured_credentials() + actual_credentials = mongo_repository.get_all_credentials() + actual_stolen_credentials = mongo_repository.get_stolen_credentials() + actual_configured_credentials = mongo_repository.get_configured_credentials() assert actual_credentials == [] assert actual_stolen_credentials == [] From efb12df483bbfd268bbd05ace3162f3d777ba30c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 11 Jul 2022 12:41:23 +0200 Subject: [PATCH 13/20] Island: Build MongoCredentialsRepository with a mongo connection --- monkey/monkey_island/cc/services/initialize.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index 9642b29de..a06d3635a 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -1,6 +1,8 @@ import logging from pathlib import Path +from pymongo import MongoClient + from common import DIContainer from common.aws import AWSInstance from common.configuration import ( @@ -29,6 +31,7 @@ from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.services import AWSService, IslandModeService from monkey_island.cc.services.post_breach_files import PostBreachFilesService from monkey_island.cc.services.run_local_monkey import LocalMonkeyRunService +from monkey_island.cc.setup.mongo.mongo_setup import MONGO_URL from . import AuthenticationService, JsonFileUserDatastore from .reporting.report import ReportService @@ -75,9 +78,7 @@ def _register_repositories(container: DIContainer, data_dir: Path): IAgentConfigurationRepository, container.resolve(FileAgentConfigurationRepository) ) container.register_instance(ISimulationRepository, container.resolve(FileSimulationRepository)) - container.register_instance( - ICredentialsRepository, container.resolve(MongoCredentialsRepository) - ) + container.register_instance(ICredentialsRepository, _build_mongo_credentials_repository()) def _decorate_file_repository(file_repository: IFileRepository) -> IFileRepository: @@ -95,6 +96,13 @@ def _build_agent_binary_repository(): return agent_binary_repository +def _build_mongo_credentials_repository(): + mongo = MongoClient(MONGO_URL, serverSelectionTimeoutMS=100) + + mongo_credentials_repository = MongoCredentialsRepository(mongo) + return mongo_credentials_repository + + def _log_agent_binary_hashes(agent_binary_repository: IAgentBinaryRepository): """ Logs all the hashes of the agent executables for debbuging ease From 8a7e89e733ac4010a234fa660d01cdb88d5363a1 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 11 Jul 2022 07:58:18 -0400 Subject: [PATCH 14/20] Common: Fix minor grammar mistake --- monkey/common/credentials/credentials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/common/credentials/credentials.py b/monkey/common/credentials/credentials.py index e027f5bfc..9fdf78eab 100644 --- a/monkey/common/credentials/credentials.py +++ b/monkey/common/credentials/credentials.py @@ -204,7 +204,7 @@ class Credentials: @staticmethod def to_mapping(credentials: Credentials) -> Mapping: """ - Serialize a Credentials object to Mapping + Serialize a Credentials object to a Mapping :param credentials: A Credentials object :return: A mapping representing a Credentials object From 088ec94269fdbc8eefb78c1eb7d62b16bfc36a7e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 11 Jul 2022 08:02:20 -0400 Subject: [PATCH 15/20] Island: Rename _delete_collection -> _remove_credentials_fom_collection --- .../cc/repository/mongo_credentials_repository.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py index 4d9566420..454196ee8 100644 --- a/monkey/monkey_island/cc/repository/mongo_credentials_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_credentials_repository.py @@ -43,10 +43,14 @@ class MongoCredentialsRepository(ICredentialsRepository): ) def remove_configured_credentials(self): - MongoCredentialsRepository._delete_collection(self._mongo.db.configured_credentials) + MongoCredentialsRepository._remove_credentials_fom_collection( + self._mongo.db.configured_credentials + ) def remove_stolen_credentials(self): - MongoCredentialsRepository._delete_collection(self._mongo.db.stolen_credentials) + MongoCredentialsRepository._remove_credentials_fom_collection( + self._mongo.db.stolen_credentials + ) def remove_all_credentials(self): self.remove_configured_credentials() @@ -74,7 +78,7 @@ class MongoCredentialsRepository(ICredentialsRepository): raise StorageError(err) @staticmethod - def _delete_collection(collection): + def _remove_credentials_fom_collection(collection): try: collection.delete_many({}) except RemovalError as err: From 0bddef7078318b99a2a9735c6cfae039d5ccd875 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 11 Jul 2022 08:10:58 -0400 Subject: [PATCH 16/20] Island: Add pymongo as an explicit dependency --- monkey/monkey_island/Pipfile | 1 + monkey/monkey_island/Pipfile.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/Pipfile b/monkey/monkey_island/Pipfile index 052a46039..a5e698168 100644 --- a/monkey/monkey_island/Pipfile +++ b/monkey/monkey_island/Pipfile @@ -31,6 +31,7 @@ pefile = {version = "*", sys_platform = "== 'win32'"} # Pyinstaller requirement marshmallow = "*" marshmallow-enum = "*" readerwriterlock = "*" +pymongo = "*" [dev-packages] virtualenv = ">=20.0.26" diff --git a/monkey/monkey_island/Pipfile.lock b/monkey/monkey_island/Pipfile.lock index 67647710a..34c656edf 100644 --- a/monkey/monkey_island/Pipfile.lock +++ b/monkey/monkey_island/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "91b8cfcf1408b3709300f47d420c550fe355df76ad396e455049fef1cceca3ad" + "sha256": "4dae5f6c39b19f146ce303e0e1b6c4a52f45128e44b07ceefacf9d8c493d00a2" }, "pipfile-spec": 6, "requires": { From b7786e2d0ac5afcf7c1ce0fc981e5d085c10e56a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 11 Jul 2022 14:24:57 +0200 Subject: [PATCH 17/20] Island: Register mongo client in DI container --- monkey/monkey_island/cc/services/initialize.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index a06d3635a..5c951eca3 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -66,6 +66,9 @@ def _register_conventions(container: DIContainer, data_dir: Path): "default_ransomware_agent_configuration", DEFAULT_RANSOMWARE_AGENT_CONFIGURATION, ) + container.register_convention( + MongoClient, "mongo", MongoClient(MONGO_URL, serverSelectionTimeoutMS=100) + ) def _register_repositories(container: DIContainer, data_dir: Path): @@ -78,7 +81,9 @@ def _register_repositories(container: DIContainer, data_dir: Path): IAgentConfigurationRepository, container.resolve(FileAgentConfigurationRepository) ) container.register_instance(ISimulationRepository, container.resolve(FileSimulationRepository)) - container.register_instance(ICredentialsRepository, _build_mongo_credentials_repository()) + container.register_instance( + ICredentialsRepository, container.resolve(MongoCredentialsRepository) + ) def _decorate_file_repository(file_repository: IFileRepository) -> IFileRepository: @@ -96,13 +101,6 @@ def _build_agent_binary_repository(): return agent_binary_repository -def _build_mongo_credentials_repository(): - mongo = MongoClient(MONGO_URL, serverSelectionTimeoutMS=100) - - mongo_credentials_repository = MongoCredentialsRepository(mongo) - return mongo_credentials_repository - - def _log_agent_binary_hashes(agent_binary_repository: IAgentBinaryRepository): """ Logs all the hashes of the agent executables for debbuging ease From 14cbf07a21fac2dc0e2e6be843224e8d9aaaed3d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 11 Jul 2022 09:02:57 -0400 Subject: [PATCH 18/20] Island: Register mongo client as instance, not convention --- monkey/monkey_island/cc/services/initialize.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index 5c951eca3..d51383132 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -44,7 +44,10 @@ AGENT_BINARIES_PATH = Path(MONKEY_ISLAND_ABS_PATH) / "cc" / "binaries" def initialize_services(data_dir: Path) -> DIContainer: container = DIContainer() _register_conventions(container, data_dir) + container.register_instance(AWSInstance, AWSInstance()) + container.register_instance(MongoClient, MongoClient(MONGO_URL, serverSelectionTimeoutMS=100)) + _register_repositories(container, data_dir) _register_services(container) @@ -66,9 +69,6 @@ def _register_conventions(container: DIContainer, data_dir: Path): "default_ransomware_agent_configuration", DEFAULT_RANSOMWARE_AGENT_CONFIGURATION, ) - container.register_convention( - MongoClient, "mongo", MongoClient(MONGO_URL, serverSelectionTimeoutMS=100) - ) def _register_repositories(container: DIContainer, data_dir: Path): From 4dd4b38fc952ef5c6e4e456a1b65a68c88ce2f6d Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 11 Jul 2022 15:03:37 +0200 Subject: [PATCH 19/20] UT: Use Credentials object instead of dict in MongoCredentialsRepository --- .../test_mongo_credentials_repository.py | 48 +++++++------------ 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py index 6debde1bb..e939a4c89 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py @@ -1,7 +1,7 @@ import mongomock import pytest -from common.credentials import Credentials +from common.credentials import Credentials, LMHash, NTHash, Password, SSHKeypair, Username from monkey_island.cc.repository import MongoCredentialsRepository USER1 = "test_user_1" @@ -15,41 +15,25 @@ NT_HASH = "7A21990FCD3D759941E45C490F143D5F" PUBLIC_KEY = "MY_PUBLIC_KEY" PRIVATE_KEY = "MY_PRIVATE_KEY" -CREDENTIALS_DICT_1 = { - "identities": [ - {"credential_type": "USERNAME", "username": USER1}, - {"credential_type": "USERNAME", "username": USER2}, - ], - "secrets": [ - {"credential_type": "PASSWORD", "password": PASSWORD}, - {"credential_type": "LM_HASH", "lm_hash": LM_HASH}, - {"credential_type": "NT_HASH", "nt_hash": NT_HASH}, - { - "credential_type": "SSH_KEYPAIR", - "public_key": PUBLIC_KEY, - "private_key": PRIVATE_KEY, - }, - ], -} +IDENTITIES_1 = (Username(USER1), Username(USER2)) +SECRETS_1 = ( + Password(PASSWORD), + LMHash(LM_HASH), + NTHash(NT_HASH), + SSHKeypair(PRIVATE_KEY, PUBLIC_KEY), +) +CREDENTIALS_OBJECT_1 = Credentials(IDENTITIES_1, SECRETS_1) -CREDENTIALS_DICT_2 = { - "identities": [ - {"credential_type": "USERNAME", "username": USER3}, - ], - "secrets": [ - {"credential_type": "PASSWORD", "password": PASSWORD2}, - {"credential_type": "PASSWORD", "password": PASSWORD3}, - ], -} +IDENTITIES_2 = (Username(USER3),) +SECRETS_2 = (Password(PASSWORD2), Password(PASSWORD3)) +CREDENTIALS_OBJECT_2 = Credentials(IDENTITIES_2, SECRETS_2) -CONFIGURED_CREDENTIALS = [Credentials.from_mapping(CREDENTIALS_DICT_1)] -STOLEN_CREDENTIALS = [Credentials.from_mapping(CREDENTIALS_DICT_2)] +CONFIGURED_CREDENTIALS = [CREDENTIALS_OBJECT_1] -CREDENTIALS_LIST = [ - Credentials.from_mapping(CREDENTIALS_DICT_1), - Credentials.from_mapping(CREDENTIALS_DICT_2), -] +STOLEN_CREDENTIALS = [CREDENTIALS_OBJECT_2] + +CREDENTIALS_LIST = [CREDENTIALS_OBJECT_1, CREDENTIALS_OBJECT_2] @pytest.fixture From ae3357a1b686dfd447054c38b2a7aa425c799a0c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 11 Jul 2022 15:09:10 +0200 Subject: [PATCH 20/20] UT: Remove removal of collection in MongoCredentialsRepository --- .../cc/repository/test_mongo_credentials_repository.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py index e939a4c89..4b2a28849 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_credentials_repository.py @@ -94,9 +94,6 @@ def test_mongo_repository_stolen(mongo_repository): assert actual_stolen_credentials == [] - # Must remove configured also for the next tests - mongo_repository.remove_configured_credentials() - def test_mongo_repository_all(mongo_repository):