diff --git a/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.ransomware.py b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.ransomware.py new file mode 100644 index 000000000..4cedd58e7 --- /dev/null +++ b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.ransomware.py @@ -0,0 +1,3 @@ +from PyInstaller.utils.hooks import collect_data_files + +datas = collect_data_files("infection_monkey.ransomware") diff --git a/monkey/infection_monkey/ransomware/__init__.py b/monkey/infection_monkey/ransomware/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/infection_monkey/ransomware/ransomware_payload.py b/monkey/infection_monkey/ransomware/ransomware_payload.py index da300a175..bde62bfcc 100644 --- a/monkey/infection_monkey/ransomware/ransomware_payload.py +++ b/monkey/infection_monkey/ransomware/ransomware_payload.py @@ -1,4 +1,5 @@ import logging +import shutil from pathlib import Path from typing import List, Optional, Tuple @@ -14,6 +15,9 @@ LOG = logging.getLogger(__name__) EXTENSION = ".m0nk3y" CHUNK_SIZE = 4096 * 24 +README_SRC = Path(__file__).parent / "ransomware_readme.txt" +README_DEST = "README.txt" + class RansomewarePayload: def __init__(self, config: dict, telemetry_messenger: ITelemetryMessenger): @@ -29,6 +33,9 @@ class RansomewarePayload: else target_directories["linux_dir"] ) + self._readme_enabled = config["other_behaviors"]["readme"] + LOG.info(f"README enabled: {self._readme_enabled}") + self._new_file_extension = EXTENSION self._valid_file_extensions_for_encryption = VALID_FILE_EXTENSIONS_FOR_ENCRYPTION.copy() self._valid_file_extensions_for_encryption.discard(self._new_file_extension) @@ -40,6 +47,7 @@ class RansomewarePayload: LOG.info("Running ransomware payload") file_list = self._find_files() self._encrypt_files(file_list) + self._leave_readme() def _find_files(self) -> List[Path]: if not self._target_dir: @@ -70,3 +78,10 @@ class RansomewarePayload: def _send_telemetry(self, filepath: Path, error: str): encryption_attempt = RansomwareTelem((str(filepath), str(error))) self._telemetry_messenger.send_telemetry(encryption_attempt) + + def _leave_readme(self): + if self._readme_enabled: + try: + shutil.copyfile(README_SRC, Path(self._target_dir) / README_DEST) + except Exception as ex: + LOG.warning(f"An error occurred while attempting to leave a README.txt file: {ex}") diff --git a/monkey/infection_monkey/ransomware/ransomware_readme.txt b/monkey/infection_monkey/ransomware/ransomware_readme.txt new file mode 100644 index 000000000..8f480a536 --- /dev/null +++ b/monkey/infection_monkey/ransomware/ransomware_readme.txt @@ -0,0 +1,2 @@ +This is a placeholder README for the Infection Monkey Ransomware Simulation. +Don't panic :) diff --git a/monkey/monkey_island/cc/services/config_schema/ransomware.py b/monkey/monkey_island/cc/services/config_schema/ransomware.py index cbd4e4e72..bf7d5aced 100644 --- a/monkey/monkey_island/cc/services/config_schema/ransomware.py +++ b/monkey/monkey_island/cc/services/config_schema/ransomware.py @@ -27,7 +27,7 @@ RANSOMWARE = { "type": "object", "properties": { "readme": { - "title": "Create a README.TXT file", + "title": "Create a README.txt file", "type": "boolean", "default": True, "description": "Creates a README.txt ransomware note on infected systems.", 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 f26463ed1..46d334d3a 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 @@ -1,5 +1,5 @@ import os -from pathlib import PurePath +from pathlib import Path, PurePath import pytest from tests.unit_tests.infection_monkey.ransomware.ransomware_target_files import ( @@ -22,7 +22,11 @@ from tests.unit_tests.infection_monkey.ransomware.ransomware_target_files import from tests.utils import hash_file, is_user_admin from infection_monkey.ransomware import ransomware_payload as ransomware_payload_module -from infection_monkey.ransomware.ransomware_payload import EXTENSION, RansomewarePayload +from infection_monkey.ransomware.ransomware_payload import ( + EXTENSION, + README_DEST, + RansomewarePayload, +) def with_extension(filename): @@ -32,7 +36,8 @@ def with_extension(filename): @pytest.fixture def ransomware_payload_config(ransomware_target): return { - "directories": {"linux_dir": str(ransomware_target), "windows_dir": str(ransomware_target)} + "directories": {"linux_dir": str(ransomware_target), "windows_dir": str(ransomware_target)}, + "other_behaviors": {"readme": False}, } @@ -151,3 +156,19 @@ def test_telemetry_failure(monkeypatch, ransomware_payload, telemetry_messenger_ assert "/file/not/exist" in telem_1.get_data()["ransomware_attempts"][0][0] assert "No such file or directory" in telem_1.get_data()["ransomware_attempts"][0][1] + + +def test_readme_false(ransomware_payload_config, ransomware_target, telemetry_messenger_spy): + ransomware_payload_config["other_behaviors"]["readme"] = False + ransomware_payload = RansomewarePayload(ransomware_payload_config, telemetry_messenger_spy) + + ransomware_payload.run_payload() + assert not Path(ransomware_target / README_DEST).exists() + + +def test_readme_true(ransomware_payload_config, ransomware_target, telemetry_messenger_spy): + ransomware_payload_config["other_behaviors"]["readme"] = True + ransomware_payload = RansomewarePayload(ransomware_payload_config, telemetry_messenger_spy) + + ransomware_payload.run_payload() + assert Path(ransomware_target / README_DEST).exists()