Agent: Add ExploiterWrapper

Issue #1605
PR #1739
This commit is contained in:
Mike Salvatore 2022-02-23 11:36:21 -05:00
parent 2431e2f20b
commit 5cbcb88dd6
5 changed files with 48 additions and 17 deletions

View File

@ -0,0 +1 @@
from .exploiter_wrapper import ExploiterWrapper

View File

@ -0,0 +1,32 @@
from typing import Dict, Type
from infection_monkey.model import VictimHost
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
from .HostExploiter import HostExploiter
class ExploiterWrapper:
"""
This class is a temporary measure to allow existing exploiters to play nicely within the
confines of the IPuppet interface. It keeps a reference to an ITelemetryMessenger that is passed
to all exploiters. Additionally, it constructs a new instance of the exploiter for each call to
exploit_host(). When exploiters are refactored into plugins, this class will likely go away.
"""
class Inner:
def __init__(
self, exploit_class: Type[HostExploiter], telemetry_messenger: ITelemetryMessenger
):
self._exploit_class = exploit_class
self._telemetry_messenger = telemetry_messenger
def exploit_host(self, host: VictimHost, options: Dict):
exploiter = self._exploit_class()
return exploiter.exploit_host(host, self._telemetry_messenger, options)
def __init__(self, telemetry_messenger: ITelemetryMessenger):
self._telemetry_messenger = telemetry_messenger
def wrap(self, exploit_class: Type[HostExploiter]):
return ExploiterWrapper.Inner(exploit_class, self._telemetry_messenger)

View File

@ -16,6 +16,7 @@ from infection_monkey.credential_collectors import (
MimikatzCredentialCollector, MimikatzCredentialCollector,
SSHCredentialCollector, SSHCredentialCollector,
) )
from infection_monkey.exploit import ExploiterWrapper
from infection_monkey.exploit.sshexec import SSHExploiter from infection_monkey.exploit.sshexec import SSHExploiter
from infection_monkey.i_puppet import IPuppet, PluginType from infection_monkey.i_puppet import IPuppet, PluginType
from infection_monkey.master import AutomatedMaster from infection_monkey.master import AutomatedMaster
@ -195,7 +196,7 @@ class InfectionMonkey:
return local_network_interfaces return local_network_interfaces
def _build_puppet(self) -> IPuppet: def _build_puppet(self) -> IPuppet:
puppet = Puppet(self.telemetry_messenger) puppet = Puppet()
puppet.load_plugin( puppet.load_plugin(
"MimikatzCollector", "MimikatzCollector",
@ -214,7 +215,13 @@ class InfectionMonkey:
puppet.load_plugin("smb", SMBFingerprinter(), PluginType.FINGERPRINTER) puppet.load_plugin("smb", SMBFingerprinter(), PluginType.FINGERPRINTER)
puppet.load_plugin("ssh", SSHFingerprinter(), PluginType.FINGERPRINTER) puppet.load_plugin("ssh", SSHFingerprinter(), PluginType.FINGERPRINTER)
puppet.load_plugin("SSHExploiter", SSHExploiter(), PluginType.EXPLOITER) exploit_wrapper = ExploiterWrapper(self.telemetry_messenger)
puppet.load_plugin(
"SSHExploiter",
exploit_wrapper.wrap(SSHExploiter),
PluginType.EXPLOITER,
)
puppet.load_plugin("ransomware", RansomwarePayload(), PluginType.PAYLOAD) puppet.load_plugin("ransomware", RansomwarePayload(), PluginType.PAYLOAD)

View File

@ -15,7 +15,6 @@ from infection_monkey.i_puppet import (
) )
from infection_monkey.model import VictimHost from infection_monkey.model import VictimHost
from ..telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
from .mock_puppet import MockPuppet from .mock_puppet import MockPuppet
from .plugin_registry import PluginRegistry from .plugin_registry import PluginRegistry
@ -23,10 +22,9 @@ logger = logging.getLogger()
class Puppet(IPuppet): class Puppet(IPuppet):
def __init__(self, telemetry_messenger: ITelemetryMessenger) -> None: def __init__(self) -> None:
self._mock_puppet = MockPuppet() self._mock_puppet = MockPuppet()
self._plugin_registry = PluginRegistry() self._plugin_registry = PluginRegistry()
self._telemetry_messenger = telemetry_messenger
def load_plugin(self, plugin_name: str, plugin: object, plugin_type: PluginType) -> None: def load_plugin(self, plugin_name: str, plugin: object, plugin_type: PluginType) -> None:
self._plugin_registry.load_plugin(plugin_name, plugin, plugin_type) self._plugin_registry.load_plugin(plugin_name, plugin, plugin_type)
@ -63,7 +61,7 @@ class Puppet(IPuppet):
self, name: str, host: VictimHost, options: Dict, interrupt: threading.Event self, name: str, host: VictimHost, options: Dict, interrupt: threading.Event
) -> ExploiterResultData: ) -> ExploiterResultData:
exploiter = self._plugin_registry.get_plugin(name, PluginType.EXPLOITER) exploiter = self._plugin_registry.get_plugin(name, PluginType.EXPLOITER)
return exploiter.exploit_host(host, self._telemetry_messenger, options) return exploiter.exploit_host(host, options)
def run_payload(self, name: str, options: Dict, interrupt: threading.Event): def run_payload(self, name: str, options: Dict, interrupt: threading.Event):
payload = self._plugin_registry.get_plugin(name, PluginType.PAYLOAD) payload = self._plugin_registry.get_plugin(name, PluginType.PAYLOAD)

View File

@ -1,19 +1,12 @@
import threading import threading
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest
from infection_monkey.i_puppet import PluginType from infection_monkey.i_puppet import PluginType
from infection_monkey.puppet.puppet import Puppet from infection_monkey.puppet.puppet import Puppet
@pytest.fixture def test_puppet_run_payload_success():
def mock_telemetry_messenger(): p = Puppet()
return MagicMock()
def test_puppet_run_payload_success(monkeypatch, mock_telemetry_messenger):
p = Puppet(mock_telemetry_messenger)
payload = MagicMock() payload = MagicMock()
payload_name = "PayloadOne" payload_name = "PayloadOne"
@ -24,8 +17,8 @@ def test_puppet_run_payload_success(monkeypatch, mock_telemetry_messenger):
payload.run.assert_called_once() payload.run.assert_called_once()
def test_puppet_run_multiple_payloads(monkeypatch, mock_telemetry_messenger): def test_puppet_run_multiple_payloads():
p = Puppet(mock_telemetry_messenger) p = Puppet()
payload_1 = MagicMock() payload_1 = MagicMock()
payload1_name = "PayloadOne" payload1_name = "PayloadOne"