forked from p15670423/monkey
Merge pull request #1333 from guardicore/ransomware-skip-encrypt-readme
Ransomware skip encrypt readme
This commit is contained in:
commit
81f7de74ab
|
@ -1 +1,4 @@
|
||||||
monkey/tests/data_for_tests/ransomware_targets/** -text
|
monkey/tests/data_for_tests/ransomware_targets/** -text
|
||||||
|
monkey/tests/data_for_tests/test_readme.txt -text
|
||||||
|
monkey/tests/data_for_tests/stable_file.txt -text
|
||||||
|
monkey/infection_monkey/ransomware/ransomware_readme.txt -text
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import hashlib
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
@ -11,3 +12,12 @@ def expand_path(path: str) -> Path:
|
||||||
raise InvalidPath("Empty path provided")
|
raise InvalidPath("Empty path provided")
|
||||||
|
|
||||||
return Path(os.path.expandvars(os.path.expanduser(path)))
|
return Path(os.path.expandvars(os.path.expanduser(path)))
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_sha256_hash(filepath: Path):
|
||||||
|
sha256 = hashlib.sha256()
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
for block in iter(lambda: f.read(65536), b""):
|
||||||
|
sha256.update(block)
|
||||||
|
|
||||||
|
return sha256.hexdigest()
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
README_SRC = Path(__file__).parent / "ransomware_readme.txt"
|
||||||
|
README_FILE_NAME = "README.txt"
|
||||||
|
README_SHA256_HASH = "e3d9343cbcce6097c83044327b00ead14b6e8e6aa0d411160610033a856032fc"
|
|
@ -1,6 +1,8 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Set
|
from typing import List, Set
|
||||||
|
|
||||||
|
from common.utils.file_utils import get_file_sha256_hash
|
||||||
|
from infection_monkey.ransomware.consts import README_FILE_NAME, README_SHA256_HASH
|
||||||
from infection_monkey.utils.dir_utils import (
|
from infection_monkey.utils.dir_utils import (
|
||||||
file_extension_filter,
|
file_extension_filter,
|
||||||
filter_files,
|
filter_files,
|
||||||
|
@ -19,7 +21,15 @@ class ProductionSafeTargetFileSelector:
|
||||||
file_extension_filter(self._targeted_file_extensions),
|
file_extension_filter(self._targeted_file_extensions),
|
||||||
is_not_shortcut_filter,
|
is_not_shortcut_filter,
|
||||||
is_not_symlink_filter,
|
is_not_symlink_filter,
|
||||||
|
_is_not_ransomware_readme_filter,
|
||||||
]
|
]
|
||||||
|
|
||||||
all_files = get_all_regular_files_in_directory(target_dir)
|
all_files = get_all_regular_files_in_directory(target_dir)
|
||||||
return filter_files(all_files, file_filters)
|
return filter_files(all_files, file_filters)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_not_ransomware_readme_filter(filepath: Path) -> bool:
|
||||||
|
if filepath.name != README_FILE_NAME:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return get_file_sha256_hash(filepath) != README_SHA256_HASH
|
||||||
|
|
|
@ -2,15 +2,13 @@ import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, List
|
from typing import Callable, List
|
||||||
|
|
||||||
|
from infection_monkey.ransomware.consts import README_FILE_NAME, README_SRC
|
||||||
from infection_monkey.ransomware.ransomware_config import RansomwareConfig
|
from infection_monkey.ransomware.ransomware_config import RansomwareConfig
|
||||||
from infection_monkey.telemetry.file_encryption_telem import FileEncryptionTelem
|
from infection_monkey.telemetry.file_encryption_telem import FileEncryptionTelem
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
README_SRC = Path(__file__).parent / "ransomware_readme.txt"
|
|
||||||
README_DEST = "README.txt"
|
|
||||||
|
|
||||||
|
|
||||||
class RansomwarePayload:
|
class RansomwarePayload:
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -39,7 +37,7 @@ class RansomwarePayload:
|
||||||
self._encrypt_files(file_list)
|
self._encrypt_files(file_list)
|
||||||
|
|
||||||
if self._config.readme_enabled:
|
if self._config.readme_enabled:
|
||||||
self._leave_readme(README_SRC, self._config.target_directory / README_DEST)
|
self._leave_readme(README_SRC, self._config.target_directory / README_FILE_NAME)
|
||||||
|
|
||||||
def _find_files(self) -> List[Path]:
|
def _find_files(self) -> List[Path]:
|
||||||
LOG.info(f"Collecting files in {self._config.target_directory}")
|
LOG.info(f"Collecting files in {self._config.target_directory}")
|
||||||
|
|
|
@ -11,3 +11,13 @@ sys.path.insert(0, MONKEY_BASE_PATH)
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def data_for_tests_dir(pytestconfig):
|
def data_for_tests_dir(pytestconfig):
|
||||||
return Path(os.path.join(pytestconfig.rootdir, "monkey", "tests", "data_for_tests"))
|
return Path(os.path.join(pytestconfig.rootdir, "monkey", "tests", "data_for_tests"))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def stable_file(data_for_tests_dir) -> Path:
|
||||||
|
return data_for_tests_dir / "stable_file.txt"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def stable_file_sha256_hash() -> str:
|
||||||
|
return "d9dcaadc91261692dafa86e7275b1bf39bb7e19d2efcfacd6fe2bfc9a1ae1062"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Don't change me!
|
|
@ -2,7 +2,7 @@ import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from common.utils.file_utils import InvalidPath, expand_path
|
from common.utils.file_utils import InvalidPath, expand_path, get_file_sha256_hash
|
||||||
|
|
||||||
|
|
||||||
def test_expand_user(patched_home_env):
|
def test_expand_user(patched_home_env):
|
||||||
|
@ -22,3 +22,7 @@ def test_expand_vars(patched_home_env):
|
||||||
def test_expand_path__empty_path_provided():
|
def test_expand_path__empty_path_provided():
|
||||||
with pytest.raises(InvalidPath):
|
with pytest.raises(InvalidPath):
|
||||||
expand_path("")
|
expand_path("")
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_file_sha256_hash(stable_file, stable_file_sha256_hash):
|
||||||
|
assert get_file_sha256_hash(stable_file) == stable_file_sha256_hash
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from tests.unit_tests.infection_monkey.ransomware.ransomware_target_files import (
|
from tests.unit_tests.infection_monkey.ransomware.ransomware_target_files import (
|
||||||
|
@ -12,6 +13,7 @@ from tests.unit_tests.infection_monkey.ransomware.ransomware_target_files import
|
||||||
from tests.utils import is_user_admin
|
from tests.utils import is_user_admin
|
||||||
|
|
||||||
from infection_monkey.ransomware.file_selectors import ProductionSafeTargetFileSelector
|
from infection_monkey.ransomware.file_selectors import ProductionSafeTargetFileSelector
|
||||||
|
from infection_monkey.ransomware.ransomware_payload import README_SRC
|
||||||
|
|
||||||
TARGETED_FILE_EXTENSIONS = [".pdf", ".txt"]
|
TARGETED_FILE_EXTENSIONS = [".pdf", ".txt"]
|
||||||
|
|
||||||
|
@ -53,3 +55,21 @@ def test_directories_not_selected(ransomware_test_data, file_selector):
|
||||||
selected_files = file_selector(ransomware_test_data)
|
selected_files = file_selector(ransomware_test_data)
|
||||||
|
|
||||||
assert (ransomware_test_data / SUBDIR / HELLO_TXT) not in selected_files
|
assert (ransomware_test_data / SUBDIR / HELLO_TXT) not in selected_files
|
||||||
|
|
||||||
|
|
||||||
|
def test_ransomware_readme_not_selected(ransomware_target, file_selector):
|
||||||
|
readme_file = ransomware_target / "README.txt"
|
||||||
|
shutil.copyfile(README_SRC, readme_file)
|
||||||
|
|
||||||
|
selected_files = file_selector(ransomware_target)
|
||||||
|
|
||||||
|
assert readme_file not in selected_files
|
||||||
|
|
||||||
|
|
||||||
|
def test_pre_existing_readme_is_selected(ransomware_target, stable_file, file_selector):
|
||||||
|
readme_file = ransomware_target / "README.txt"
|
||||||
|
shutil.copyfile(stable_file, readme_file)
|
||||||
|
|
||||||
|
selected_files = file_selector(ransomware_target)
|
||||||
|
|
||||||
|
assert readme_file in selected_files
|
||||||
|
|
|
@ -9,8 +9,8 @@ from tests.unit_tests.infection_monkey.ransomware.ransomware_target_files import
|
||||||
TEST_KEYBOARD_TXT_CLEARTEXT_SHA256,
|
TEST_KEYBOARD_TXT_CLEARTEXT_SHA256,
|
||||||
TEST_KEYBOARD_TXT_ENCRYPTED_SHA256,
|
TEST_KEYBOARD_TXT_ENCRYPTED_SHA256,
|
||||||
)
|
)
|
||||||
from tests.utils import hash_file
|
|
||||||
|
|
||||||
|
from common.utils.file_utils import get_file_sha256_hash
|
||||||
from infection_monkey.ransomware.in_place_file_encryptor import InPlaceFileEncryptor
|
from infection_monkey.ransomware.in_place_file_encryptor import InPlaceFileEncryptor
|
||||||
from infection_monkey.utils.bit_manipulators import flip_bits
|
from infection_monkey.utils.bit_manipulators import flip_bits
|
||||||
|
|
||||||
|
@ -44,11 +44,11 @@ def test_file_encrypted(
|
||||||
):
|
):
|
||||||
test_keyboard = ransomware_target / file_name
|
test_keyboard = ransomware_target / file_name
|
||||||
|
|
||||||
assert hash_file(test_keyboard) == cleartext_hash
|
assert get_file_sha256_hash(test_keyboard) == cleartext_hash
|
||||||
|
|
||||||
in_place_bitflip_file_encryptor(test_keyboard)
|
in_place_bitflip_file_encryptor(test_keyboard)
|
||||||
|
|
||||||
assert hash_file(test_keyboard) == encrypted_hash
|
assert get_file_sha256_hash(test_keyboard) == encrypted_hash
|
||||||
|
|
||||||
|
|
||||||
def test_file_encrypted_in_place(in_place_bitflip_file_encryptor, ransomware_target):
|
def test_file_encrypted_in_place(in_place_bitflip_file_encryptor, ransomware_target):
|
||||||
|
@ -70,4 +70,4 @@ def test_encrypted_file_has_new_extension(ransomware_target):
|
||||||
|
|
||||||
assert not test_keyboard.exists()
|
assert not test_keyboard.exists()
|
||||||
assert encrypted_test_keyboard.exists()
|
assert encrypted_test_keyboard.exists()
|
||||||
assert hash_file(encrypted_test_keyboard) == TEST_KEYBOARD_TXT_ENCRYPTED_SHA256
|
assert get_file_sha256_hash(encrypted_test_keyboard) == TEST_KEYBOARD_TXT_ENCRYPTED_SHA256
|
||||||
|
|
|
@ -7,12 +7,9 @@ from tests.unit_tests.infection_monkey.ransomware.ransomware_target_files import
|
||||||
TEST_KEYBOARD_TXT,
|
TEST_KEYBOARD_TXT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from infection_monkey.ransomware.consts import README_FILE_NAME, README_SRC
|
||||||
from infection_monkey.ransomware.ransomware_config import RansomwareConfig
|
from infection_monkey.ransomware.ransomware_config import RansomwareConfig
|
||||||
from infection_monkey.ransomware.ransomware_payload import (
|
from infection_monkey.ransomware.ransomware_payload import RansomwarePayload
|
||||||
README_DEST,
|
|
||||||
README_SRC,
|
|
||||||
RansomwarePayload,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -162,7 +159,7 @@ def test_readme_true(
|
||||||
ransomware_payload = build_ransomware_payload(ransomware_payload_config)
|
ransomware_payload = build_ransomware_payload(ransomware_payload_config)
|
||||||
|
|
||||||
ransomware_payload.run_payload()
|
ransomware_payload.run_payload()
|
||||||
mock_leave_readme.assert_called_with(README_SRC, ransomware_test_data / README_DEST)
|
mock_leave_readme.assert_called_with(README_SRC, ransomware_test_data / README_FILE_NAME)
|
||||||
|
|
||||||
|
|
||||||
def test_no_readme_if_no_directory(
|
def test_no_readme_if_no_directory(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
from tests.utils import hash_file
|
|
||||||
|
|
||||||
|
from common.utils.file_utils import get_file_sha256_hash
|
||||||
from infection_monkey.ransomware.readme_dropper import leave_readme
|
from infection_monkey.ransomware.readme_dropper import leave_readme
|
||||||
|
|
||||||
DEST_FILE = "README.TXT"
|
DEST_FILE = "README.TXT"
|
||||||
|
@ -23,10 +23,10 @@ def test_readme_already_exists(src_readme, dest_readme):
|
||||||
|
|
||||||
leave_readme(src_readme, dest_readme)
|
leave_readme(src_readme, dest_readme)
|
||||||
|
|
||||||
assert hash_file(dest_readme) == EMPTY_FILE_HASH
|
assert get_file_sha256_hash(dest_readme) == EMPTY_FILE_HASH
|
||||||
|
|
||||||
|
|
||||||
def test_leave_readme(src_readme, dest_readme):
|
def test_leave_readme(src_readme, dest_readme):
|
||||||
leave_readme(src_readme, dest_readme)
|
leave_readme(src_readme, dest_readme)
|
||||||
|
|
||||||
assert hash_file(dest_readme) == README_HASH
|
assert get_file_sha256_hash(dest_readme) == README_HASH
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import ctypes
|
import ctypes
|
||||||
import hashlib
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
def is_user_admin():
|
def is_user_admin():
|
||||||
|
@ -11,14 +9,5 @@ def is_user_admin():
|
||||||
return ctypes.windll.shell32.IsUserAnAdmin()
|
return ctypes.windll.shell32.IsUserAnAdmin()
|
||||||
|
|
||||||
|
|
||||||
def hash_file(filepath: Path):
|
|
||||||
sha256 = hashlib.sha256()
|
|
||||||
with open(filepath, "rb") as f:
|
|
||||||
for block in iter(lambda: f.read(65536), b""):
|
|
||||||
sha256.update(block)
|
|
||||||
|
|
||||||
return sha256.hexdigest()
|
|
||||||
|
|
||||||
|
|
||||||
def raise_(ex):
|
def raise_(ex):
|
||||||
raise ex
|
raise ex
|
||||||
|
|
Loading…
Reference in New Issue