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:64)
|
||||
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