Agent: Replace SysInfo w/ Credential collectors in IMaster and IPuppet

This commit is contained in:
Mike Salvatore 2022-02-16 14:14:45 -05:00
parent 5b53984014
commit 419aa6fd84
7 changed files with 69 additions and 163 deletions

View File

@ -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

View File

@ -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"

View File

@ -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]):
name = pba[0] name = pba[0]

View File

@ -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("MimikatzCredentialCollector")
"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("SSHCredentialCollector")
) 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):

View File

@ -12,6 +12,7 @@ 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
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
@ -193,6 +194,12 @@ class InfectionMonkey:
def _build_puppet() -> IPuppet: def _build_puppet() -> IPuppet:
puppet = Puppet() puppet = Puppet()
puppet.load_plugin(
"MimikatzCollector",
MimikatzCredentialCollector(),
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)

View File

@ -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,
@ -25,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 == "SSHCredentialCollector":
# 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})")

View File

@ -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,8 @@ 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) return list(self._mock_puppet.run_credential_collector(name, 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)