forked from p15670423/monkey
Merge branch '1606-run-credential-collectors' into agent-refactor
PR #1719
This commit is contained in:
commit
095572f919
|
@ -10,6 +10,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- credentials.json file for storing Monkey Island user login information. #1206
|
- credentials.json file for storing Monkey Island user login information. #1206
|
||||||
- "GET /api/propagation-credentials/<string:guid>" endpoint for agents to
|
- "GET /api/propagation-credentials/<string:guid>" endpoint for agents to
|
||||||
retrieve updated credentials from the Island. #1538
|
retrieve updated credentials from the Island. #1538
|
||||||
|
- SSHCollector as a configurable System info Collector. #1606
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- "Communicate as Backdoor User" PBA's HTTP requests to request headers only and
|
- "Communicate as Backdoor User" PBA's HTTP requests to request headers only and
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
MIMIKATZ_COLLECTOR = "MimikatzCollector"
|
MIMIKATZ_COLLECTOR = "MimikatzCollector"
|
||||||
|
SSH_COLLECTOR = "SSHCollector"
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
class TelemCategoryEnum:
|
class TelemCategoryEnum:
|
||||||
|
ATTACK = "attack"
|
||||||
|
AWS_INFO = "aws_info"
|
||||||
|
CREDENTIALS = "credentials"
|
||||||
EXPLOIT = "exploit"
|
EXPLOIT = "exploit"
|
||||||
|
FILE_ENCRYPTION = "file_encryption"
|
||||||
POST_BREACH = "post_breach"
|
POST_BREACH = "post_breach"
|
||||||
SCAN = "scan"
|
SCAN = "scan"
|
||||||
STATE = "state"
|
STATE = "state"
|
||||||
SYSTEM_INFO = "system_info"
|
SYSTEM_INFO = "system_info"
|
||||||
TRACE = "trace"
|
TRACE = "trace"
|
||||||
TUNNEL = "tunnel"
|
TUNNEL = "tunnel"
|
||||||
ATTACK = "attack"
|
|
||||||
FILE_ENCRYPTION = "file_encryption"
|
|
||||||
AWS_INFO = "aws_info"
|
|
||||||
|
|
|
@ -4,3 +4,4 @@ from .credential_components.password import Password
|
||||||
from .credential_components.username import Username
|
from .credential_components.username import Username
|
||||||
from .credential_components.ssh_keypair import SSHKeypair
|
from .credential_components.ssh_keypair import SSHKeypair
|
||||||
from .mimikatz_collector import MimikatzCredentialCollector
|
from .mimikatz_collector import MimikatzCredentialCollector
|
||||||
|
from .ssh_collector import SSHCredentialCollector
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Iterable
|
from typing import Sequence
|
||||||
|
|
||||||
from infection_monkey.credential_collectors import LMHash, NTHash, Password, Username
|
from infection_monkey.credential_collectors import LMHash, NTHash, Password, Username
|
||||||
from infection_monkey.i_puppet.credential_collection import Credentials, ICredentialCollector
|
from infection_monkey.i_puppet.credential_collection import Credentials, ICredentialCollector
|
||||||
|
@ -8,12 +8,12 @@ from .windows_credentials import WindowsCredentials
|
||||||
|
|
||||||
|
|
||||||
class MimikatzCredentialCollector(ICredentialCollector):
|
class MimikatzCredentialCollector(ICredentialCollector):
|
||||||
def collect_credentials(self, options=None) -> Iterable[Credentials]:
|
def collect_credentials(self, options=None) -> Sequence[Credentials]:
|
||||||
creds = pypykatz_handler.get_windows_creds()
|
creds = pypykatz_handler.get_windows_creds()
|
||||||
return MimikatzCredentialCollector._to_credentials(creds)
|
return MimikatzCredentialCollector._to_credentials(creds)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _to_credentials(win_creds: Iterable[WindowsCredentials]) -> [Credentials]:
|
def _to_credentials(win_creds: Sequence[WindowsCredentials]) -> [Credentials]:
|
||||||
all_creds = []
|
all_creds = []
|
||||||
for win_cred in win_creds:
|
for win_cred in win_creds:
|
||||||
identities = []
|
identities = []
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import binascii
|
import binascii
|
||||||
|
import logging
|
||||||
from typing import Any, Dict, List, NewType
|
from typing import Any, Dict, List, NewType
|
||||||
|
|
||||||
from pypykatz.pypykatz import pypykatz
|
from pypykatz.pypykatz import pypykatz
|
||||||
|
|
||||||
|
from infection_monkey.utils.environment import is_windows_os
|
||||||
|
|
||||||
from .windows_credentials import WindowsCredentials
|
from .windows_credentials import WindowsCredentials
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
CREDENTIAL_TYPES = [
|
CREDENTIAL_TYPES = [
|
||||||
"msv_creds",
|
"msv_creds",
|
||||||
"wdigest_creds",
|
"wdigest_creds",
|
||||||
|
@ -19,6 +24,11 @@ PypykatzCredential = NewType("PypykatzCredential", Dict)
|
||||||
|
|
||||||
|
|
||||||
def get_windows_creds() -> List[WindowsCredentials]:
|
def get_windows_creds() -> List[WindowsCredentials]:
|
||||||
|
# TODO: Remove this check when this is turned into a plugin.
|
||||||
|
if not is_windows_os():
|
||||||
|
logger.debug("Skipping pypykatz because the operating system is not Windows")
|
||||||
|
return []
|
||||||
|
|
||||||
pypy_handle = pypykatz.go_live()
|
pypy_handle = pypykatz.go_live()
|
||||||
logon_data = pypy_handle.to_dict()
|
logon_data = pypy_handle.to_dict()
|
||||||
windows_creds = _parse_pypykatz_results(logon_data)
|
windows_creds = _parse_pypykatz_results(logon_data)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, Iterable, List
|
from typing import Dict, Iterable, Sequence
|
||||||
|
|
||||||
from infection_monkey.credential_collectors import SSHKeypair, Username
|
from infection_monkey.credential_collectors import SSHKeypair, Username
|
||||||
from infection_monkey.credential_collectors.ssh_collector import ssh_handler
|
from infection_monkey.credential_collectors.ssh_collector import ssh_handler
|
||||||
|
@ -17,7 +17,7 @@ class SSHCredentialCollector(ICredentialCollector):
|
||||||
def __init__(self, telemetry_messenger: ITelemetryMessenger):
|
def __init__(self, telemetry_messenger: ITelemetryMessenger):
|
||||||
self._telemetry_messenger = telemetry_messenger
|
self._telemetry_messenger = telemetry_messenger
|
||||||
|
|
||||||
def collect_credentials(self, _options=None) -> List[Credentials]:
|
def collect_credentials(self, _options=None) -> Sequence[Credentials]:
|
||||||
logger.info("Started scanning for SSH credentials")
|
logger.info("Started scanning for SSH credentials")
|
||||||
ssh_info = ssh_handler.get_ssh_info(self._telemetry_messenger)
|
ssh_info = ssh_handler.get_ssh_info(self._telemetry_messenger)
|
||||||
logger.info("Finished scanning for SSH credentials")
|
logger.info("Finished scanning for SSH credentials")
|
||||||
|
@ -25,7 +25,7 @@ class SSHCredentialCollector(ICredentialCollector):
|
||||||
return SSHCredentialCollector._to_credentials(ssh_info)
|
return SSHCredentialCollector._to_credentials(ssh_info)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _to_credentials(ssh_info: Iterable[Dict]) -> List[Credentials]:
|
def _to_credentials(ssh_info: Iterable[Dict]) -> Sequence[Credentials]:
|
||||||
ssh_credentials = []
|
ssh_credentials = []
|
||||||
|
|
||||||
for info in ssh_info:
|
for info in ssh_info:
|
||||||
|
|
|
@ -8,6 +8,7 @@ from common.utils.attack_utils import ScanStatus
|
||||||
from infection_monkey.telemetry.attack.t1005_telem import T1005Telem
|
from infection_monkey.telemetry.attack.t1005_telem import T1005Telem
|
||||||
from infection_monkey.telemetry.attack.t1145_telem import T1145Telem
|
from infection_monkey.telemetry.attack.t1145_telem import T1145Telem
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
|
from infection_monkey.utils.environment import is_windows_os
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -15,6 +16,13 @@ DEFAULT_DIRS = ["/.ssh/", "/"]
|
||||||
|
|
||||||
|
|
||||||
def get_ssh_info(telemetry_messenger: ITelemetryMessenger) -> Iterable[Dict]:
|
def get_ssh_info(telemetry_messenger: ITelemetryMessenger) -> Iterable[Dict]:
|
||||||
|
# TODO: Remove this check when this is turned into a plugin.
|
||||||
|
if is_windows_os():
|
||||||
|
logger.debug(
|
||||||
|
"Skipping SSH credentials collection because the operating system is not Linux"
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
|
||||||
home_dirs = _get_home_dirs()
|
home_dirs = _get_home_dirs()
|
||||||
ssh_info = _get_ssh_files(home_dirs, telemetry_messenger)
|
ssh_info = _get_ssh_files(home_dirs, telemetry_messenger)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
from .plugin_type import PluginType
|
from .plugin_type import PluginType
|
||||||
|
from .credential_collection import (
|
||||||
|
Credentials,
|
||||||
|
CredentialType,
|
||||||
|
ICredentialCollector,
|
||||||
|
ICredentialComponent,
|
||||||
|
)
|
||||||
from .i_puppet import (
|
from .i_puppet import (
|
||||||
IPuppet,
|
IPuppet,
|
||||||
ExploiterResultData,
|
ExploiterResultData,
|
||||||
|
@ -10,9 +16,3 @@ from .i_puppet import (
|
||||||
UnknownPluginError,
|
UnknownPluginError,
|
||||||
)
|
)
|
||||||
from .i_fingerprinter import IFingerprinter
|
from .i_fingerprinter import IFingerprinter
|
||||||
from .credential_collection import (
|
|
||||||
Credentials,
|
|
||||||
CredentialType,
|
|
||||||
ICredentialCollector,
|
|
||||||
ICredentialComponent,
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Iterable, Mapping, Optional
|
from typing import Mapping, Optional, Sequence
|
||||||
|
|
||||||
from .credentials import Credentials
|
from .credentials import Credentials
|
||||||
|
|
||||||
|
|
||||||
class ICredentialCollector(ABC):
|
class ICredentialCollector(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def collect_credentials(self, options: Optional[Mapping]) -> Iterable[Credentials]:
|
def collect_credentials(self, options: Optional[Mapping]) -> Sequence[Credentials]:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,9 +2,9 @@ import abc
|
||||||
import threading
|
import threading
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Sequence
|
||||||
|
|
||||||
from . import PluginType
|
from . import Credentials, PluginType
|
||||||
|
|
||||||
|
|
||||||
class PortStatus(Enum):
|
class PortStatus(Enum):
|
||||||
|
@ -36,12 +36,14 @@ class IPuppet(metaclass=abc.ABCMeta):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def run_sys_info_collector(self, name: str) -> Dict:
|
def run_credential_collector(self, name: str, options: Dict) -> Sequence[Credentials]:
|
||||||
"""
|
"""
|
||||||
Runs a system info collector
|
Runs a credential collector
|
||||||
:param str name: The name of the system info collector to run
|
:param str name: The name of the credential collector to run
|
||||||
:return: A dictionary containing the information collected from the system
|
:param Dict options: A dictionary containing options that modify the behavior of the
|
||||||
:rtype: Dict
|
Credential collector
|
||||||
|
:return: A sequence of Credentials that have been collected from the system
|
||||||
|
:rtype: Sequence[Credentials]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
|
|
|
@ -2,8 +2,8 @@ from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class PluginType(Enum):
|
class PluginType(Enum):
|
||||||
|
CREDENTIAL_COLLECTOR = "CredentialCollector"
|
||||||
EXPLOITER = "Exploiter"
|
EXPLOITER = "Exploiter"
|
||||||
FINGERPRINTER = "Fingerprinter"
|
FINGERPRINTER = "Fingerprinter"
|
||||||
PAYLOAD = "Payload"
|
PAYLOAD = "Payload"
|
||||||
POST_BREACH_ACTION = "PBA"
|
POST_BREACH_ACTION = "PBA"
|
||||||
SYSTEM_INFO_COLLECTOR = "SystemInfoCollector"
|
|
||||||
|
|
|
@ -8,9 +8,9 @@ from infection_monkey.i_master import IMaster
|
||||||
from infection_monkey.i_puppet import IPuppet
|
from infection_monkey.i_puppet import IPuppet
|
||||||
from infection_monkey.model import VictimHostFactory
|
from infection_monkey.model import VictimHostFactory
|
||||||
from infection_monkey.network import NetworkInterface
|
from infection_monkey.network import NetworkInterface
|
||||||
|
from infection_monkey.telemetry.credentials_telem import CredentialsTelem
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
|
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
|
||||||
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
|
|
||||||
from infection_monkey.utils.threading import create_daemon_thread, interruptable_iter
|
from infection_monkey.utils.threading import create_daemon_thread, interruptable_iter
|
||||||
from infection_monkey.utils.timer import Timer
|
from infection_monkey.utils.timer import Timer
|
||||||
|
|
||||||
|
@ -134,12 +134,12 @@ class AutomatedMaster(IMaster):
|
||||||
logger.error(f"An error occurred while fetching configuration: {e}")
|
logger.error(f"An error occurred while fetching configuration: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
system_info_collector_thread = create_daemon_thread(
|
credential_collector_thread = create_daemon_thread(
|
||||||
target=self._run_plugins,
|
target=self._run_plugins,
|
||||||
args=(
|
args=(
|
||||||
config["system_info_collector_classes"],
|
config["system_info_collector_classes"],
|
||||||
"system info collector",
|
"credential collector",
|
||||||
self._collect_system_info,
|
self._collect_credentials,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
pba_thread = create_daemon_thread(
|
pba_thread = create_daemon_thread(
|
||||||
|
@ -147,14 +147,14 @@ class AutomatedMaster(IMaster):
|
||||||
args=(config["post_breach_actions"].items(), "post-breach action", self._run_pba),
|
args=(config["post_breach_actions"].items(), "post-breach action", self._run_pba),
|
||||||
)
|
)
|
||||||
|
|
||||||
system_info_collector_thread.start()
|
credential_collector_thread.start()
|
||||||
pba_thread.start()
|
pba_thread.start()
|
||||||
|
|
||||||
# Future stages of the simulation require the output of the system info collectors. Nothing
|
# Future stages of the simulation require the output of the system info collectors. Nothing
|
||||||
# requires the output of PBAs, so we don't need to join on that thread here. We will join on
|
# requires the output of PBAs, so we don't need to join on that thread here. We will join on
|
||||||
# the PBA thread later in this function to prevent the simulation from ending while PBAs are
|
# the PBA thread later in this function to prevent the simulation from ending while PBAs are
|
||||||
# still running.
|
# still running.
|
||||||
system_info_collector_thread.join()
|
credential_collector_thread.join()
|
||||||
|
|
||||||
if self._can_propagate():
|
if self._can_propagate():
|
||||||
self._propagator.propagate(config["propagation"], self._stop)
|
self._propagator.propagate(config["propagation"], self._stop)
|
||||||
|
@ -168,12 +168,13 @@ class AutomatedMaster(IMaster):
|
||||||
|
|
||||||
pba_thread.join()
|
pba_thread.join()
|
||||||
|
|
||||||
def _collect_system_info(self, collector: str):
|
def _collect_credentials(self, collector: str):
|
||||||
system_info_telemetry = {}
|
credentials = self._puppet.run_credential_collector(collector, {})
|
||||||
system_info_telemetry[collector] = self._puppet.run_sys_info_collector(collector)
|
|
||||||
self._telemetry_messenger.send_telemetry(
|
if credentials:
|
||||||
SystemInfoTelem({"collectors": system_info_telemetry})
|
self._telemetry_messenger.send_telemetry(CredentialsTelem(credentials))
|
||||||
)
|
else:
|
||||||
|
logger.debug(f"No credentials were collected by {collector}")
|
||||||
|
|
||||||
def _run_pba(self, pba: Tuple[str, Dict]):
|
def _run_pba(self, pba: Tuple[str, Dict]):
|
||||||
# TODO: This is the class's name right now. We need `display_name` (see the
|
# TODO: This is the class's name right now. We need `display_name` (see the
|
||||||
|
|
|
@ -3,11 +3,11 @@ import logging
|
||||||
from infection_monkey.i_master import IMaster
|
from infection_monkey.i_master import IMaster
|
||||||
from infection_monkey.i_puppet import IPuppet, PortStatus
|
from infection_monkey.i_puppet import IPuppet, PortStatus
|
||||||
from infection_monkey.model.host import VictimHost
|
from infection_monkey.model.host import VictimHost
|
||||||
|
from infection_monkey.telemetry.credentials_telem import CredentialsTelem
|
||||||
from infection_monkey.telemetry.exploit_telem import ExploitTelem
|
from infection_monkey.telemetry.exploit_telem import ExploitTelem
|
||||||
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger
|
||||||
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
|
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
|
||||||
from infection_monkey.telemetry.scan_telem import ScanTelem
|
from infection_monkey.telemetry.scan_telem import ScanTelem
|
||||||
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
@ -31,18 +31,18 @@ class MockMaster(IMaster):
|
||||||
self._exploit()
|
self._exploit()
|
||||||
self._run_payload()
|
self._run_payload()
|
||||||
|
|
||||||
def _run_sys_info_collectors(self):
|
def _run_credential_collectors(self):
|
||||||
logger.info("Running system info collectors")
|
logger.info("Running credential collectors")
|
||||||
system_info_telemetry = {}
|
|
||||||
system_info_telemetry["ProcessListCollector"] = self._puppet.run_sys_info_collector(
|
windows_credentials = self._puppet.run_credential_collector("MimikatzCollector")
|
||||||
"ProcessListCollector"
|
if windows_credentials:
|
||||||
)
|
self._telemetry_messenger.send_telemetry(CredentialsTelem(windows_credentials))
|
||||||
self._telemetry_messenger.send_telemetry(
|
|
||||||
SystemInfoTelem({"collectors": system_info_telemetry})
|
ssh_credentials = self._puppet.run_sys_info_collector("SSHCollector")
|
||||||
)
|
if ssh_credentials:
|
||||||
system_info = self._puppet.run_sys_info_collector("LinuxInfoCollector")
|
self._telemetry_messenger.send_telemetry(CredentialsTelem(ssh_credentials))
|
||||||
self._telemetry_messenger.send_telemetry(SystemInfoTelem(system_info))
|
|
||||||
logger.info("Finished running system info collectors")
|
logger.info("Finished running credential collectors")
|
||||||
|
|
||||||
def _run_pbas(self):
|
def _run_pbas(self):
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,10 @@ from common.utils.attack_utils import ScanStatus, UsageEnum
|
||||||
from common.version import get_version
|
from common.version import get_version
|
||||||
from infection_monkey.config import GUID, WormConfiguration
|
from infection_monkey.config import GUID, WormConfiguration
|
||||||
from infection_monkey.control import ControlClient
|
from infection_monkey.control import ControlClient
|
||||||
|
from infection_monkey.credential_collectors import (
|
||||||
|
MimikatzCredentialCollector,
|
||||||
|
SSHCredentialCollector,
|
||||||
|
)
|
||||||
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
|
||||||
from infection_monkey.master.control_channel import ControlChannel
|
from infection_monkey.master.control_channel import ControlChannel
|
||||||
|
@ -169,7 +173,7 @@ class InfectionMonkey:
|
||||||
|
|
||||||
def _build_master(self):
|
def _build_master(self):
|
||||||
local_network_interfaces = InfectionMonkey._get_local_network_interfaces()
|
local_network_interfaces = InfectionMonkey._get_local_network_interfaces()
|
||||||
puppet = InfectionMonkey._build_puppet()
|
puppet = self._build_puppet()
|
||||||
|
|
||||||
victim_host_factory = self._build_victim_host_factory(local_network_interfaces)
|
victim_host_factory = self._build_victim_host_factory(local_network_interfaces)
|
||||||
|
|
||||||
|
@ -189,10 +193,20 @@ class InfectionMonkey:
|
||||||
|
|
||||||
return local_network_interfaces
|
return local_network_interfaces
|
||||||
|
|
||||||
@staticmethod
|
def _build_puppet(self) -> IPuppet:
|
||||||
def _build_puppet() -> IPuppet:
|
|
||||||
puppet = Puppet()
|
puppet = Puppet()
|
||||||
|
|
||||||
|
puppet.load_plugin(
|
||||||
|
"MimikatzCollector",
|
||||||
|
MimikatzCredentialCollector(),
|
||||||
|
PluginType.CREDENTIAL_COLLECTOR,
|
||||||
|
)
|
||||||
|
puppet.load_plugin(
|
||||||
|
"SSHCollector",
|
||||||
|
SSHCredentialCollector(self.telemetry_messenger),
|
||||||
|
PluginType.CREDENTIAL_COLLECTOR,
|
||||||
|
)
|
||||||
|
|
||||||
puppet.load_plugin("elastic", ElasticSearchFingerprinter(), PluginType.FINGERPRINTER)
|
puppet.load_plugin("elastic", ElasticSearchFingerprinter(), PluginType.FINGERPRINTER)
|
||||||
puppet.load_plugin("http", HTTPFingerprinter(), PluginType.FINGERPRINTER)
|
puppet.load_plugin("http", HTTPFingerprinter(), PluginType.FINGERPRINTER)
|
||||||
puppet.load_plugin("mssql", MSSQLFingerprinter(), PluginType.FINGERPRINTER)
|
puppet.load_plugin("mssql", MSSQLFingerprinter(), PluginType.FINGERPRINTER)
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Sequence
|
||||||
|
|
||||||
|
from infection_monkey.credential_collectors import LMHash, Password, SSHKeypair, Username
|
||||||
from infection_monkey.i_puppet import (
|
from infection_monkey.i_puppet import (
|
||||||
|
Credentials,
|
||||||
ExploiterResultData,
|
ExploiterResultData,
|
||||||
FingerprintData,
|
FingerprintData,
|
||||||
IPuppet,
|
IPuppet,
|
||||||
|
@ -12,7 +14,6 @@ from infection_monkey.i_puppet import (
|
||||||
PortStatus,
|
PortStatus,
|
||||||
PostBreachData,
|
PostBreachData,
|
||||||
)
|
)
|
||||||
from infection_monkey.post_breach.actions.collect_processes_list import ProcessListCollection
|
|
||||||
|
|
||||||
DOT_1 = "10.0.0.1"
|
DOT_1 = "10.0.0.1"
|
||||||
DOT_2 = "10.0.0.2"
|
DOT_2 = "10.0.0.2"
|
||||||
|
@ -26,133 +27,26 @@ class MockPuppet(IPuppet):
|
||||||
def load_plugin(self, plugin: object, plugin_type: PluginType) -> None:
|
def load_plugin(self, plugin: object, plugin_type: PluginType) -> None:
|
||||||
logger.debug(f"load_plugin({plugin}, {plugin_type})")
|
logger.debug(f"load_plugin({plugin}, {plugin_type})")
|
||||||
|
|
||||||
def run_sys_info_collector(self, name: str) -> Dict:
|
def run_credential_collector(self, name: str, options: Dict) -> Sequence[Credentials]:
|
||||||
logger.debug(f"run_sys_info_collector({name})")
|
logger.debug(f"run_credential_collector({name})")
|
||||||
# TODO: More collectors
|
|
||||||
if name == "LinuxInfoCollector":
|
|
||||||
return {
|
|
||||||
"credentials": {},
|
|
||||||
"network_info": {
|
|
||||||
"networks": [
|
|
||||||
{"addr": "10.0.0.7", "netmask": "255.255.255.0"},
|
|
||||||
{"addr": "10.45.31.103", "netmask": "255.255.255.0"},
|
|
||||||
{"addr": "192.168.33.241", "netmask": "255.255.0.0"},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ssh_info": [
|
|
||||||
{
|
|
||||||
"name": "m0nk3y",
|
|
||||||
"home_dir": "/home/m0nk3y",
|
|
||||||
"public_key": "ssh-rsa "
|
|
||||||
"AAAAB3NzaC1yc2EAAAADAQABAAABAQCqhqTJfcrAbTUPzQ+Ou9bhQjmP29jRBz00BAdvNu77Y1SwM/+wETxapv7QPG55oc04Y5qR1KaItcwz3Prh7Qe/ohP/I2mIhP5tDRNfYHxXaGtj58wQhFrkrUhERVvEvwyvb97RWPAtAJjWT8+S6ASjjvyUNHulFIjJ0Yptlj2fboeh1eETDQ4FKfofpgwmab110ct2500FOtY1MWqFgpRvV0EX8WgJoscQ5FnsJAn6Ueb3DnsrIDq1LtK1rmxGSiZwpgOCwvyC1FFfHeP+cfpPsS+G9pBSYm2VqR42QL1BJL1pm4wFPVrBDmzORVQRf35k6agL7loRlfmAt28epDi1 ubuntu@test\n", # noqa: E501
|
|
||||||
"private_key": "-----BEGIN RSA PRIVATE KEY-----\n"
|
|
||||||
"MIIEpAIBAAKCAQEAqoakyX3KwG01D80PjrvW4UI5j9vY0Qc9NAQHbzbu+2NUsDP/\n"
|
|
||||||
"sBE8Wqb+0DxueaHNOGOakdSmiLXMM9z64e0Hv6IT/yNpiIT+bQ0TX2B8V2hrY+fM\n"
|
|
||||||
"Ew0OBSn6H6YMJmm9ddHLdudNBTrWNTFqhYKUb1dBF/FoCaLHEORZ7CQJ+lHm9w57\n"
|
|
||||||
"KyA6tS7Sta5sRkomcKYDgsL8gtRRXx3j/nH6T7EvhvaQUmJtlakeNkC9QSS9aZuM\n"
|
|
||||||
"snegLvVSlHVmKe8SjD0YAF7g9HH/vm0R2jYTYSArslw4mUZMjTcAQ/XBeDHDkNZq\n"
|
|
||||||
"x9ECzXdeZhXCXlKcadC+kNp+yT4MwkHAjid6AyalSDJ+9k3QRaI6ItxofWJhnZdB\n"
|
|
||||||
"RxQtnkJNOZCMKqwxmxUweX7AyShT1KdBdkw0VzkY0O3VUgdR9IzQu73eME5Qr4LM\n"
|
|
||||||
"5x+rFy0EggHkzCXecviDDQ/SJZEDR4yE0SCxwY0GxVfDdvM6aoLK7wLfu0hG+hjO\n"
|
|
||||||
"ewXmOAECgYEA4yA14atxKYWf8tAJnmH+IJi1nuiyBoaKJh9nGulGTFVpugytkfdy\n"
|
|
||||||
"omGYsvlSJd6x4KPM2nXuSD9uvS0ZDeHDXbPJcFAPscghwwIekunQigECgYEAwDRl\n"
|
|
||||||
"QOhBx8PpicbRmoEe06zb+gRNTYTnvcHgkJN275pqTn1hIAdQSGnyuyWdCN6CU8cg\n"
|
|
||||||
"p7ecLbCujAstim4H8LG6xMv8jBgVeBKclKEEy9IpvMZ/DGOdUS4/RMWkdVbcFFHZ\n"
|
|
||||||
"57gycmFwgN7ZFXdMkuCCZi2KCa4jX54G1VNX0+k64cLV8lgQXvVyl9QdvBkt8NqB\n"
|
|
||||||
"Zoce2vfDrFkUHoxQmAl2jvn8925KkAdga4Zj+zvLgmcryxCFZnA6IvxaoHzrUSxO\n"
|
|
||||||
"HpuEdCFek/4gyhXPbYQO99ZtOjx0mXwZVqRaEA1kvhX3+PjoPRO2wgBLXVNyb+P5\n"
|
|
||||||
"5Bxfk6XI40UAUSYv6XQlfIQj0xz/YfSkWbOwTJOShgMbJtiZVFuZ2YcEjSYXzNtv\n"
|
|
||||||
"WBM0+05OGqjxdyI+qpjHqrZVWN9WvvkH0gJz+zvcorygINMnuSjpNCw4nipXHaud\n"
|
|
||||||
"LbiqWK42eTmVSiFH+pH+YwVaTatc0RfQ7OP218GD8dtkTgw2JFOzbA==\n"
|
|
||||||
"-----END RSA PRIVATE KEY-----\n",
|
|
||||||
"known_hosts": "|1|pERVcy3opIGJnp7HVTpeA0FmuEY=|L64j7430lwkSFrmcn49Nf8YEsLc= " # noqa: E501
|
|
||||||
"ssh-rsa "
|
|
||||||
"AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n" # noqa: E501
|
|
||||||
"|1|DXEyHSAtnxSSWb4z6XLaxHJL/aM=|zjIBopXOz1GB9hbdpVcYsHY+eSU= "
|
|
||||||
"ssh-rsa "
|
|
||||||
"AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n" # noqa: E501
|
|
||||||
"10.197.94.221 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL3o1lUn7mZ6HNKDlkFJH9lvFIOXpTH62XkxM7wKXeZbKUy1BKnx2Jkkpv6736XnbFNkUHSnPlCAYDBqsH4nr28=\n" # noqa: E501
|
|
||||||
"|1|kVjsp1IWhGMsWfrbQuhLUABrNMk=|xKCh+yr8mPEyCLZ2/E5bC8bjvw0= "
|
|
||||||
"ecdsa-sha2-nistp256 "
|
|
||||||
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL3o1lUn7mZ6HNKDlkFJH9lvFIOXpTH62XkxM7wKXeZbKUy1BKnx2Jkkpv6736XnbFNkUHSnPlCAYDBqsH4nr28=\n" # noqa: E501
|
|
||||||
"other_host,fd42:5289:fddc:ffdf:216:3eff:fe5b:9114 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL3o1lUn7mZ6HNKDlkFJH9lvFIOXpTH62XkxM7wKXeZbKUy1BKnx2Jkkpv6736XnbFNkUHSnPlCAYDBqsH4nr28=\n" # noqa: E501
|
|
||||||
"|1|S6K6SneX+l7xTM1gNLvDAAzj4gs=|cSOIX6qf5YuIe2aw/KmUrM2ye/c= "
|
|
||||||
"ecdsa-sha2-nistp256 "
|
|
||||||
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL3o1lUn7mZ6HNKDlkFJH9lvFIOXpTH62XkxM7wKXeZbKUy1BKnx2Jkkpv6736XnbFNkUHSnPlCAYDBqsH4nr28=\n", # noqa: E501
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
if name == "ProcessListCollector":
|
|
||||||
return {
|
|
||||||
"process_list": {
|
|
||||||
1: {
|
|
||||||
"cmdline": "/sbin/init",
|
|
||||||
"full_image_path": "/sbin/init",
|
|
||||||
"name": "systemd",
|
|
||||||
"pid": 1,
|
|
||||||
"ppid": 0,
|
|
||||||
},
|
|
||||||
65: {
|
|
||||||
"cmdline": "/lib/systemd/systemd-journald",
|
|
||||||
"full_image_path": "/lib/systemd/systemd-journald",
|
|
||||||
"name": "systemd-journald",
|
|
||||||
"pid": 65,
|
|
||||||
"ppid": 1,
|
|
||||||
},
|
|
||||||
84: {
|
|
||||||
"cmdline": "/lib/systemd/systemd-udevd",
|
|
||||||
"full_image_path": "/lib/systemd/systemd-udevd",
|
|
||||||
"name": "systemd-udevd",
|
|
||||||
"pid": 84,
|
|
||||||
"ppid": 1,
|
|
||||||
},
|
|
||||||
192: {
|
|
||||||
"cmdline": "/lib/systemd/systemd-networkd",
|
|
||||||
"full_image_path": "/lib/systemd/systemd-networkd",
|
|
||||||
"name": "systemd-networkd",
|
|
||||||
"pid": 192,
|
|
||||||
"ppid": 1,
|
|
||||||
},
|
|
||||||
17749: {
|
|
||||||
"cmdline": "-zsh",
|
|
||||||
"full_image_path": "/bin/zsh",
|
|
||||||
"name": "zsh",
|
|
||||||
"pid": 17749,
|
|
||||||
"ppid": 17748,
|
|
||||||
},
|
|
||||||
18392: {
|
|
||||||
"cmdline": "/home/ubuntu/venvs/monkey/bin/python " "monkey_island.py",
|
|
||||||
"full_image_path": "/usr/bin/python3.7",
|
|
||||||
"name": "python",
|
|
||||||
"pid": 18392,
|
|
||||||
"ppid": 17502,
|
|
||||||
},
|
|
||||||
18400: {
|
|
||||||
"cmdline": "/home/ubuntu/git/monkey/monkey/monkey_island/bin/mongodb/bin/mongod " # noqa: E501
|
|
||||||
"--dbpath /home/ubuntu/.monkey_island/db",
|
|
||||||
"full_image_path": "/home/ubuntu/git/monkey/monkey/monkey_island/bin/mongodb/bin/mongod", # noqa: E501
|
|
||||||
"name": "mongod",
|
|
||||||
"pid": 18400,
|
|
||||||
"ppid": 18392,
|
|
||||||
},
|
|
||||||
26535: {
|
|
||||||
"cmdline": "ACCESS DENIED",
|
|
||||||
"full_image_path": "null",
|
|
||||||
"name": "null",
|
|
||||||
"pid": 26535,
|
|
||||||
"ppid": 26469,
|
|
||||||
},
|
|
||||||
29291: {
|
|
||||||
"cmdline": "python infection_monkey.py m0nk3y -s " "localhost:5000",
|
|
||||||
"full_image_path": "/usr/bin/python3.7",
|
|
||||||
"name": "python",
|
|
||||||
"pid": 29291,
|
|
||||||
"ppid": 17749,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {}
|
if name == "SSHCollector":
|
||||||
|
# TODO: Replace Passwords with SSHKeypair after it is implemented
|
||||||
|
ssh_credentials = Credentials(
|
||||||
|
[Username("m0nk3y")],
|
||||||
|
[
|
||||||
|
SSHKeypair("Public_Key_0", "Private_Key_0"),
|
||||||
|
SSHKeypair("Public_Key_1", "Private_Key_1"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return [ssh_credentials]
|
||||||
|
elif name == "MimikatzCollector":
|
||||||
|
windows_credentials = Credentials(
|
||||||
|
[Username("test_user")], [Password("1234"), LMHash("DEADBEEF")]
|
||||||
|
)
|
||||||
|
return [windows_credentials]
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def run_pba(self, name: str, options: Dict) -> PostBreachData:
|
def run_pba(self, name: str, options: Dict) -> PostBreachData:
|
||||||
logger.debug(f"run_pba({name}, {options})")
|
logger.debug(f"run_pba({name}, {options})")
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from infection_monkey.i_puppet import PluginType, UnknownPluginError
|
from infection_monkey.i_puppet import PluginType, UnknownPluginError
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ class PluginRegistry:
|
||||||
|
|
||||||
logger.debug(f"Plugin '{plugin_name}' loaded")
|
logger.debug(f"Plugin '{plugin_name}' loaded")
|
||||||
|
|
||||||
def get_plugin(self, plugin_name: str, plugin_type: PluginType) -> Optional[object]:
|
def get_plugin(self, plugin_name: str, plugin_type: PluginType) -> object:
|
||||||
try:
|
try:
|
||||||
plugin = self._registry[plugin_type][plugin_name]
|
plugin = self._registry[plugin_type][plugin_name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Sequence
|
||||||
|
|
||||||
from infection_monkey import network
|
from infection_monkey import network
|
||||||
from infection_monkey.i_puppet import (
|
from infection_monkey.i_puppet import (
|
||||||
|
Credentials,
|
||||||
ExploiterResultData,
|
ExploiterResultData,
|
||||||
FingerprintData,
|
FingerprintData,
|
||||||
IPuppet,
|
IPuppet,
|
||||||
|
@ -27,8 +28,11 @@ class Puppet(IPuppet):
|
||||||
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)
|
||||||
|
|
||||||
def run_sys_info_collector(self, name: str) -> Dict:
|
def run_credential_collector(self, name: str, options: Dict) -> Sequence[Credentials]:
|
||||||
return self._mock_puppet.run_sys_info_collector(name)
|
credential_collector = self._plugin_registry.get_plugin(
|
||||||
|
name, PluginType.CREDENTIAL_COLLECTOR
|
||||||
|
)
|
||||||
|
return credential_collector.collect_credentials(options)
|
||||||
|
|
||||||
def run_pba(self, name: str, options: Dict) -> PostBreachData:
|
def run_pba(self, name: str, options: Dict) -> PostBreachData:
|
||||||
return self._mock_puppet.run_pba(name, options)
|
return self._mock_puppet.run_pba(name, options)
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import enum
|
||||||
|
import json
|
||||||
|
from typing import Dict, Iterable
|
||||||
|
|
||||||
|
from common.common_consts.telem_categories import TelemCategoryEnum
|
||||||
|
from infection_monkey.i_puppet.credential_collection import Credentials, ICredentialComponent
|
||||||
|
from infection_monkey.telemetry.base_telem import BaseTelem
|
||||||
|
|
||||||
|
|
||||||
|
class CredentialsTelem(BaseTelem):
|
||||||
|
telem_category = TelemCategoryEnum.CREDENTIALS
|
||||||
|
|
||||||
|
def __init__(self, credentials: Iterable[Credentials]):
|
||||||
|
"""
|
||||||
|
Used to send information about stolen or discovered credentials to the Island.
|
||||||
|
:param credentials: An iterable containing credentials to be sent to the Island.
|
||||||
|
"""
|
||||||
|
self._credentials = credentials
|
||||||
|
|
||||||
|
def get_data(self) -> Dict:
|
||||||
|
# TODO: At a later time we can consider factoring this into a Serializer class or similar.
|
||||||
|
return json.loads(json.dumps(self._credentials, default=_serialize))
|
||||||
|
|
||||||
|
|
||||||
|
def _serialize(obj):
|
||||||
|
if isinstance(obj, enum.Enum):
|
||||||
|
return obj.name
|
||||||
|
|
||||||
|
if isinstance(obj, ICredentialComponent):
|
||||||
|
# This is a workaround for ICredentialComponents that are implemented as dataclasses. If the
|
||||||
|
# credential_type attribute is populated with `field(init=False, ...)`, then credential_type
|
||||||
|
# is not added to the object's __dict__ attribute. The biggest risk of this workaround is
|
||||||
|
# that we might change the name of the credential_type field in ICredentialComponents, but
|
||||||
|
# automated refactoring tools would not detect that this string needs to change. This is
|
||||||
|
# mittigated by the call to getattr() below, which will raise an AttributeException if the
|
||||||
|
# attribute name changes and a unit test will fail under these conditions.
|
||||||
|
credential_type = getattr(obj, "credential_type")
|
||||||
|
return dict(obj.__dict__, **{"credential_type": credential_type})
|
||||||
|
|
||||||
|
return getattr(obj, "__dict__", str(obj))
|
|
@ -1,6 +1,4 @@
|
||||||
from common.common_consts.system_info_collectors_names import (
|
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
|
||||||
MIMIKATZ_COLLECTOR,
|
|
||||||
)
|
|
||||||
|
|
||||||
SYSTEM_INFO_COLLECTOR_CLASSES = {
|
SYSTEM_INFO_COLLECTOR_CLASSES = {
|
||||||
"title": "System Information Collectors",
|
"title": "System Information Collectors",
|
||||||
|
@ -10,10 +8,18 @@ SYSTEM_INFO_COLLECTOR_CLASSES = {
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [MIMIKATZ_COLLECTOR],
|
"enum": [MIMIKATZ_COLLECTOR],
|
||||||
"title": "Mimikatz Collector",
|
"title": "Mimikatz Credentials Collector",
|
||||||
"safe": True,
|
"safe": True,
|
||||||
"info": "Collects credentials from Windows credential manager.",
|
"info": "Collects credentials from Windows credential manager.",
|
||||||
"attack_techniques": ["T1003", "T1005"],
|
"attack_techniques": ["T1003", "T1005"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [SSH_COLLECTOR],
|
||||||
|
"title": "SSH Credentials Collector",
|
||||||
|
"safe": True,
|
||||||
|
"info": "Searches users' home directories and collects SSH keypairs.",
|
||||||
|
"attack_techniques": ["T1005", "T1145"],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
from common.common_consts.system_info_collectors_names import (
|
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
|
||||||
MIMIKATZ_COLLECTOR,
|
|
||||||
)
|
|
||||||
|
|
||||||
MONKEY = {
|
MONKEY = {
|
||||||
"title": "Monkey",
|
"title": "Monkey",
|
||||||
|
@ -86,6 +84,7 @@ MONKEY = {
|
||||||
"items": {"$ref": "#/definitions/system_info_collector_classes"},
|
"items": {"$ref": "#/definitions/system_info_collector_classes"},
|
||||||
"default": [
|
"default": [
|
||||||
MIMIKATZ_COLLECTOR,
|
MIMIKATZ_COLLECTOR,
|
||||||
|
SSH_COLLECTOR,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -104,7 +104,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"system_info_collector_classes": [
|
"system_info_collector_classes": [
|
||||||
"MimikatzCollector"
|
"MimikatzCollector",
|
||||||
|
"SSHCollector"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,8 +146,8 @@
|
||||||
},
|
},
|
||||||
"system_info": {
|
"system_info": {
|
||||||
"system_info_collector_classes": [
|
"system_info_collector_classes": [
|
||||||
"hostnamecollector",
|
"MimikatzCollector",
|
||||||
"mimikatzcollector"
|
"SSHCollector"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import List
|
from typing import Sequence
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ def patch_pypykatz(win_creds: [WindowsCredentials], monkeypatch):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def collect_credentials() -> List[Credentials]:
|
def collect_credentials() -> Sequence[Credentials]:
|
||||||
return list(MimikatzCredentialCollector().collect_credentials())
|
return MimikatzCredentialCollector().collect_credentials()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -2,8 +2,7 @@ from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from infection_monkey.credential_collectors import SSHKeypair, Username
|
from infection_monkey.credential_collectors import SSHCredentialCollector, SSHKeypair, Username
|
||||||
from infection_monkey.credential_collectors.ssh_collector import SSHCredentialCollector
|
|
||||||
from infection_monkey.i_puppet.credential_collection import Credentials
|
from infection_monkey.i_puppet.credential_collection import Credentials
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from infection_monkey.credential_collectors import Password, SSHKeypair, Username
|
||||||
|
from infection_monkey.i_puppet import Credentials
|
||||||
|
from infection_monkey.telemetry.credentials_telem import CredentialsTelem
|
||||||
|
|
||||||
|
|
||||||
|
def test_credential_telem_send(spy_send_telemetry):
|
||||||
|
username = "m0nkey"
|
||||||
|
password = "mmm"
|
||||||
|
public_key = "pub_key"
|
||||||
|
private_key = "priv_key"
|
||||||
|
|
||||||
|
expected_data = [
|
||||||
|
{
|
||||||
|
"identities": [{"username": username, "credential_type": "USERNAME"}],
|
||||||
|
"secrets": [
|
||||||
|
{"password": password, "credential_type": "PASSWORD"},
|
||||||
|
{
|
||||||
|
"private_key": "pub_key",
|
||||||
|
"public_key": "priv_key",
|
||||||
|
"credential_type": "SSH_KEYPAIR",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
credentials = Credentials(
|
||||||
|
[Username(username)], [Password(password), SSHKeypair(public_key, private_key)]
|
||||||
|
)
|
||||||
|
|
||||||
|
telem = CredentialsTelem([credentials])
|
||||||
|
telem.send()
|
||||||
|
|
||||||
|
expected_data = json.dumps(expected_data, cls=telem.json_encoder)
|
||||||
|
assert spy_send_telemetry.data == expected_data
|
||||||
|
assert spy_send_telemetry.telem_category == "credentials"
|
Loading…
Reference in New Issue