forked from p15670423/monkey
Merge pull request #1626 from guardicore/1592-i-puppet
Create IPuppet interface
This commit is contained in:
commit
4da7c1ab5f
|
@ -0,0 +1,89 @@
|
||||||
|
import abc
|
||||||
|
import threading
|
||||||
|
from collections import namedtuple
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Dict, Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
class PortStatus(Enum):
|
||||||
|
OPEN = 1
|
||||||
|
CLOSED = 2
|
||||||
|
|
||||||
|
|
||||||
|
PortScanData = namedtuple("PortScanData", ["port", "status", "banner", "service"])
|
||||||
|
|
||||||
|
|
||||||
|
class IPuppet(metaclass=abc.ABCMeta):
|
||||||
|
@abc.abstractmethod
|
||||||
|
def run_sys_info_collector(self, name: str) -> Dict:
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def run_pba(self, name: str, options: Dict) -> None:
|
||||||
|
"""
|
||||||
|
Runs a post-breach action (PBA)
|
||||||
|
:param str name: The name of the post-breach action to run
|
||||||
|
:param Dict options: A dictionary containing options that modify the behavior of the PBA
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def ping(self, host: str) -> Tuple[bool, Optional[str]]:
|
||||||
|
"""
|
||||||
|
Sends a ping (ICMP packet) to a remote host
|
||||||
|
:param str host: The domain name or IP address of a host
|
||||||
|
:return: A tuple that contains whether or not the host responded and the host's inferred
|
||||||
|
operating system
|
||||||
|
:rtype: Tuple[bool, Optional[str]]
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def scan_tcp_port(self, host: str, port: int, timeout: int) -> PortScanData:
|
||||||
|
"""
|
||||||
|
Scans a TCP port on a remote host
|
||||||
|
:param str host: The domain name or IP address of a host
|
||||||
|
:param int port: A TCP port number to scan
|
||||||
|
:param int timeout: The maximum amount of time (in seconds) to wait for a response
|
||||||
|
:return: The data collected by scanning the provided host:port combination
|
||||||
|
:rtype: PortScanData
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def fingerprint(self, name: str, host: str) -> Dict:
|
||||||
|
"""
|
||||||
|
Runs a fingerprinter against a remote host
|
||||||
|
:param str name: The name of the fingerprinter to run
|
||||||
|
:param str host: The domain name or IP address of a host
|
||||||
|
:return: A dictionary containing the information collected by the fingerprinter
|
||||||
|
:rtype: Dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def exploit_host(self, name: str, host: str, options: Dict, interrupt: threading.Event) -> bool:
|
||||||
|
"""
|
||||||
|
Runs an exploiter against a remote host
|
||||||
|
:param str name: The name of the exploiter to run
|
||||||
|
:param str host: The domain name or IP address of a host
|
||||||
|
:param Dict options: A dictionary containing options that modify the behavior of the
|
||||||
|
exploiter
|
||||||
|
:return: True if exploitation was successful, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def run_payload(self, name: str, options: Dict, interrupt: threading.Event) -> None:
|
||||||
|
"""
|
||||||
|
Runs a payload
|
||||||
|
:param str name: The name of the payload to run
|
||||||
|
:param Dict options: A dictionary containing options that modify the behavior of the payload
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def cleanup(self) -> None:
|
||||||
|
"""
|
||||||
|
Revert any changes made to the system by the puppet.
|
||||||
|
"""
|
|
@ -0,0 +1,225 @@
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
from typing import Dict, Optional, Tuple
|
||||||
|
|
||||||
|
from infection_monkey.i_puppet import IPuppet, PortScanData, PortStatus
|
||||||
|
|
||||||
|
DOT_1 = "10.0.0.1"
|
||||||
|
DOT_2 = "10.0.0.2"
|
||||||
|
DOT_3 = "10.0.0.3"
|
||||||
|
DOT_4 = "10.0.0.4"
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class MockPuppet(IPuppet):
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def run_pba(self, name: str, options: Dict) -> None:
|
||||||
|
logger.debug(f"run_pba({name}, {options})")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def ping(self, host: str) -> Tuple[bool, Optional[str]]:
|
||||||
|
logger.debug(f"run_ping({host})")
|
||||||
|
if host == DOT_1:
|
||||||
|
return (True, "windows")
|
||||||
|
|
||||||
|
if host == DOT_2:
|
||||||
|
return (False, None)
|
||||||
|
|
||||||
|
if host == DOT_3:
|
||||||
|
return (True, "Linux")
|
||||||
|
|
||||||
|
if host == DOT_4:
|
||||||
|
return (False, None)
|
||||||
|
|
||||||
|
return (False, None)
|
||||||
|
|
||||||
|
def scan_tcp_port(self, host: str, port: int, timeout: int = 3) -> PortScanData:
|
||||||
|
logger.debug(f"run_scan_tcp_port({host}, {port}, {timeout})")
|
||||||
|
dot_1_results = {
|
||||||
|
22: PortScanData(22, PortStatus.CLOSED, None, None),
|
||||||
|
445: PortScanData(445, PortStatus.OPEN, "SMB BANNER", "tcp-445"),
|
||||||
|
3389: PortScanData(3389, PortStatus.OPEN, "", "tcp-3389"),
|
||||||
|
}
|
||||||
|
dot_3_results = {
|
||||||
|
22: PortScanData(22, PortStatus.OPEN, "SSH BANNER", "tcp-22"),
|
||||||
|
443: PortScanData(443, PortStatus.OPEN, "HTTPS BANNER", "tcp-443"),
|
||||||
|
3389: PortScanData(3389, PortStatus.CLOSED, "", None),
|
||||||
|
}
|
||||||
|
|
||||||
|
if host == DOT_1:
|
||||||
|
return dot_1_results.get(port, _get_empty_results(port))
|
||||||
|
|
||||||
|
if host == DOT_3:
|
||||||
|
return dot_3_results.get(port, _get_empty_results(port))
|
||||||
|
|
||||||
|
return _get_empty_results(port)
|
||||||
|
|
||||||
|
def fingerprint(self, name: str, host: str) -> Dict:
|
||||||
|
logger.debug(f"fingerprint({name}, {host})")
|
||||||
|
dot_1_results = {
|
||||||
|
"SMBFinger": {
|
||||||
|
"os": {"type": "windows", "version": "vista"},
|
||||||
|
"services": {"tcp-445": {"name": "SSH", "os": "linux"}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dot_3_results = {
|
||||||
|
"SSHFinger": {"os": "linux", "services": {"tcp-22": {"name": "SSH"}}},
|
||||||
|
"HTTPFinger": {
|
||||||
|
"services": {"tcp-https": {"name": "http", "data": ("SERVER_HEADERS", DOT_3)}}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if host == DOT_1:
|
||||||
|
return dot_1_results.get(name, {})
|
||||||
|
|
||||||
|
if host == DOT_3:
|
||||||
|
return dot_3_results.get(name, {})
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def exploit_host(self, name: str, host: str, options: Dict, interrupt: threading.Event) -> bool:
|
||||||
|
logger.debug(f"exploit_hosts({name}, {host}, {options})")
|
||||||
|
successful_exploiters = {DOT_1: {"PowerShellExploiter"}, DOT_3: {"SSHExploiter"}}
|
||||||
|
|
||||||
|
return name in successful_exploiters.get(host, {})
|
||||||
|
|
||||||
|
def run_payload(self, name: str, options: Dict, interrupt: threading.Event) -> None:
|
||||||
|
logger.debug(f"run_payload({name}, {options})")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def cleanup(self) -> None:
|
||||||
|
print("Cleanup called!")
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _get_empty_results(port: int):
|
||||||
|
return PortScanData(port, False, None, None)
|
|
@ -201,3 +201,11 @@ environment # unused variable (monkey/monkey_island/cc/models/monkey.py:59)
|
||||||
_.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:35)
|
_.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:35)
|
||||||
_.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:64)
|
_.instance_name # unused attribute (monkey/common/cloud/azure/azure_instance.py:64)
|
||||||
GCPHandler # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:57)
|
GCPHandler # unused function (envs/monkey_zoo/blackbox/test_blackbox.py:57)
|
||||||
|
|
||||||
|
# TODO: Reevaluate these as the agent refactor progresses
|
||||||
|
run_sys_info_collector
|
||||||
|
ping
|
||||||
|
scan_tcp_port
|
||||||
|
fingerprint
|
||||||
|
interrupt
|
||||||
|
MockPuppet
|
||||||
|
|
Loading…
Reference in New Issue