Island, Agent: Add custom user PBA to puppet and master

This commit is contained in:
vakarisz 2022-03-25 16:38:13 +02:00
parent ee0561a061
commit 23b8c351fb
7 changed files with 53 additions and 31 deletions

View File

@ -3,7 +3,7 @@ import threading
from collections import namedtuple from collections import namedtuple
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Dict, Iterable, List, Mapping, Sequence from typing import Any, Dict, Iterable, List, Mapping, Sequence
from infection_monkey.model import VictimHost from infection_monkey.model import VictimHost
@ -67,6 +67,14 @@ class IPuppet(metaclass=abc.ABCMeta):
:rtype: Iterable[PostBreachData] :rtype: Iterable[PostBreachData]
""" """
@abc.abstractmethod
def run_custom_pba(self, options: Mapping[str, Any]) -> PostBreachData:
"""
Runs a user configured post breach action (PBA)
:param Dict options: A dictionary containing options that modify the behavior of the PBA
:rtype: PostBreachData
"""
@abc.abstractmethod @abc.abstractmethod
def ping(self, host: str, timeout: float) -> PingScanData: def ping(self, host: str, timeout: float) -> PingScanData:
""" """

View File

@ -1,9 +1,10 @@
import logging import logging
import threading import threading
import time import time
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple
from infection_monkey.credential_store import ICredentialsStore from infection_monkey.credential_store import ICredentialsStore
from common.common_consts.post_breach_consts import POST_BREACH_FILE_EXECUTION
from infection_monkey.i_control_channel import IControlChannel, IslandCommunicationError from infection_monkey.i_control_channel import IControlChannel, IslandCommunicationError
from infection_monkey.i_master import IMaster from infection_monkey.i_master import IMaster
from infection_monkey.i_puppet import IPuppet from infection_monkey.i_puppet import IPuppet
@ -154,9 +155,9 @@ class AutomatedMaster(IMaster):
), ),
) )
pba_thread = create_daemon_thread( pba_thread = create_daemon_thread(
target=self._run_plugins, target=self._run_PBAs,
name="PBAThread", name="PBAThread",
args=(config["post_breach_actions"].items(), "post-breach action", self._run_pba), args=(config["post_breach_actions"].items(), self._run_pba, config["custom_pbas"]),
) )
credential_collector_thread.start() credential_collector_thread.start()
@ -212,6 +213,15 @@ class AutomatedMaster(IMaster):
self._puppet.run_payload(name, options, self._stop) self._puppet.run_payload(name, options, self._stop)
def _run_PBAs(
self, plugins: Iterable[Any], callback: Callable[[Any], None], custom_pba_options: Mapping
):
self._run_plugins(plugins, "post-breach action", callback)
command, result = self._puppet.run_custom_pba(custom_pba_options)
telem = PostBreachTelem(POST_BREACH_FILE_EXECUTION, command, result)
self._telemetry_messenger.send_telemetry(telem)
def _run_plugins( def _run_plugins(
self, plugins: Iterable[Any], plugin_type: str, callback: Callable[[Any], None] self, plugins: Iterable[Any], plugin_type: str, callback: Callable[[Any], None]
): ):

View File

@ -1,5 +1,6 @@
import logging import logging
import os import os
from typing import Any, Mapping
from common.common_consts.post_breach_consts import POST_BREACH_FILE_EXECUTION from common.common_consts.post_breach_consts import POST_BREACH_FILE_EXECUTION
from common.utils.attack_utils import ScanStatus from common.utils.attack_utils import ScanStatus
@ -24,32 +25,32 @@ class UsersPBA(PBA):
Defines user's configured post breach action. Defines user's configured post breach action.
""" """
def __init__(self, telemetry_messenger: ITelemetryMessenger): def __init__(self, options: Mapping[str, Any], telemetry_messenger: ITelemetryMessenger):
super(UsersPBA, self).__init__(telemetry_messenger, POST_BREACH_FILE_EXECUTION) super(UsersPBA, self).__init__(telemetry_messenger, POST_BREACH_FILE_EXECUTION)
self.filename = "" self.filename = ""
if not is_windows_os(): if is_windows_os():
# Add linux commands to PBA's
if WormConfiguration.PBA_linux_filename:
self.filename = WormConfiguration.PBA_linux_filename
if WormConfiguration.custom_PBA_linux_cmd:
# Add change dir command, because user will try to access his file
self.command = (
DIR_CHANGE_LINUX % get_monkey_dir_path()
) + WormConfiguration.custom_PBA_linux_cmd
elif WormConfiguration.custom_PBA_linux_cmd:
self.command = WormConfiguration.custom_PBA_linux_cmd
else:
# Add windows commands to PBA's # Add windows commands to PBA's
if WormConfiguration.PBA_windows_filename: if options["windows_filename"]:
self.filename = WormConfiguration.PBA_windows_filename self.filename = options["windows_filename"]
if WormConfiguration.custom_PBA_windows_cmd: if options["windows_command"]:
# Add change dir command, because user will try to access his file # Add change dir command, because user will try to access his file
self.command = ( self.command = (DIR_CHANGE_WINDOWS % get_monkey_dir_path()) + options[
DIR_CHANGE_WINDOWS % get_monkey_dir_path() "windows_command"
) + WormConfiguration.custom_PBA_windows_cmd ]
elif WormConfiguration.custom_PBA_windows_cmd: elif options["windows_command"]:
self.command = WormConfiguration.custom_PBA_windows_cmd self.command = options["windows_command"]
else:
# Add linux commands to PBA's
if options["linux_filename"]:
self.filename = options["linux_filename"]
if options["linux_command"]:
# Add change dir command, because user will try to access his file
self.command = (DIR_CHANGE_LINUX % get_monkey_dir_path()) + options[
"linux_command"
]
elif options["linux_command"]:
self.command = options["linux_command"]
def _execute_default(self): def _execute_default(self):
if self.filename: if self.filename:
@ -57,12 +58,12 @@ class UsersPBA(PBA):
return super(UsersPBA, self)._execute_default() return super(UsersPBA, self)._execute_default()
@staticmethod @staticmethod
def should_run(class_name): def should_run(options):
if not is_windows_os(): if not is_windows_os():
if WormConfiguration.PBA_linux_filename or WormConfiguration.custom_PBA_linux_cmd: if options["linux_filename"] or options["linux_command"]:
return True return True
else: else:
if WormConfiguration.PBA_windows_filename or WormConfiguration.custom_PBA_windows_cmd: if options["windows_filename"] or options["windows_command"]:
return True return True
return False return False

View File

@ -452,7 +452,7 @@ class ConfigService:
for pba in config.get("post_breach_actions", []): for pba in config.get("post_breach_actions", []):
formatted_pbas_config[pba] = {} formatted_pbas_config[pba] = {}
formatted_pbas_config["Custom"] = { config["custom_pbas"] = {
"linux_command": config.get(flat_linux_command_field, ""), "linux_command": config.get(flat_linux_command_field, ""),
"linux_filename": config.get(flat_linux_filename_field, ""), "linux_filename": config.get(flat_linux_filename_field, ""),
"windows_command": config.get(flat_windows_command_field, ""), "windows_command": config.get(flat_windows_command_field, ""),

View File

@ -1,6 +1,6 @@
import logging import logging
import threading import threading
from typing import Dict, Iterable, List, Sequence from typing import Any, Dict, Iterable, List, Mapping, Sequence
from infection_monkey.credential_collectors import LMHash, Password, SSHKeypair, Username from infection_monkey.credential_collectors import LMHash, Password, SSHKeypair, Username
from infection_monkey.i_puppet import ( from infection_monkey.i_puppet import (
@ -57,6 +57,9 @@ class MockPuppet(IPuppet):
else: else:
return [PostBreachData(name, "pba command 2", ["pba result 2", False])] return [PostBreachData(name, "pba command 2", ["pba result 2", False])]
def run_custom_pba(self, options: Mapping[str, Any]) -> PostBreachData:
pass
def ping(self, host: str, timeout: float = 1) -> PingScanData: def ping(self, host: str, timeout: float = 1) -> PingScanData:
logger.debug(f"run_ping({host}, {timeout})") logger.debug(f"run_ping({host}, {timeout})")
if host == DOT_1: if host == DOT_1:

View File

@ -2,7 +2,7 @@ from unittest.mock import MagicMock
import pytest import pytest
from infection_monkey.post_breach.actions.users_custom_pba import UsersPBA from infection_monkey.post_breach.custom_pba.users_custom_pba import UsersPBA
MONKEY_DIR_PATH = "/dir/to/monkey/" MONKEY_DIR_PATH = "/dir/to/monkey/"
CUSTOM_LINUX_CMD = "command-for-linux" CUSTOM_LINUX_CMD = "command-for-linux"