diff --git a/monkey/common/utils/attack_utils.py b/monkey/common/utils/attack_utils.py index d86b0ba55..f1d4e21b0 100644 --- a/monkey/common/utils/attack_utils.py +++ b/monkey/common/utils/attack_utils.py @@ -12,8 +12,8 @@ class ScanStatus(Enum): class UsageEnum(Enum): SMB = { - ScanStatus.USED.value: "SMB exploiter ran the monkey by creating a service via MS-SCMR.", - ScanStatus.SCANNED.value: "SMB exploiter failed to run the monkey by creating a service " + ScanStatus.USED.value: "SMB exploiter ran the agent by creating a service via MS-SCMR.", + ScanStatus.SCANNED.value: "SMB exploiter failed to run the agent by creating a service " "via MS-SCMR.", } MIMIKATZ = { @@ -24,19 +24,19 @@ class UsageEnum(Enum): ScanStatus.USED.value: "WinAPI was called to load mimikatz.", ScanStatus.SCANNED.value: "Monkey tried to call WinAPI to load mimikatz.", } - DROPPER = { - ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot." + AGENT_WINAPI = { + ScanStatus.USED.value: "WinAPI was used to mark agent's files for deletion on next boot." } SINGLETON_WINAPI = { - ScanStatus.USED.value: "WinAPI was called to acquire system singleton for monkey's " + ScanStatus.USED.value: "WinAPI was called to acquire system singleton for agent's " "process.", ScanStatus.SCANNED.value: "WinAPI call to acquire system singleton" - " for monkey process wasn't successful.", + " for agent's process wasn't successful.", } DROPPER_WINAPI = { - ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot." + ScanStatus.USED.value: "WinAPI was used to mark dropper files for deletion on next boot." } # Dict that describes what BITS job was used for -BITS_UPLOAD_STRING = "BITS job was used to upload monkey to a remote system." +BITS_UPLOAD_STRING = "BITS job was used to upload agent to a remote system." diff --git a/monkey/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py index 90d6712d5..4bbc272ea 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -1,5 +1,4 @@ import argparse -import ctypes import filecmp import logging import os @@ -8,17 +7,17 @@ import shutil import subprocess import sys import time -from ctypes import c_char_p +from pathlib import WindowsPath -from common.utils.attack_utils import ScanStatus, UsageEnum +from common.utils.attack_utils import UsageEnum from infection_monkey.config import WormConfiguration -from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from infection_monkey.utils.commands import ( build_monkey_commandline_explicitly, get_monkey_commandline_linux, get_monkey_commandline_windows, ) from infection_monkey.utils.environment import is_windows_os +from infection_monkey.utils.file_utils import mark_file_for_deletion_on_windows if "win32" == sys.platform: from win32process import DETACHED_PROCESS @@ -198,23 +197,9 @@ class MonkeyDrops(object): ) # mark the file for removal on next boot - dropper_source_path_ctypes = c_char_p(self._config["source_path"].encode()) - if 0 == ctypes.windll.kernel32.MoveFileExA( - dropper_source_path_ctypes, None, MOVEFILE_DELAY_UNTIL_REBOOT - ): - logger.debug( - "Error marking source file '%s' for deletion on next boot (error " - "%d)", - self._config["source_path"], - ctypes.windll.kernel32.GetLastError(), - ) - else: - logger.debug( - "Dropper source file '%s' is marked for deletion on next boot", - self._config["source_path"], - ) - T1106Telem(ScanStatus.USED, UsageEnum.DROPPER_WINAPI).send() - + mark_file_for_deletion_on_windows( + WindowsPath(self._config["source_path"]), UsageEnum.DROPPER_WINAPI + ) logger.info("Dropper cleanup complete") except AttributeError: logger.error("Invalid configuration options. Failing") diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index e31c62cad..7f09adde4 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -3,7 +3,7 @@ import logging import os import subprocess import sys -from pathlib import Path +from pathlib import Path, WindowsPath from typing import List import infection_monkey.tunnel as tunnel @@ -70,6 +70,7 @@ from infection_monkey.telemetry.state_telem import StateTelem from infection_monkey.telemetry.tunnel_telem import TunnelTelem from infection_monkey.utils.aws_environment_check import run_aws_environment_check from infection_monkey.utils.environment import is_windows_os +from infection_monkey.utils.file_utils import mark_file_for_deletion_on_windows from infection_monkey.utils.monkey_dir import ( create_monkey_dir, get_monkey_dir_path, @@ -410,6 +411,9 @@ class InfectionMonkey: try: if "win32" == sys.platform: + mark_file_for_deletion_on_windows( + WindowsPath(sys.executable), UsageEnum.AGENT_WINAPI + ) InfectionMonkey._self_delete_windows() else: InfectionMonkey._self_delete_linux() diff --git a/monkey/infection_monkey/payload/ransomware/file_selectors.py b/monkey/infection_monkey/payload/ransomware/file_selectors.py index 1857d7d63..901b6b571 100644 --- a/monkey/infection_monkey/payload/ransomware/file_selectors.py +++ b/monkey/infection_monkey/payload/ransomware/file_selectors.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Iterable, Set from common.utils.file_utils import get_all_regular_files_in_directory -from infection_monkey.utils.dir_utils import ( +from infection_monkey.utils.file_utils import ( file_extension_filter, filter_files, is_not_shortcut_filter, diff --git a/monkey/infection_monkey/utils/dir_utils.py b/monkey/infection_monkey/utils/dir_utils.py deleted file mode 100644 index 9cd20a316..000000000 --- a/monkey/infection_monkey/utils/dir_utils.py +++ /dev/null @@ -1,27 +0,0 @@ -from pathlib import Path -from typing import Callable, Iterable, Set - - -def filter_files( - files: Iterable[Path], file_filters: Iterable[Callable[[Path], bool]] -) -> Iterable[Path]: - filtered_files = files - for file_filter in file_filters: - filtered_files = filter(file_filter, filtered_files) - - return filtered_files - - -def file_extension_filter(file_extensions: Set) -> Callable[[Path], bool]: - def inner_filter(f: Path) -> bool: - return f.suffix in file_extensions - - return inner_filter - - -def is_not_symlink_filter(f: Path) -> bool: - return not f.is_symlink() - - -def is_not_shortcut_filter(f: Path) -> bool: - return f.suffix != ".lnk" diff --git a/monkey/infection_monkey/utils/file_utils.py b/monkey/infection_monkey/utils/file_utils.py new file mode 100644 index 000000000..66235b9b1 --- /dev/null +++ b/monkey/infection_monkey/utils/file_utils.py @@ -0,0 +1,55 @@ +import ctypes +import logging +from pathlib import Path, WindowsPath +from typing import Callable, Iterable, Set + +from common.utils.attack_utils import ScanStatus, UsageEnum + +logger = logging.getLogger(__name__) + +MOVEFILE_DELAY_UNTIL_REBOOT = 4 + + +def filter_files( + files: Iterable[Path], file_filters: Iterable[Callable[[Path], bool]] +) -> Iterable[Path]: + filtered_files = files + for file_filter in file_filters: + filtered_files = filter(file_filter, filtered_files) + + return filtered_files + + +def file_extension_filter(file_extensions: Set) -> Callable[[Path], bool]: + def inner_filter(f: Path) -> bool: + return f.suffix in file_extensions + + return inner_filter + + +def is_not_symlink_filter(f: Path) -> bool: + return not f.is_symlink() + + +def is_not_shortcut_filter(f: Path) -> bool: + return f.suffix != ".lnk" + + +def mark_file_for_deletion_on_windows(file_path: WindowsPath, usage: UsageEnum): + from infection_monkey.telemetry.attack.t1106_telem import T1106Telem + + file_source_path_ctypes = ctypes.c_char_p(str(file_path).encode()) + + mark_file_response = ctypes.windll.kernel32.MoveFileExA( + file_source_path_ctypes, None, MOVEFILE_DELAY_UNTIL_REBOOT + ) + + if mark_file_response == 0: + logger.debug( + f"Error marking file {file_path} for deletion on next boot:" + f"{ctypes.windll.kernel32.GetLastError()}" + ) + return + + logger.debug(f"File {file_path} is marked for deletion on next boot") + T1106Telem(ScanStatus.USED, usage).send() diff --git a/monkey/tests/unit_tests/infection_monkey/utils/__init__.py b/monkey/tests/unit_tests/infection_monkey/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/tests/unit_tests/infection_monkey/utils/test_dir_utils.py b/monkey/tests/unit_tests/infection_monkey/utils/test_file_utils.py similarity index 98% rename from monkey/tests/unit_tests/infection_monkey/utils/test_dir_utils.py rename to monkey/tests/unit_tests/infection_monkey/utils/test_file_utils.py index fe7b499bf..2154f2c9e 100644 --- a/monkey/tests/unit_tests/infection_monkey/utils/test_dir_utils.py +++ b/monkey/tests/unit_tests/infection_monkey/utils/test_file_utils.py @@ -4,7 +4,7 @@ import pytest from tests.utils import add_files_to_dir, is_user_admin from common.utils.file_utils import get_all_regular_files_in_directory -from infection_monkey.utils.dir_utils import ( +from infection_monkey.utils.file_utils import ( file_extension_filter, filter_files, is_not_shortcut_filter, diff --git a/monkey/tests/unit_tests/monkey_island/cc/server_utils/__init__.py b/monkey/tests/unit_tests/monkey_island/cc/server_utils/__init__.py new file mode 100644 index 000000000..e69de29bb