Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Agent: Add ExploiterWrapper #1739

Merged
merged 1 commit into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions monkey/infection_monkey/exploit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .exploiter_wrapper import ExploiterWrapper
32 changes: 32 additions & 0 deletions monkey/infection_monkey/exploit/exploiter_wrapper.py
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 9 additions & 2 deletions monkey/infection_monkey/monkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
MimikatzCredentialCollector,
SSHCredentialCollector,
)
from infection_monkey.exploit import ExploiterWrapper
from infection_monkey.exploit.sshexec import SSHExploiter
from infection_monkey.i_puppet import IPuppet, PluginType
from infection_monkey.master import AutomatedMaster
Expand Down Expand Up @@ -195,7 +196,7 @@ def _get_local_network_interfaces():
return local_network_interfaces

def _build_puppet(self) -> IPuppet:
puppet = Puppet(self.telemetry_messenger)
puppet = Puppet()

puppet.load_plugin(
"MimikatzCollector",
Expand All @@ -214,7 +215,13 @@ def _build_puppet(self) -> IPuppet:
puppet.load_plugin("smb", SMBFingerprinter(), 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)

Expand Down
6 changes: 2 additions & 4 deletions monkey/infection_monkey/puppet/puppet.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,16 @@
)
from infection_monkey.model import VictimHost

from ..telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
from .mock_puppet import MockPuppet
from .plugin_registry import PluginRegistry

logger = logging.getLogger()


class Puppet(IPuppet):
def __init__(self, telemetry_messenger: ITelemetryMessenger) -> None:
def __init__(self) -> None:
self._mock_puppet = MockPuppet()
self._plugin_registry = PluginRegistry()
self._telemetry_messenger = telemetry_messenger

def load_plugin(self, plugin_name: str, plugin: object, plugin_type: PluginType) -> None:
self._plugin_registry.load_plugin(plugin_name, plugin, plugin_type)
Expand Down Expand Up @@ -63,7 +61,7 @@ def exploit_host(
self, name: str, host: VictimHost, options: Dict, interrupt: threading.Event
) -> ExploiterResultData:
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):
payload = self._plugin_registry.get_plugin(name, PluginType.PAYLOAD)
Expand Down
15 changes: 4 additions & 11 deletions monkey/tests/unit_tests/infection_monkey/puppet/test_puppet.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import threading
from unittest.mock import MagicMock

import pytest

from infection_monkey.i_puppet import PluginType
from infection_monkey.puppet.puppet import Puppet


@pytest.fixture
def mock_telemetry_messenger():
return MagicMock()


def test_puppet_run_payload_success(monkeypatch, mock_telemetry_messenger):
p = Puppet(mock_telemetry_messenger)
def test_puppet_run_payload_success():
p = Puppet()

payload = MagicMock()
payload_name = "PayloadOne"
Expand All @@ -24,8 +17,8 @@ def test_puppet_run_payload_success(monkeypatch, mock_telemetry_messenger):
payload.run.assert_called_once()


def test_puppet_run_multiple_payloads(monkeypatch, mock_telemetry_messenger):
p = Puppet(mock_telemetry_messenger)
def test_puppet_run_multiple_payloads():
p = Puppet()

payload_1 = MagicMock()
payload1_name = "PayloadOne"
Expand Down