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

Modify ExploiterResultData #1728

Merged
merged 12 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import glob
import logging
import os
import pwd

try: # can't import on Windows
import pwd
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is solved by Vakaris in parsing credential telemetry. Check this commit

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave this in since this'll most likely get merged before that.

Copy link
Collaborator

@mssalvatore mssalvatore Feb 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we move this import to line 37, then we don't need to try/except the import, since _get_home_dirs() only runs if the check on line 24 passes.

except ModuleNotFoundError:
pass
from typing import Dict, Iterable

from common.utils.attack_utils import ScanStatus
Expand Down
2 changes: 1 addition & 1 deletion monkey/infection_monkey/exploit/HostExploiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def is_os_supported(self):
def send_exploit_telemetry(self, name: str, result: bool):
from infection_monkey.telemetry.exploit_telem import ExploitTelem

ExploitTelem(
ExploitTelem( # stale code
name=name,
host=self.host,
result=result,
Expand Down
3 changes: 2 additions & 1 deletion monkey/infection_monkey/i_puppet/i_puppet.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class UnknownPluginError(Exception):


ExploiterResultData = namedtuple(
"ExploiterResultData", ["success", "info", "attempts", "error_message"]
"ExploiterResultData",
["exploitation_success", "propagation_success", "os", "info", "attempts", "error_message"],
)
PingScanData = namedtuple("PingScanData", ["response_received", "os"])
PortScanData = namedtuple("PortScanData", ["port", "status", "banner", "service"])
Expand Down
2 changes: 1 addition & 1 deletion monkey/infection_monkey/master/exploiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def _run_all_exploiters(
exploiter_results = self._run_exploiter(exploiter_name, victim_host, stop)
results_callback(exploiter_name, victim_host, exploiter_results)

if exploiter_name != "ZerologonExploiter" and exploiter_results.success:
if exploiter_results.propagation_success:
break

def _run_exploiter(
Expand Down
40 changes: 32 additions & 8 deletions monkey/infection_monkey/master/mock_master.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,44 @@ def _fingerprint(self):

def _exploit(self):
logger.info("Exploiting victims")
result, info, attempts, error_message = self._puppet.exploit_host(
"PowerShellExploiter", "10.0.0.1", {}, None
)
(
exploitation_result,
propagation_result,
os,
info,
attempts,
error_message,
) = self._puppet.exploit_host("PowerShellExploiter", "10.0.0.1", {}, None)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of unpacking this, let's modify ExploitTelem to accept an ExploiterResultsData object.

logger.info(f"Attempts for exploiting {attempts}")
self._telemetry_messenger.send_telemetry(
ExploitTelem("PowerShellExploiter", self._hosts["10.0.0.1"], result, info, attempts)
ExploitTelem(
"PowerShellExploiter",
self._hosts["10.0.0.1"],
exploitation_result,
propagation_result,
info,
attempts,
)
)

result, info, attempts, error_message = self._puppet.exploit_host(
"SSHExploiter", "10.0.0.3", {}, None
)
(
exploitation_result,
propagation_result,
os,
info,
attempts,
error_message,
) = self._puppet.exploit_host("SSHExploiter", "10.0.0.3", {}, None)
logger.info(f"Attempts for exploiting {attempts}")
self._telemetry_messenger.send_telemetry(
ExploitTelem("SSHExploiter", self._hosts["10.0.0.3"], result, info, attempts)
ExploitTelem(
"SSHExploiter",
self._hosts["10.0.0.3"],
exploitation_result,
propagation_result,
info,
attempts,
)
)
logger.info("Finished exploiting victims")

Expand Down
18 changes: 15 additions & 3 deletions monkey/infection_monkey/master/propagator.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,25 @@ def _exploit_hosts(
def _process_exploit_attempts(
self, exploiter_name: str, host: VictimHost, result: ExploiterResultData
):
if result.success:
if result.propagation_success:
logger.info(f"Successfully propagated to {host} using {exploiter_name}")
elif result.exploitation_success:
logger.info(
f"Successfully exploited (but did not propagate to) {host} using {exploiter_name}"
)
else:
logger.info(
f"Failed to propagate to {host} using {exploiter_name}: {result.error_message}"
f"Failed to exploit or propagate to {host} using {exploiter_name}: "
f"{result.error_message}"
)

self._telemetry_messenger.send_telemetry(
ExploitTelem(exploiter_name, host, result.success, result.info, result.attempts)
ExploitTelem(
exploiter_name,
host,
result.exploitation_success,
result.propagation_success,
result.info,
result.attempts,
)
)
32 changes: 25 additions & 7 deletions monkey/infection_monkey/puppet/mock_puppet.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,25 +177,43 @@ def exploit_host(
"vulnerable_ports": [22],
"executed_cmds": [],
}
os_windows = "windows"
os_linux = "linux"

successful_exploiters = {
DOT_1: {
"PowerShellExploiter": ExploiterResultData(True, info_powershell, attempts, None),
"ZerologonExploiter": ExploiterResultData(False, {}, [], "Zerologon failed"),
"SSHExploiter": ExploiterResultData(False, info_ssh, attempts, "Failed exploiting"),
"PowerShellExploiter": ExploiterResultData(
True, True, os_windows, info_powershell, attempts, None
),
"ZerologonExploiter": ExploiterResultData(
False, False, os_windows, {}, [], "Zerologon failed"
),
"SSHExploiter": ExploiterResultData(
False, False, os_linux, info_ssh, attempts, "Failed exploiting"
),
},
DOT_3: {
"PowerShellExploiter": ExploiterResultData(
False, info_powershell, attempts, "PowerShell Exploiter Failed"
False,
False,
os_windows,
info_powershell,
attempts,
"PowerShell Exploiter Failed",
),
"SSHExploiter": ExploiterResultData(
False, False, os_linux, info_ssh, attempts, "Failed exploiting"
),
"SSHExploiter": ExploiterResultData(False, info_ssh, attempts, "Failed exploiting"),
"ZerologonExploiter": ExploiterResultData(True, {}, [], None),
"ZerologonExploiter": ExploiterResultData(True, False, os_windows, {}, [], None),
},
}

try:
return successful_exploiters[host][name]
except KeyError:
return ExploiterResultData(False, {}, [], f"{name} failed for host {host}")
return ExploiterResultData(
False, False, os_linux, {}, [], f"{name} failed for host {host}"
)

def run_payload(self, name: str, options: Dict, interrupt: threading.Event):
logger.debug(f"run_payload({name}, {options})")
Expand Down
21 changes: 17 additions & 4 deletions monkey/infection_monkey/telemetry/exploit_telem.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,41 @@


class ExploitTelem(BaseTelem):
def __init__(self, name: str, host: VictimHost, result: bool, info: Dict, attempts: List):
def __init__(
self,
name: str,
host: VictimHost,
exploitation_result: bool,
propagation_result: bool,
info: Dict,
attempts: List,
):
"""
Default exploit telemetry constructor
:param name: The name of exploiter used
:param host: The host machine
:param result: The result from the 'exploit_host' method
:param exploitation_result: The result of the exploitation attempt from the 'exploit_host'
method
:param propagation_result: The result of the propagation attempt from the 'exploit_host'
method
:param info: Information about the exploiter
:param attempts: Information about the exploiter's attempts
"""
super(ExploitTelem, self).__init__()

self.name = name
self.host = host.__dict__
self.result = result
self.exploitation_result = exploitation_result
self.propagation_result = propagation_result
self.info = info
self.attempts = attempts

telem_category = TelemCategoryEnum.EXPLOIT

def get_data(self) -> Dict:
return {
"result": self.result,
"exploitation_result": self.exploitation_result,
"propagation_result": self.propagation_result,
"machine": self.host,
"exploiter": self.name,
"info": self.info,
Expand Down
28 changes: 18 additions & 10 deletions monkey/tests/unit_tests/infection_monkey/master/test_propagator.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ def build_victim_host(self, network_address: NetworkAddress) -> VictimHost:
},
}

os_windows = "windows"

os_linux = "linux"


@pytest.fixture
def mock_ip_scanner():
Expand Down Expand Up @@ -184,34 +188,38 @@ def exploit_hosts(
results_callback(
"PowerShellExploiter",
host,
ExploiterResultData(True, {}, {}, None),
ExploiterResultData(True, True, os_windows, {}, {}, None),
)
results_callback(
"SSHExploiter",
host,
ExploiterResultData(False, {}, {}, "SSH FAILED for .1"),
ExploiterResultData(False, False, os_linux, {}, {}, "SSH FAILED for .1"),
)
elif host.ip_addr.endswith(".2"):
results_callback(
"PowerShellExploiter",
host,
ExploiterResultData(False, {}, {}, "POWERSHELL FAILED for .2"),
ExploiterResultData(
False, False, os_windows, {}, {}, "POWERSHELL FAILED for .2"
),
)
results_callback(
"SSHExploiter",
host,
ExploiterResultData(False, {}, {}, "SSH FAILED for .2"),
ExploiterResultData(False, False, os_linux, {}, {}, "SSH FAILED for .2"),
)
elif host.ip_addr.endswith(".3"):
results_callback(
"PowerShellExploiter",
host,
ExploiterResultData(False, {}, {}, "POWERSHELL FAILED for .3"),
ExploiterResultData(
False, False, os_windows, {}, {}, "POWERSHELL FAILED for .3"
),
)
results_callback(
"SSHExploiter",
host,
ExploiterResultData(True, {}, {}, None),
ExploiterResultData(True, True, os_linux, {}, {}, None),
)


Expand Down Expand Up @@ -246,14 +254,14 @@ def test_exploiter_result_processing(

if ip.endswith(".1"):
if data["exploiter"].startswith("PowerShell"):
assert data["result"]
assert data["propagation_result"]
else:
assert not data["result"]
assert not data["propagation_result"]
elif ip.endswith(".3"):
if data["exploiter"].startswith("PowerShell"):
assert not data["result"]
assert not data["propagation_result"]
else:
assert data["result"]
assert data["propagation_result"]


def test_scan_target_generation(telemetry_messenger_spy, mock_ip_scanner, mock_victim_host_factory):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@

@pytest.fixture
def exploit_telem_test_instance():
return ExploitTelem(EXPLOITER_NAME, HOST, RESULT, EXPLOITER_INFO, EXPLOITER_ATTEMPTS)
return ExploitTelem(EXPLOITER_NAME, HOST, RESULT, RESULT, EXPLOITER_INFO, EXPLOITER_ATTEMPTS)


def test_exploit_telem_send(exploit_telem_test_instance, spy_send_telemetry):
exploit_telem_test_instance.send()
expected_data = {
"result": RESULT,
"exploitation_result": RESULT,
"propagation_result": RESULT,
"machine": HOST_AS_DICT,
"exploiter": EXPLOITER_NAME,
"info": EXPLOITER_INFO,
Expand Down