From 9577c5569e7bfab9b2d3e09fcef0ddaaf49e7578 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Fri, 16 Sep 2022 14:11:17 +0300 Subject: [PATCH 1/3] Common, Agent: Extract credential encoding related methods --- monkey/common/credentials/__init__.py | 1 + monkey/common/credentials/credentials.py | 18 +++-------------- monkey/common/credentials/encoding.py | 20 +++++++++++++++++++ monkey/infection_monkey/exploit/mssqlexec.py | 2 +- .../powershell_utils/powershell_client.py | 2 +- monkey/infection_monkey/exploit/smbexec.py | 2 +- monkey/infection_monkey/exploit/sshexec.py | 2 +- .../exploit/tools/smb_tools.py | 2 +- monkey/infection_monkey/exploit/wmiexec.py | 2 +- .../unit_tests/common/events/__init__.py | 0 10 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 monkey/common/credentials/encoding.py create mode 100644 monkey/tests/unit_tests/common/events/__init__.py diff --git a/monkey/common/credentials/__init__.py b/monkey/common/credentials/__init__.py index 66b91971d..175ec1481 100644 --- a/monkey/common/credentials/__init__.py +++ b/monkey/common/credentials/__init__.py @@ -3,5 +3,6 @@ from .nt_hash import NTHash from .password import Password from .ssh_keypair import SSHKeypair from .username import Username +from .encoding import get_plaintext, SecretEncodingConfig from .credentials import Credentials diff --git a/monkey/common/credentials/credentials.py b/monkey/common/credentials/credentials.py index 834e16ac3..744de5b2b 100644 --- a/monkey/common/credentials/credentials.py +++ b/monkey/common/credentials/credentials.py @@ -2,22 +2,14 @@ from __future__ import annotations from typing import Optional, Union -from pydantic import SecretBytes, SecretStr - from ..base_models import InfectionMonkeyBaseModel, InfectionMonkeyModelConfig from . import LMHash, NTHash, Password, SSHKeypair, Username +from .encoding import SecretEncodingConfig Secret = Union[Password, LMHash, NTHash, SSHKeypair] Identity = Username -def get_plaintext(secret: Union[SecretStr, SecretBytes, None, str]) -> Optional[str]: - if isinstance(secret, (SecretStr, SecretBytes)): - return secret.get_secret_value() - else: - return secret - - class Credentials(InfectionMonkeyBaseModel): """Represents a credential pair (an identity and a secret)""" @@ -27,9 +19,5 @@ class Credentials(InfectionMonkeyBaseModel): secret: Optional[Secret] """Secret part of credentials, like a password or a hash""" - class Config(InfectionMonkeyModelConfig): - json_encoders = { - # This makes secrets dumpable to json, but not loggable - SecretStr: get_plaintext, - SecretBytes: get_plaintext, - } + class Config(SecretEncodingConfig, InfectionMonkeyModelConfig): + pass diff --git a/monkey/common/credentials/encoding.py b/monkey/common/credentials/encoding.py new file mode 100644 index 000000000..e76ff8599 --- /dev/null +++ b/monkey/common/credentials/encoding.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing import Optional, Union + +from pydantic import SecretBytes, SecretStr + + +def get_plaintext(secret: Union[SecretStr, SecretBytes, None, str]) -> Optional[str]: + if isinstance(secret, (SecretStr, SecretBytes)): + return secret.get_secret_value() + else: + return secret + + +class SecretEncodingConfig: + json_encoders = { + # This makes secrets dumpable to json, but not loggable + SecretStr: get_plaintext, + SecretBytes: get_plaintext, + } diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 6fd8e27cb..a2a63eec8 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -6,7 +6,7 @@ from typing import Sequence, Tuple import pymssql from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT -from common.credentials.credentials import get_plaintext +from common.credentials import get_plaintext from common.utils.exceptions import FailedExploitationError from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_agent_dst_path diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py index 0ea71c6f1..cb8eec010 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -11,7 +11,7 @@ from pypsrp.powershell import PowerShell, RunspacePool from typing_extensions import Protocol from urllib3 import connectionpool -from common.credentials.credentials import get_plaintext +from common.credentials import get_plaintext from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index abf8b4f47..0facecc9f 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -4,7 +4,7 @@ from impacket.dcerpc.v5 import scmr, transport from impacket.dcerpc.v5.scmr import DCERPCSessionError from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT -from common.credentials.credentials import get_plaintext +from common.credentials import get_plaintext from common.utils.attack_utils import ScanStatus, UsageEnum from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_agent_dst_path diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 1b317b2ac..b7edda56e 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -6,7 +6,7 @@ import paramiko from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT -from common.credentials.credentials import get_plaintext +from common.credentials import get_plaintext from common.utils import Timer from common.utils.attack_utils import ScanStatus from common.utils.exceptions import FailedExploitationError diff --git a/monkey/infection_monkey/exploit/tools/smb_tools.py b/monkey/infection_monkey/exploit/tools/smb_tools.py index c9ac254e1..5a4ab5042 100644 --- a/monkey/infection_monkey/exploit/tools/smb_tools.py +++ b/monkey/infection_monkey/exploit/tools/smb_tools.py @@ -10,7 +10,7 @@ from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21 from impacket.smbconnection import SMB_DIALECT, SMBConnection from pydantic import SecretStr -from common.credentials.credentials import get_plaintext +from common.credentials import get_plaintext from common.utils.attack_utils import ScanStatus from infection_monkey.network.tools import get_interface_to_target from infection_monkey.telemetry.attack.t1105_telem import T1105Telem diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 8cfd27a3d..0788ff813 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -5,7 +5,7 @@ import traceback from impacket.dcerpc.v5.rpcrt import DCERPCException -from common.credentials.credentials import get_plaintext +from common.credentials import get_plaintext from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_agent_dst_path from infection_monkey.exploit.tools.smb_tools import SmbTools diff --git a/monkey/tests/unit_tests/common/events/__init__.py b/monkey/tests/unit_tests/common/events/__init__.py new file mode 100644 index 000000000..e69de29bb From 46f7390a05077f5579516a20bc96136b385b1174 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Fri, 16 Sep 2022 14:14:18 +0300 Subject: [PATCH 2/3] Common: Use SecretEncodingConfig in credentials_stolen_events.py --- .../events/credentials_stolen_events.py | 4 +++ .../common/credentials/test_credentials.py | 3 +-- .../events/test_credentials_stolen_events.py | 26 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 monkey/tests/unit_tests/common/events/test_credentials_stolen_events.py diff --git a/monkey/common/events/credentials_stolen_events.py b/monkey/common/events/credentials_stolen_events.py index 06d2f967e..8c7dfdd1e 100644 --- a/monkey/common/events/credentials_stolen_events.py +++ b/monkey/common/events/credentials_stolen_events.py @@ -4,6 +4,7 @@ from pydantic import Field from common.credentials import Credentials +from ..credentials.encoding import SecretEncodingConfig from . import AbstractAgentEvent @@ -16,3 +17,6 @@ class CredentialsStolenEvent(AbstractAgentEvent): """ stolen_credentials: Sequence[Credentials] = Field(default_factory=list) + + class Config(SecretEncodingConfig): + pass diff --git a/monkey/tests/unit_tests/common/credentials/test_credentials.py b/monkey/tests/unit_tests/common/credentials/test_credentials.py index 09d68a4c3..9577a8fe9 100644 --- a/monkey/tests/unit_tests/common/credentials/test_credentials.py +++ b/monkey/tests/unit_tests/common/credentials/test_credentials.py @@ -16,8 +16,7 @@ from tests.data_for_tests.propagation_credentials import ( ) from common.base_models import InfectionMonkeyBaseModel -from common.credentials import Credentials -from common.credentials.credentials import get_plaintext +from common.credentials import Credentials, get_plaintext @pytest.mark.parametrize( diff --git a/monkey/tests/unit_tests/common/events/test_credentials_stolen_events.py b/monkey/tests/unit_tests/common/events/test_credentials_stolen_events.py new file mode 100644 index 000000000..df200f550 --- /dev/null +++ b/monkey/tests/unit_tests/common/events/test_credentials_stolen_events.py @@ -0,0 +1,26 @@ +from tests.data_for_tests.propagation_credentials import ( + CREDENTIALS, + PLAINTEXT_LM_HASH, + PLAINTEXT_NT_HASH, + PLAINTEXT_PASSWORD, + PLAINTEXT_PRIVATE_KEY, +) +from tests.unit_tests.monkey_island.cc.models.test_agent import AGENT_ID + +from common.events import CredentialsStolenEvent + +TEST_EVENT = CredentialsStolenEvent(stolen_credentials=CREDENTIALS, source=AGENT_ID) + + +def test_credentials_stolen_event_serialization_json(): + serialized_event = TEST_EVENT.json() + assert PLAINTEXT_PASSWORD in serialized_event + assert PLAINTEXT_LM_HASH in serialized_event + assert PLAINTEXT_NT_HASH in serialized_event + assert PLAINTEXT_PRIVATE_KEY in serialized_event + + +def test_credential_stolen_event_deserialization_json(): + serialized_event = TEST_EVENT.json() + deserialized_event = CredentialsStolenEvent.parse_raw(serialized_event) + assert deserialized_event == TEST_EVENT From ec40a9c6ad239ce74f6bed451fd6924d4730bb51 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 16 Sep 2022 08:34:46 -0400 Subject: [PATCH 3/3] Common: Inherit from InfectionMonkeyModelConfig in CredsStolenEvent --- monkey/common/events/credentials_stolen_events.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/common/events/credentials_stolen_events.py b/monkey/common/events/credentials_stolen_events.py index 8c7dfdd1e..84f4b44f3 100644 --- a/monkey/common/events/credentials_stolen_events.py +++ b/monkey/common/events/credentials_stolen_events.py @@ -2,6 +2,7 @@ from typing import Sequence from pydantic import Field +from common.base_models import InfectionMonkeyModelConfig from common.credentials import Credentials from ..credentials.encoding import SecretEncodingConfig @@ -18,5 +19,5 @@ class CredentialsStolenEvent(AbstractAgentEvent): stolen_credentials: Sequence[Credentials] = Field(default_factory=list) - class Config(SecretEncodingConfig): + class Config(SecretEncodingConfig, InfectionMonkeyModelConfig): pass