diff --git a/monkey/infection_monkey/ransomware/ransomware_payload.py b/monkey/infection_monkey/ransomware/ransomware_payload.py index 698da80d4..dcc0055de 100644 --- a/monkey/infection_monkey/ransomware/ransomware_payload.py +++ b/monkey/infection_monkey/ransomware/ransomware_payload.py @@ -1,6 +1,7 @@ import logging import shutil from pathlib import Path +from pprint import pformat from typing import List, Optional, Tuple from infection_monkey.ransomware.bitflip_encryptor import BitflipEncryptor @@ -21,20 +22,17 @@ README_DEST = "README.txt" class RansomwarePayload: def __init__(self, config: dict, telemetry_messenger: ITelemetryMessenger): - target_directories = config["directories"] - LOG.info( - f"Windows dir configured for encryption is \"{target_directories['windows_dir']}\"" - ) - LOG.info(f"Linux dir configured for encryption is \"{target_directories['linux_dir']}\"") - - self._target_dir = ( - target_directories["windows_dir"] - if is_windows_os() - else target_directories["linux_dir"] - ) + LOG.debug(f"Ransomware payload configuration:\n{pformat(config)}") + self._encryption_enabled = config["encryption"]["enabled"] self._readme_enabled = config["other_behaviors"]["readme"] - LOG.info(f"README enabled: {self._readme_enabled}") + + target_directories = config["encryption"]["directories"] + self._target_dir = ( + target_directories["windows_target_dir"] + if is_windows_os() + else target_directories["linux_target_dir"] + ) self._new_file_extension = EXTENSION self._valid_file_extensions_for_encryption = VALID_FILE_EXTENSIONS_FOR_ENCRYPTION.copy() @@ -44,12 +42,15 @@ class RansomwarePayload: self._telemetry_messenger = telemetry_messenger def run_payload(self): - LOG.info("Running ransomware payload") - file_list = self._find_files() - self._encrypt_files(file_list) + if self._encryption_enabled: + LOG.info("Running ransomware payload") + file_list = self._find_files() + self._encrypt_files(file_list) + self._leave_readme() def _find_files(self) -> List[Path]: + LOG.info(f"Collecting files in {self._target_dir}") if not self._target_dir: return [] @@ -58,6 +59,8 @@ class RansomwarePayload: ) def _encrypt_files(self, file_list: List[Path]) -> List[Tuple[Path, Optional[Exception]]]: + LOG.info(f"Encrypting files in {self._target_dir}") + results = [] for filepath in file_list: try: diff --git a/monkey/monkey_island/cc/services/config_schema/ransomware.py b/monkey/monkey_island/cc/services/config_schema/ransomware.py index bf7d5aced..c30c1a392 100644 --- a/monkey/monkey_island/cc/services/config_schema/ransomware.py +++ b/monkey/monkey_island/cc/services/config_schema/ransomware.py @@ -2,28 +2,43 @@ RANSOMWARE = { "title": "Ransomware", "type": "object", "properties": { - "directories": { - "title": "Directories to encrypt", + "encryption": { + "title": "Encryption", "type": "object", "properties": { - "linux_dir": { - "title": "Linux encryptable directory", - "type": "string", - "default": "", - "description": "Files in the specified directory will be encrypted " - "using bitflip to simulate ransomware.", + "enabled": { + "title": "Encrypt files", + "type": "boolean", + "default": True, + "description": "Ransomware encryption will be simulated by flipping every bit " + "in the files contained within the target directories.", }, - "windows_dir": { - "title": "Windows encryptable directory", - "type": "string", - "default": "", - "description": "Files in the specified directory will be encrypted " - "using bitflip to simulate ransomware.", + "directories": { + "title": "Directories to encrypt", + "type": "object", + "properties": { + "linux_target_dir": { + "title": "Linux target directory", + "type": "string", + "default": "", + "description": "A path to a directory on Linux systems that can be " + "used to safely simulate the encryption behavior of ransomware. If no " + "directory is specified, no files will be encrypted.", + }, + "windows_target_dir": { + "title": "Windows target directory", + "type": "string", + "default": "", + "description": "A path to a directory on Windows systems that can be " + "used to safely simulate the encryption behavior of ransomware. If no " + "directory is specified, no files will be encrypted.", + }, + }, }, }, }, "other_behaviors": { - "title": "Other Behaviors", + "title": "Other behavior", "type": "object", "properties": { "readme": { 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 80677ecf2..36dc6615e 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 @@ -32,7 +32,13 @@ def with_extension(filename): @pytest.fixture def ransomware_payload_config(ransomware_target): return { - "directories": {"linux_dir": str(ransomware_target), "windows_dir": str(ransomware_target)}, + "encryption": { + "enabled": True, + "directories": { + "linux_target_dir": str(ransomware_target), + "windows_target_dir": str(ransomware_target), + }, + }, "other_behaviors": {"readme": False}, } @@ -127,6 +133,18 @@ def test_skip_already_encrypted_file(ransomware_target, ransomware_payload): ) +def test_encryption_skipped_if_configured_false( + ransomware_payload_config, ransomware_target, telemetry_messenger_spy +): + ransomware_payload_config["encryption"]["enabled"] = False + + ransomware_payload = RansomwarePayload(ransomware_payload_config, telemetry_messenger_spy) + ransomware_payload.run_payload() + + assert hash_file(ransomware_target / ALL_ZEROS_PDF) == ALL_ZEROS_PDF_CLEARTEXT_SHA256 + assert hash_file(ransomware_target / TEST_KEYBOARD_TXT) == TEST_KEYBOARD_TXT_CLEARTEXT_SHA256 + + def test_telemetry_success(ransomware_payload, telemetry_messenger_spy): ransomware_payload.run_payload()