From d9046b39a8cbfcc0252b711a30b6b9cbc0c1c105 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 11 May 2022 16:14:48 +0200 Subject: [PATCH] Agent: Mark monkey agent for deletion on reboot Each windows agent which will be runned, we are going to to mark it for deletion on boot and also try it to delete it using delayed command. --- monkey/common/utils/attack_utils.py | 16 +++--- monkey/infection_monkey/dropper.py | 27 ++------- monkey/infection_monkey/monkey.py | 6 +- .../payload/ransomware/file_selectors.py | 2 +- monkey/infection_monkey/utils/dir_utils.py | 27 --------- monkey/infection_monkey/utils/file_utils.py | 55 +++++++++++++++++++ .../{test_dir_utils.py => test_file_utils.py} | 2 +- 7 files changed, 76 insertions(+), 59 deletions(-) delete mode 100644 monkey/infection_monkey/utils/dir_utils.py create mode 100644 monkey/infection_monkey/utils/file_utils.py rename monkey/tests/unit_tests/infection_monkey/utils/{test_dir_utils.py => test_file_utils.py} (98%) 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/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,