Merge pull request #1938 from guardicore/1781-rework-windows-agent-deletion

Mark monkey agent for deletion on boot
This commit is contained in:
Mike Salvatore 2022-05-12 06:50:49 -04:00 committed by GitHub
commit e0cec77ec6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 76 additions and 59 deletions

View File

@ -12,8 +12,8 @@ class ScanStatus(Enum):
class UsageEnum(Enum): class UsageEnum(Enum):
SMB = { SMB = {
ScanStatus.USED.value: "SMB exploiter ran the monkey by creating a service via MS-SCMR.", ScanStatus.USED.value: "SMB exploiter ran the agent by creating a service via MS-SCMR.",
ScanStatus.SCANNED.value: "SMB exploiter failed to run the monkey by creating a service " ScanStatus.SCANNED.value: "SMB exploiter failed to run the agent by creating a service "
"via MS-SCMR.", "via MS-SCMR.",
} }
MIMIKATZ = { MIMIKATZ = {
@ -24,19 +24,19 @@ class UsageEnum(Enum):
ScanStatus.USED.value: "WinAPI was called to load mimikatz.", ScanStatus.USED.value: "WinAPI was called to load mimikatz.",
ScanStatus.SCANNED.value: "Monkey tried to call WinAPI to load mimikatz.", ScanStatus.SCANNED.value: "Monkey tried to call WinAPI to load mimikatz.",
} }
DROPPER = { AGENT_WINAPI = {
ScanStatus.USED.value: "WinAPI was used to mark monkey files for deletion on next boot." ScanStatus.USED.value: "WinAPI was used to mark agent's files for deletion on next boot."
} }
SINGLETON_WINAPI = { 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.", "process.",
ScanStatus.SCANNED.value: "WinAPI call to acquire system singleton" ScanStatus.SCANNED.value: "WinAPI call to acquire system singleton"
" for monkey process wasn't successful.", " for agent's process wasn't successful.",
} }
DROPPER_WINAPI = { 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 # 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."

View File

@ -1,5 +1,4 @@
import argparse import argparse
import ctypes
import filecmp import filecmp
import logging import logging
import os import os
@ -8,17 +7,17 @@ import shutil
import subprocess import subprocess
import sys import sys
import time 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.config import WormConfiguration
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
from infection_monkey.utils.commands import ( from infection_monkey.utils.commands import (
build_monkey_commandline_explicitly, build_monkey_commandline_explicitly,
get_monkey_commandline_linux, get_monkey_commandline_linux,
get_monkey_commandline_windows, get_monkey_commandline_windows,
) )
from infection_monkey.utils.environment import is_windows_os 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: if "win32" == sys.platform:
from win32process import DETACHED_PROCESS from win32process import DETACHED_PROCESS
@ -198,23 +197,9 @@ class MonkeyDrops(object):
) )
# mark the file for removal on next boot # mark the file for removal on next boot
dropper_source_path_ctypes = c_char_p(self._config["source_path"].encode()) mark_file_for_deletion_on_windows(
if 0 == ctypes.windll.kernel32.MoveFileExA( WindowsPath(self._config["source_path"]), UsageEnum.DROPPER_WINAPI
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()
logger.info("Dropper cleanup complete") logger.info("Dropper cleanup complete")
except AttributeError: except AttributeError:
logger.error("Invalid configuration options. Failing") logger.error("Invalid configuration options. Failing")

View File

@ -3,7 +3,7 @@ import logging
import os import os
import subprocess import subprocess
import sys import sys
from pathlib import Path from pathlib import Path, WindowsPath
from typing import List from typing import List
import infection_monkey.tunnel as tunnel 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.telemetry.tunnel_telem import TunnelTelem
from infection_monkey.utils.aws_environment_check import run_aws_environment_check 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.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 ( from infection_monkey.utils.monkey_dir import (
create_monkey_dir, create_monkey_dir,
get_monkey_dir_path, get_monkey_dir_path,
@ -410,6 +411,9 @@ class InfectionMonkey:
try: try:
if "win32" == sys.platform: if "win32" == sys.platform:
mark_file_for_deletion_on_windows(
WindowsPath(sys.executable), UsageEnum.AGENT_WINAPI
)
InfectionMonkey._self_delete_windows() InfectionMonkey._self_delete_windows()
else: else:
InfectionMonkey._self_delete_linux() InfectionMonkey._self_delete_linux()

View File

@ -3,7 +3,7 @@ from pathlib import Path
from typing import Iterable, Set from typing import Iterable, Set
from common.utils.file_utils import get_all_regular_files_in_directory 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, file_extension_filter,
filter_files, filter_files,
is_not_shortcut_filter, is_not_shortcut_filter,

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import pytest
from tests.utils import add_files_to_dir, is_user_admin from tests.utils import add_files_to_dir, is_user_admin
from common.utils.file_utils import get_all_regular_files_in_directory 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, file_extension_filter,
filter_files, filter_files,
is_not_shortcut_filter, is_not_shortcut_filter,