From 6282cd0de333e2d834a7e9d4d5fedb841657f340 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 7 Jul 2021 10:48:35 +0300 Subject: [PATCH 1/5] Add a UT to test if ransomware payload tries to encrypt files if "linux_target_dir" and "windows_target_dir" inputs are empty. We have empty "linux_target_dir" and "windows_target_dir" by default so it's important that ransomware payload doesn't try to encrypt files by default, without users' knowledge. --- .../ransomware/test_ransomware_payload.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/monkey/tests/unit_tests/infection_monkey/ransomware/test_ransomware_payload.py b/monkey/tests/unit_tests/infection_monkey/ransomware/test_ransomware_payload.py index 2014c7486..f0cb6bad9 100644 --- a/monkey/tests/unit_tests/infection_monkey/ransomware/test_ransomware_payload.py +++ b/monkey/tests/unit_tests/infection_monkey/ransomware/test_ransomware_payload.py @@ -163,6 +163,24 @@ def test_encryption_skipped_if_configured_false( assert hash_file(ransomware_target / TEST_KEYBOARD_TXT) == TEST_KEYBOARD_TXT_CLEARTEXT_SHA256 +def test_encryption_skipped_if_no_directory( + ransomware_payload_config, telemetry_messenger_spy, monkeypatch +): + ransomware_payload_config["encryption"]["enabled"] = True + ransomware_payload_config["encryption"]["directories"]["linux_target_dir"] = "" + ransomware_payload_config["encryption"]["directories"]["windows_target_dir"] = "" + + def _file_encryption_method_mock(*args, **kwargs): + raise Exception( + "Ransomware payload attempted to " + "encrypt files even though no directory was provided!" + ) + + ransomware_payload = RansomwarePayload(ransomware_payload_config, telemetry_messenger_spy) + monkeypatch.setattr(ransomware_payload, "_encrypt_files", _file_encryption_method_mock) + ransomware_payload.run_payload() + + def test_telemetry_success(ransomware_payload, telemetry_messenger_spy): ransomware_payload.run_payload() From bd60bef35f5cb9198db629dfbbf0983d7d422468 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 7 Jul 2021 11:23:10 +0300 Subject: [PATCH 2/5] Change the expand_path method in file_utils.py to throw an error if an empty file path is provided instead of expanding it to current working directory --- monkey/common/utils/file_utils.py | 6 ++++++ .../unit_tests/common/utils/test_common_file_utils.py | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/monkey/common/utils/file_utils.py b/monkey/common/utils/file_utils.py index 225fb8732..6110cd020 100644 --- a/monkey/common/utils/file_utils.py +++ b/monkey/common/utils/file_utils.py @@ -1,5 +1,11 @@ import os +class InvalidPath(Exception): + pass + + def expand_path(path: str) -> str: + if not path: + raise InvalidPath("Empty path provided") return os.path.expandvars(os.path.expanduser(path)) diff --git a/monkey/tests/unit_tests/common/utils/test_common_file_utils.py b/monkey/tests/unit_tests/common/utils/test_common_file_utils.py index 226a403b8..b67341cfe 100644 --- a/monkey/tests/unit_tests/common/utils/test_common_file_utils.py +++ b/monkey/tests/unit_tests/common/utils/test_common_file_utils.py @@ -1,6 +1,8 @@ import os -from common.utils.file_utils import expand_path +import pytest + +from common.utils.file_utils import InvalidPath, expand_path def test_expand_user(patched_home_env): @@ -15,3 +17,8 @@ def test_expand_vars(patched_home_env): expected_path = os.path.join(patched_home_env, "test") assert expand_path(input_path) == expected_path + + +def test_expand_path__empty_path_provided(): + with pytest.raises(InvalidPath): + expand_path("") From ca1712cdd65e86d2ed4b8221d4b369e4a19dbde2 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 7 Jul 2021 11:40:56 +0300 Subject: [PATCH 3/5] Extract the logic of determining target directory for ransomware payload into a separate method --- .../ransomware/ransomware_payload.py | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/monkey/infection_monkey/ransomware/ransomware_payload.py b/monkey/infection_monkey/ransomware/ransomware_payload.py index a58b50545..0cf8d77ec 100644 --- a/monkey/infection_monkey/ransomware/ransomware_payload.py +++ b/monkey/infection_monkey/ransomware/ransomware_payload.py @@ -4,7 +4,7 @@ from pathlib import Path from pprint import pformat from typing import List, Optional, Tuple -from common.utils.file_utils import expand_path +from common.utils.file_utils import InvalidPath, expand_path from infection_monkey.ransomware.bitflip_encryptor import BitflipEncryptor from infection_monkey.ransomware.file_selectors import select_production_safe_target_files from infection_monkey.ransomware.targeted_file_extensions import TARGETED_FILE_EXTENSIONS @@ -28,15 +28,7 @@ class RansomwarePayload: self._encryption_enabled = config["encryption"]["enabled"] self._readme_enabled = config["other_behaviors"]["readme"] - target_directories = config["encryption"]["directories"] - self._target_dir = Path( - expand_path( - target_directories["windows_target_dir"] - if is_windows_os() - else target_directories["linux_target_dir"] - ) - ) - + self._target_dir = RansomwarePayload.get_target_dir(config) self._new_file_extension = EXTENSION self._valid_file_extensions_for_encryption = TARGETED_FILE_EXTENSIONS.copy() self._valid_file_extensions_for_encryption.discard(self._new_file_extension) @@ -44,6 +36,19 @@ class RansomwarePayload: self._encryptor = BitflipEncryptor(chunk_size=CHUNK_SIZE) self._telemetry_messenger = telemetry_messenger + @staticmethod + def get_target_dir(config: dict): + target_directories = config["encryption"]["directories"] + if is_windows_os(): + target_dir_field = target_directories["windows_target_dir"] + else: + target_dir_field = target_directories["linux_target_dir"] + + try: + return Path(expand_path(target_dir_field)) + except InvalidPath: + return None + def run_payload(self): if self._encryption_enabled: LOG.info("Running ransomware payload") From d3beebf99553f9c16287ce67582513d952e93a7f Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 7 Jul 2021 11:41:42 +0300 Subject: [PATCH 4/5] Change ransomware_payload.py to not encrypt files in CWD if no directory was specified --- monkey/infection_monkey/ransomware/ransomware_payload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/ransomware/ransomware_payload.py b/monkey/infection_monkey/ransomware/ransomware_payload.py index 0cf8d77ec..56fd6f07f 100644 --- a/monkey/infection_monkey/ransomware/ransomware_payload.py +++ b/monkey/infection_monkey/ransomware/ransomware_payload.py @@ -50,7 +50,7 @@ class RansomwarePayload: return None def run_payload(self): - if self._encryption_enabled: + if self._encryption_enabled and self._target_dir: LOG.info("Running ransomware payload") file_list = self._find_files() self._encrypt_files(file_list) From 726e18079737c51d32bf4a0b6b56988ffc3f1dd2 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 7 Jul 2021 13:02:10 +0300 Subject: [PATCH 5/5] Add a log message explaining why ransomware target directory is set to none --- monkey/infection_monkey/ransomware/ransomware_payload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/ransomware/ransomware_payload.py b/monkey/infection_monkey/ransomware/ransomware_payload.py index 56fd6f07f..f2e3eb476 100644 --- a/monkey/infection_monkey/ransomware/ransomware_payload.py +++ b/monkey/infection_monkey/ransomware/ransomware_payload.py @@ -46,7 +46,8 @@ class RansomwarePayload: try: return Path(expand_path(target_dir_field)) - except InvalidPath: + except InvalidPath as e: + LOG.debug(f"Target ransomware dir set to None: {e}") return None def run_payload(self):