From ef4fbb30cc820d42fb622ba0405ec232cf035029 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 18 Jul 2022 09:55:08 -0400 Subject: [PATCH 1/3] Agent: Use new credentials format in ControlChannel --- monkey/infection_monkey/i_control_channel.py | 9 ++++++--- monkey/infection_monkey/master/control_channel.py | 10 +++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/monkey/infection_monkey/i_control_channel.py b/monkey/infection_monkey/i_control_channel.py index b90308018..03a5bb4a9 100644 --- a/monkey/infection_monkey/i_control_channel.py +++ b/monkey/infection_monkey/i_control_channel.py @@ -1,6 +1,8 @@ import abc +from typing import Sequence from common.configuration import AgentConfiguration +from common.credentials import Credentials class IControlChannel(metaclass=abc.ABCMeta): @@ -21,10 +23,11 @@ class IControlChannel(metaclass=abc.ABCMeta): pass @abc.abstractmethod - def get_credentials_for_propagation(self) -> dict: + def get_credentials_for_propagation(self) -> Sequence[Credentials]: """ - :return: A dictionary containing propagation credentials data - :rtype: dict + Get credentials to use during propagation + + :return: A Sequence containing propagation credentials data """ pass diff --git a/monkey/infection_monkey/master/control_channel.py b/monkey/infection_monkey/master/control_channel.py index 6daf107fc..cdf660842 100644 --- a/monkey/infection_monkey/master/control_channel.py +++ b/monkey/infection_monkey/master/control_channel.py @@ -1,13 +1,13 @@ import json import logging from pprint import pformat -from typing import Mapping +from typing import Mapping, Sequence import requests from common.common_consts.timeouts import SHORT_REQUEST_TIMEOUT from common.configuration import AgentConfiguration -from infection_monkey.custom_types import PropagationCredentials +from common.credentials import Credentials from infection_monkey.i_control_channel import IControlChannel, IslandCommunicationError requests.packages.urllib3.disable_warnings() @@ -71,7 +71,7 @@ class ControlChannel(IControlChannel): ) as e: raise IslandCommunicationError(e) - def get_credentials_for_propagation(self) -> PropagationCredentials: + def get_credentials_for_propagation(self) -> Sequence[Credentials]: propagation_credentials_url = ( f"https://{self._control_channel_server}/api/propagation-credentials" ) @@ -84,9 +84,9 @@ class ControlChannel(IControlChannel): ) response.raise_for_status() - return json.loads(response.content.decode())["propagation_credentials"] + return [Credentials.from_mapping(credentials) for credentials in response.json] except ( - json.JSONDecodeError, + requests.exceptions.JSONDecodeError, requests.exceptions.ConnectionError, requests.exceptions.Timeout, requests.exceptions.TooManyRedirects, From ebc854735e9f4dc8fb7a5d2f548129abf0fceade Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 18 Jul 2022 10:22:14 -0400 Subject: [PATCH 2/3] Agent: Use new Credentials objects in AggregatingCredentialsStore --- .../aggregating_credentials_store.py | 12 +- .../test_aggregating_credentials_store.py | 121 ++++++++++-------- 2 files changed, 72 insertions(+), 61 deletions(-) diff --git a/monkey/infection_monkey/credential_store/aggregating_credentials_store.py b/monkey/infection_monkey/credential_store/aggregating_credentials_store.py index bfa69117f..cef6f1bf4 100644 --- a/monkey/infection_monkey/credential_store/aggregating_credentials_store.py +++ b/monkey/infection_monkey/credential_store/aggregating_credentials_store.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Iterable, Mapping +from typing import Any, Iterable, Sequence from common.credentials import CredentialComponentType, Credentials, ICredentialComponent from infection_monkey.custom_types import PropagationCredentials @@ -52,9 +52,7 @@ class AggregatingCredentialsStore(ICredentialsStore): def get_credentials(self) -> PropagationCredentials: try: propagation_credentials = self._get_credentials_from_control_channel() - - # Needs to be reworked when exploiters accepts sequence of Credentials - self._aggregate_credentials(propagation_credentials) + self.add_credentials(propagation_credentials) return self._stored_credentials except Exception as ex: @@ -62,13 +60,9 @@ class AggregatingCredentialsStore(ICredentialsStore): logger.error(f"Error while attempting to retrieve credentials for propagation: {ex}") @request_cache(CREDENTIALS_POLL_PERIOD_SEC) - def _get_credentials_from_control_channel(self) -> PropagationCredentials: + def _get_credentials_from_control_channel(self) -> Sequence[Credentials]: return self._control_channel.get_credentials_for_propagation() - def _aggregate_credentials(self, credentials_to_aggr: Mapping): - for cred_attr, credentials_values in credentials_to_aggr.items(): - self._set_attribute(cred_attr, credentials_values) - def _set_attribute(self, attribute_to_be_set: str, credentials_values: Iterable[Any]): if not credentials_values: return 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_credentials_store.py index e5ddccfe2..5435d8e15 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_credentials_store.py @@ -1,54 +1,67 @@ from unittest.mock import MagicMock import pytest +from tests.data_for_tests.propagation_credentials import ( + LM_HASH, + NT_HASH, + PASSWORD_1, + PASSWORD_2, + PASSWORD_3, + PRIVATE_KEY, + PROPAGATION_CREDENTIALS, + PUBLIC_KEY, + SPECIAL_USERNAME, + USERNAME, +) -from common.credentials import Credentials, Password, SSHKeypair, Username +from common.credentials import Credentials, LMHash, NTHash, Password, SSHKeypair, Username from infection_monkey.credential_store import AggregatingCredentialsStore -CONTROL_CHANNEL_CREDENTIALS = { - "exploit_user_list": ["Administrator", "root", "user1"], - "exploit_password_list": ["123456", "123456789", "password", "root"], - "exploit_lm_hash_list": ["aasdf23asd1fdaasadasdfas"], - "exploit_ntlm_hash_list": ["asdfadvxvsdftw3e3421234123412", "qw4trklxklvznksbhasd1231"], - "exploit_ssh_keys": [ - {"public_key": "some_public_key", "private_key": "some_private_key"}, - { - "public_key": "ssh-ed25519 AAAAC3NzEIFaJ7xH+Yoxd\n", - "private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BdHIAAAAGYXjl0j66VAKruPEKjS3A=\n" - "-----END OPENSSH PRIVATE KEY-----\n", - }, - ], +CONTROL_CHANNEL_CREDENTIALS = PROPAGATION_CREDENTIALS +TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS = { + "exploit_user_list": {USERNAME, SPECIAL_USERNAME}, + "exploit_password_list": {PASSWORD_1, PASSWORD_2, PASSWORD_3}, + "exploit_lm_hash_list": {LM_HASH}, + "exploit_ntlm_hash_list": {NT_HASH}, + "exploit_ssh_keys": [{"public_key": PUBLIC_KEY, "private_key": PRIVATE_KEY}], } -EMPTY_CHANNEL_CREDENTIALS = { - "exploit_user_list": [], - "exploit_password_list": [], - "exploit_lm_hash_list": [], - "exploit_ntlm_hash_list": [], - "exploit_ssh_keys": [], -} +EMPTY_CHANNEL_CREDENTIALS = [] -TEST_CREDENTIALS = [ +STOLEN_USERNAME_1 = "user1" +STOLEN_USERNAME_2 = "user2" +STOLEN_USERNAME_3 = "user3" +STOLEN_PASSWORD_1 = "abcdefg" +STOLEN_PASSWORD_2 = "super_secret" +STOLEN_PUBLIC_KEY_1 = "some_public_key_1" +STOLEN_PUBLIC_KEY_2 = "some_public_key_2" +STOLEN_LM_HASH = "AAD3B435B51404EEAAD3B435B51404EE" +STOLEN_NT_HASH = "C0172DFF622FE29B5327CB79DC12D24C" +STOLEN_PRIVATE_KEY_1 = "some_private_key_1" +STOLEN_PRIVATE_KEY_2 = "some_private_key_2" +STOLEN_CREDENTIALS = [ Credentials( - identity=Username("user1"), - secret=Password("root"), + identity=Username(STOLEN_USERNAME_1), + secret=Password(PASSWORD_1), ), - Credentials(identity=Username("user1"), secret=Password("abcdefg")), + Credentials(identity=Username(STOLEN_USERNAME_1), secret=Password(STOLEN_PASSWORD_1)), Credentials( - identity=Username("user3"), - secret=SSHKeypair(public_key="some_public_key_1", private_key="some_private_key_1"), + identity=Username(STOLEN_USERNAME_2), + secret=SSHKeypair(public_key=STOLEN_PUBLIC_KEY_1, private_key=STOLEN_PRIVATE_KEY_1), ), Credentials( identity=None, - secret=Password("super_secret"), + secret=Password(STOLEN_PASSWORD_2), ), - Credentials(identity=Username("user4"), secret=None), + Credentials(identity=Username(STOLEN_USERNAME_2), secret=LMHash(STOLEN_LM_HASH)), + Credentials(identity=Username(STOLEN_USERNAME_2), secret=NTHash(STOLEN_NT_HASH)), + Credentials(identity=Username(STOLEN_USERNAME_3), secret=None), ] -SSH_KEYS_CREDENTIALS = [ +STOLEN_SSH_KEYS_CREDENTIALS = [ Credentials( - Username("root"), - SSHKeypair(public_key="some_public_key", private_key="some_private_key"), + Username(USERNAME), + SSHKeypair(public_key=STOLEN_PUBLIC_KEY_2, private_key=STOLEN_PRIVATE_KEY_2), ) ] @@ -63,45 +76,49 @@ def aggregating_credentials_store() -> AggregatingCredentialsStore: def test_get_credentials_from_store(aggregating_credentials_store): actual_stored_credentials = aggregating_credentials_store.get_credentials() - assert actual_stored_credentials["exploit_user_list"] == set( - CONTROL_CHANNEL_CREDENTIALS["exploit_user_list"] + assert ( + actual_stored_credentials["exploit_user_list"] + == TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS["exploit_user_list"] ) - assert actual_stored_credentials["exploit_password_list"] == set( - CONTROL_CHANNEL_CREDENTIALS["exploit_password_list"] + assert ( + actual_stored_credentials["exploit_password_list"] + == TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS["exploit_password_list"] ) - assert actual_stored_credentials["exploit_ntlm_hash_list"] == set( - CONTROL_CHANNEL_CREDENTIALS["exploit_ntlm_hash_list"] + assert ( + actual_stored_credentials["exploit_ntlm_hash_list"] + == TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS["exploit_ntlm_hash_list"] ) for ssh_keypair in actual_stored_credentials["exploit_ssh_keys"]: - assert ssh_keypair in CONTROL_CHANNEL_CREDENTIALS["exploit_ssh_keys"] + assert ssh_keypair in TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS["exploit_ssh_keys"] def test_add_credentials_to_store(aggregating_credentials_store): - aggregating_credentials_store.add_credentials(TEST_CREDENTIALS) - aggregating_credentials_store.add_credentials(SSH_KEYS_CREDENTIALS) + aggregating_credentials_store.add_credentials(STOLEN_CREDENTIALS) + aggregating_credentials_store.add_credentials(STOLEN_SSH_KEYS_CREDENTIALS) actual_stored_credentials = aggregating_credentials_store.get_credentials() assert actual_stored_credentials["exploit_user_list"] == set( [ - "Administrator", - "root", - "user1", - "user3", - "user4", + USERNAME, + SPECIAL_USERNAME, + STOLEN_USERNAME_1, + STOLEN_USERNAME_2, + STOLEN_USERNAME_3, ] ) assert actual_stored_credentials["exploit_password_list"] == set( [ - "123456", - "123456789", - "abcdefg", - "password", - "root", - "super_secret", + PASSWORD_1, + PASSWORD_2, + PASSWORD_3, + STOLEN_PASSWORD_1, + STOLEN_PASSWORD_2, ] ) + assert actual_stored_credentials["exploit_lm_hash_list"] == set([LM_HASH, STOLEN_LM_HASH]) + assert actual_stored_credentials["exploit_ntlm_hash_list"] == set([NT_HASH, STOLEN_NT_HASH]) assert len(actual_stored_credentials["exploit_ssh_keys"]) == 3 From dde3fd34767b0b1d5de800189ea7a30bf497a259 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 18 Jul 2022 10:30:52 -0400 Subject: [PATCH 3/3] UT: Parametrize test_get_credentials_from_store() --- .../test_aggregating_credentials_store.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) 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_credentials_store.py index 5435d8e15..49bdb2b88 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_credentials_store.py @@ -73,24 +73,11 @@ def aggregating_credentials_store() -> AggregatingCredentialsStore: return AggregatingCredentialsStore(control_channel) -def test_get_credentials_from_store(aggregating_credentials_store): +@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() - assert ( - actual_stored_credentials["exploit_user_list"] - == TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS["exploit_user_list"] - ) - assert ( - actual_stored_credentials["exploit_password_list"] - == TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS["exploit_password_list"] - ) - assert ( - actual_stored_credentials["exploit_ntlm_hash_list"] - == TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS["exploit_ntlm_hash_list"] - ) - - for ssh_keypair in actual_stored_credentials["exploit_ssh_keys"]: - assert ssh_keypair in TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS["exploit_ssh_keys"] + assert actual_stored_credentials[key] == TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS[key] def test_add_credentials_to_store(aggregating_credentials_store):