Merge pull request #1333 from guardicore/ransomware-skip-encrypt-readme

Ransomware skip encrypt readme
This commit is contained in:
Mike Salvatore 2021-07-19 06:59:41 -04:00 committed by GitHub
commit 81f7de74ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 76 additions and 29 deletions

3
.gitattributes vendored
View File

@ -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

View File

@ -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()

View File

@ -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"

View File

@ -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

View File

@ -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}")

View File

@ -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"

View File

@ -0,0 +1 @@
Don't change me!

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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