From ef4fbb30cc820d42fb622ba0405ec232cf035029 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
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 <mike.s.salvatore@gmail.com>
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 <mike.s.salvatore@gmail.com>
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):