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
from collections import namedtuple
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):
@ -36,12 +36,14 @@ class IPuppet(metaclass=abc.ABCMeta):
"""
@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
:param str name: The name of the system info collector to run
:return: A dictionary containing the information collected from the system
:rtype: Dict
Runs a credential collector
:param str name: The name of the credential collector to run
:param Dict options: A dictionary containing options that modify the behavior of the
Credential collector
:return: A sequence of Credentials that have been collected from the system
:rtype: Sequence[Credentials]
"""
@abc.abstractmethod

View File

@ -2,8 +2,8 @@ from enum import Enum
class PluginType(Enum):
CREDENTIAL_COLLECTOR = "CredentialCollector"
EXPLOITER = "Exploiter"
FINGERPRINTER = "Fingerprinter"
PAYLOAD = "Payload"
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.model import VictimHostFactory
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.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.timer import Timer
@ -134,12 +134,12 @@ class AutomatedMaster(IMaster):
logger.error(f"An error occurred while fetching configuration: {e}")
return
system_info_collector_thread = create_daemon_thread(
credential_collector_thread = create_daemon_thread(
target=self._run_plugins,
args=(
config["system_info_collector_classes"],
"system info collector",
self._collect_system_info,
"credential collector",
self._collect_credentials,
),
)
pba_thread = create_daemon_thread(
@ -147,14 +147,14 @@ class AutomatedMaster(IMaster):
args=(config["post_breach_actions"].items(), "post-breach action", self._run_pba),
)
system_info_collector_thread.start()
credential_collector_thread.start()
pba_thread.start()
# 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
# the PBA thread later in this function to prevent the simulation from ending while PBAs are
# still running.
system_info_collector_thread.join()
credential_collector_thread.join()
if self._can_propagate():
self._propagator.propagate(config["propagation"], self._stop)
@ -168,12 +168,13 @@ class AutomatedMaster(IMaster):
pba_thread.join()
def _collect_system_info(self, collector: str):
system_info_telemetry = {}
system_info_telemetry[collector] = self._puppet.run_sys_info_collector(collector)
self._telemetry_messenger.send_telemetry(
SystemInfoTelem({"collectors": system_info_telemetry})
)
def _collect_credentials(self, collector: str):
credentials = self._puppet.run_credential_collector(collector, {})
if credentials:
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]):
name = pba[0]

View File

@ -3,11 +3,11 @@ import logging
from infection_monkey.i_master import IMaster
from infection_monkey.i_puppet import IPuppet, PortStatus
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.messengers.i_telemetry_messenger import ITelemetryMessenger
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
from infection_monkey.telemetry.scan_telem import ScanTelem
from infection_monkey.telemetry.system_info_telem import SystemInfoTelem
logger = logging.getLogger()
@ -31,18 +31,18 @@ class MockMaster(IMaster):
self._exploit()
self._run_payload()
def _run_sys_info_collectors(self):
logger.info("Running system info collectors")
system_info_telemetry = {}
system_info_telemetry["ProcessListCollector"] = self._puppet.run_sys_info_collector(
"ProcessListCollector"
)
self._telemetry_messenger.send_telemetry(
SystemInfoTelem({"collectors": system_info_telemetry})
)
system_info = self._puppet.run_sys_info_collector("LinuxInfoCollector")
self._telemetry_messenger.send_telemetry(SystemInfoTelem(system_info))
logger.info("Finished running system info collectors")
def _run_credential_collectors(self):
logger.info("Running credential collectors")
windows_credentials = self._puppet.run_credential_collector("MimikatzCredentialCollector")
if windows_credentials:
self._telemetry_messenger.send_telemetry(CredentialsTelem(windows_credentials))
ssh_credentials = self._puppet.run_sys_info_collector("SSHCredentialCollector")
if ssh_credentials:
self._telemetry_messenger.send_telemetry(CredentialsTelem(ssh_credentials))
logger.info("Finished running credential collectors")
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 infection_monkey.config import GUID, WormConfiguration
from infection_monkey.control import ControlClient
from infection_monkey.credential_collectors import MimikatzCredentialCollector
from infection_monkey.i_puppet import IPuppet, PluginType
from infection_monkey.master import AutomatedMaster
from infection_monkey.master.control_channel import ControlChannel
@ -193,6 +194,12 @@ class InfectionMonkey:
def _build_puppet() -> IPuppet:
puppet = Puppet()
puppet.load_plugin(
"MimikatzCollector",
MimikatzCredentialCollector(),
PluginType.CREDENTIAL_COLLECTOR,
)
puppet.load_plugin("elastic", ElasticSearchFingerprinter(), PluginType.FINGERPRINTER)
puppet.load_plugin("http", HTTPFingerprinter(), PluginType.FINGERPRINTER)
puppet.load_plugin("mssql", MSSQLFingerprinter(), PluginType.FINGERPRINTER)

View File

@ -1,8 +1,10 @@
import logging
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 (
Credentials,
ExploiterResultData,
FingerprintData,
IPuppet,
@ -25,133 +27,26 @@ class MockPuppet(IPuppet):
def load_plugin(self, plugin: object, plugin_type: PluginType) -> None:
logger.debug(f"load_plugin({plugin}, {plugin_type})")
def run_sys_info_collector(self, name: str) -> Dict:
logger.debug(f"run_sys_info_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,
},
}
}
def run_credential_collector(self, name: str, options: Dict) -> Sequence[Credentials]:
logger.debug(f"run_credential_collector({name})")
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:
logger.debug(f"run_pba({name}, {options})")

View File

@ -1,9 +1,10 @@
import logging
import threading
from typing import Dict, List
from typing import Dict, List, Sequence
from infection_monkey import network
from infection_monkey.i_puppet import (
Credentials,
ExploiterResultData,
FingerprintData,
IPuppet,
@ -27,8 +28,8 @@ class Puppet(IPuppet):
def load_plugin(self, plugin_name: str, plugin: object, plugin_type: PluginType) -> None:
self._plugin_registry.load_plugin(plugin_name, plugin, plugin_type)
def run_sys_info_collector(self, name: str) -> Dict:
return self._mock_puppet.run_sys_info_collector(name)
def run_credential_collector(self, name: str, options: Dict) -> Sequence[Credentials]:
return list(self._mock_puppet.run_credential_collector(name, options))
def run_pba(self, name: str, options: Dict) -> PostBreachData:
return self._mock_puppet.run_pba(name, options)