forked from p15670423/monkey
Merge pull request #2031 from guardicore/1960-configuration-repository
1960 configuration repository
This commit is contained in:
commit
58733f7572
|
@ -2,3 +2,4 @@ from .agent_configuration import (
|
||||||
AgentConfiguration,
|
AgentConfiguration,
|
||||||
AgentConfigurationSchema,
|
AgentConfigurationSchema,
|
||||||
)
|
)
|
||||||
|
from .default_agent_configuration import DEFAULT_AGENT_CONFIGURATION
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
DEFAULT_AGENT_CONFIGURATION = """{
|
||||||
|
"keep_tunnel_open_time": 30,
|
||||||
|
"post_breach_actions": [
|
||||||
|
{
|
||||||
|
"name": "CommunicateAsBackdoorUser",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ModifyShellStartupFiles",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HiddenFiles",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TrapCommand",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ChangeSetuidSetgid",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ScheduleJobs",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Timestomping",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AccountDiscovery",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ProcessListCollection",
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credential_collectors": [
|
||||||
|
{
|
||||||
|
"name": "MimikatzCollector",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SSHCollector",
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payloads": [
|
||||||
|
{
|
||||||
|
"name": "ransomware",
|
||||||
|
"options": {
|
||||||
|
"encryption": {
|
||||||
|
"enabled": true,
|
||||||
|
"directories": {
|
||||||
|
"linux_target_dir": "",
|
||||||
|
"windows_target_dir": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"other_behaviors": {
|
||||||
|
"readme": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"custom_pbas": {
|
||||||
|
"linux_command": "",
|
||||||
|
"linux_filename": "",
|
||||||
|
"windows_command": "",
|
||||||
|
"windows_filename": ""
|
||||||
|
},
|
||||||
|
"propagation": {
|
||||||
|
"maximum_depth": 2,
|
||||||
|
"network_scan": {
|
||||||
|
"tcp": {
|
||||||
|
"timeout": 3000,
|
||||||
|
"ports": [
|
||||||
|
22,
|
||||||
|
80,
|
||||||
|
135,
|
||||||
|
443,
|
||||||
|
445,
|
||||||
|
2222,
|
||||||
|
3306,
|
||||||
|
3389,
|
||||||
|
5985,
|
||||||
|
5986,
|
||||||
|
7001,
|
||||||
|
8008,
|
||||||
|
8080,
|
||||||
|
8088,
|
||||||
|
8983,
|
||||||
|
9200,
|
||||||
|
9600
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"icmp": {
|
||||||
|
"timeout": 1000
|
||||||
|
},
|
||||||
|
"fingerprinters": [
|
||||||
|
{
|
||||||
|
"name": "elastic",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "http",
|
||||||
|
"options": {
|
||||||
|
"http_ports": [
|
||||||
|
80,
|
||||||
|
443,
|
||||||
|
7001,
|
||||||
|
8008,
|
||||||
|
8080,
|
||||||
|
8983,
|
||||||
|
9200,
|
||||||
|
9600
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mssql",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "smb",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ssh",
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"targets": {
|
||||||
|
"blocked_ips": [],
|
||||||
|
"inaccessible_subnets": [],
|
||||||
|
"local_network_scan": true,
|
||||||
|
"subnets": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exploitation": {
|
||||||
|
"options": {
|
||||||
|
"http_ports": [
|
||||||
|
80,
|
||||||
|
443,
|
||||||
|
7001,
|
||||||
|
8008,
|
||||||
|
8080,
|
||||||
|
8983,
|
||||||
|
9200,
|
||||||
|
9600
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"brute_force": [
|
||||||
|
{
|
||||||
|
"name": "MSSQLExploiter",
|
||||||
|
"options": {},
|
||||||
|
"supported_os": [
|
||||||
|
"WINDOWS"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PowerShellExploiter",
|
||||||
|
"options": {},
|
||||||
|
"supported_os": [
|
||||||
|
"WINDOWS"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SSHExploiter",
|
||||||
|
"options": {},
|
||||||
|
"supported_os": [
|
||||||
|
"LINUX"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SmbExploiter",
|
||||||
|
"options": {
|
||||||
|
"smb_download_timeout": 30
|
||||||
|
},
|
||||||
|
"supported_os": [
|
||||||
|
"WINDOWS"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WmiExploiter",
|
||||||
|
"options": {
|
||||||
|
"smb_download_timeout": 30
|
||||||
|
},
|
||||||
|
"supported_os": [
|
||||||
|
"WINDOWS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vulnerability": [
|
||||||
|
{
|
||||||
|
"name": "HadoopExploiter",
|
||||||
|
"options": {},
|
||||||
|
"supported_os": [
|
||||||
|
"LINUX",
|
||||||
|
"WINDOWS"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Log4ShellExploiter",
|
||||||
|
"options": {},
|
||||||
|
"supported_os": [
|
||||||
|
"LINUX",
|
||||||
|
"WINDOWS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
|
@ -1,3 +1,6 @@
|
||||||
|
from .errors import RetrievalError
|
||||||
from .file_storage import FileRetrievalError, IFileRepository, LocalStorageFileRepository
|
from .file_storage import FileRetrievalError, IFileRepository, LocalStorageFileRepository
|
||||||
from .i_agent_binary_repository import IAgentBinaryRepository, AgentRetrievalError
|
from .i_agent_binary_repository import IAgentBinaryRepository, AgentRetrievalError
|
||||||
from .agent_binary_repository import AgentBinaryRepository
|
from .agent_binary_repository import AgentBinaryRepository
|
||||||
|
from .i_agent_configuration_repository import IAgentConfigurationRepository
|
||||||
|
from .file_agent_configuration_repository import FileAgentConfigurationRepository
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
class RetrievalError(RuntimeError):
|
||||||
|
pass
|
|
@ -0,0 +1,38 @@
|
||||||
|
import io
|
||||||
|
|
||||||
|
from common.configuration import (
|
||||||
|
DEFAULT_AGENT_CONFIGURATION,
|
||||||
|
AgentConfiguration,
|
||||||
|
AgentConfigurationSchema,
|
||||||
|
)
|
||||||
|
from monkey_island.cc.repository import (
|
||||||
|
IAgentConfigurationRepository,
|
||||||
|
IFileRepository,
|
||||||
|
RetrievalError,
|
||||||
|
)
|
||||||
|
|
||||||
|
AGENT_CONFIGURATION_FILE_NAME = "agent_configuration.json"
|
||||||
|
|
||||||
|
|
||||||
|
class FileAgentConfigurationRepository(IAgentConfigurationRepository):
|
||||||
|
def __init__(self, file_repository: IFileRepository):
|
||||||
|
self._file_repository = file_repository
|
||||||
|
self._schema = AgentConfigurationSchema()
|
||||||
|
|
||||||
|
def get_configuration(self) -> AgentConfiguration:
|
||||||
|
try:
|
||||||
|
with self._file_repository.open_file(AGENT_CONFIGURATION_FILE_NAME) as f:
|
||||||
|
configuration_json = f.read().decode()
|
||||||
|
|
||||||
|
return self._schema.loads(configuration_json)
|
||||||
|
# TODO: Handle FileRetrievalError vs FileNotFoundError
|
||||||
|
# https://github.com/guardicore/monkey/blob/e8001d8cf76340e42bf17ff62523bd2d85fc4841/monkey/monkey_island/cc/repository/file_storage/local_storage_file_repository.py#L47-L50
|
||||||
|
except RetrievalError:
|
||||||
|
return self._schema.loads(DEFAULT_AGENT_CONFIGURATION)
|
||||||
|
|
||||||
|
def store_configuration(self, agent_configuration: AgentConfiguration):
|
||||||
|
configuration_json = self._schema.dumps(agent_configuration)
|
||||||
|
|
||||||
|
self._file_repository.save_file(
|
||||||
|
AGENT_CONFIGURATION_FILE_NAME, io.BytesIO(configuration_json.encode())
|
||||||
|
)
|
|
@ -1,8 +1,10 @@
|
||||||
import abc
|
import abc
|
||||||
from typing import BinaryIO
|
from typing import BinaryIO
|
||||||
|
|
||||||
|
from monkey_island.cc.repository import RetrievalError
|
||||||
|
|
||||||
class FileRetrievalError(RuntimeError):
|
|
||||||
|
class FileRetrievalError(RetrievalError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from common.configuration import AgentConfiguration
|
||||||
|
|
||||||
|
|
||||||
|
class IAgentConfigurationRepository(ABC):
|
||||||
|
"""
|
||||||
|
A repository used to store and retrieve the agent configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_configuration(self) -> AgentConfiguration:
|
||||||
|
"""
|
||||||
|
Retrieve the agent configuration from the repository
|
||||||
|
|
||||||
|
:return: The agent configuration as retrieved from the repository, or the default
|
||||||
|
configuration if none could be retrieved.
|
||||||
|
:raises RetrievalError: if the configuration can not be retrieved
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def store_configuration(self, agent_configuration: AgentConfiguration):
|
||||||
|
"""
|
||||||
|
Store the agent configuration in the repository
|
||||||
|
|
||||||
|
:param agent_configuration: The agent configuration to store in the repository
|
||||||
|
"""
|
||||||
|
pass
|
|
@ -1,30 +0,0 @@
|
||||||
from abc import ABC
|
|
||||||
from typing import Any, Mapping, Sequence
|
|
||||||
|
|
||||||
|
|
||||||
class IConfigRepository(ABC):
|
|
||||||
|
|
||||||
# Config
|
|
||||||
###############################################
|
|
||||||
|
|
||||||
# This returns the current config
|
|
||||||
# TODO investigate if encryption should be here or where
|
|
||||||
# TODO potentially should be a DTO as well, but it's structure is defined in schema already
|
|
||||||
def get_config(self) -> Mapping:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_config(self, config: dict):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Used when only a subset of config is submitted, for example only PBAFiles
|
|
||||||
# Used by passing keys, like ['monkey', 'post_breach_actions', 'linux_filename']
|
|
||||||
# Using a list is less ambiguous IMO, than using . notation
|
|
||||||
def set_config_field(self, key_list: Sequence[str], value: Any):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Used when only a subset of config is needed, for example only PBAFiles
|
|
||||||
# Used by passing keys, like ['monkey', 'post_breach_actions', 'linux_filename']
|
|
||||||
# Using a list is less ambiguous IMO, than using . notation
|
|
||||||
# TODO Still in doubt about encryption, this should probably be determined automatically
|
|
||||||
def get_config_field(self, key_list: Sequence[str]) -> Any:
|
|
||||||
pass
|
|
|
@ -7,7 +7,9 @@ from common.utils.file_utils import get_binary_io_sha256_hash
|
||||||
from monkey_island.cc.repository import (
|
from monkey_island.cc.repository import (
|
||||||
AgentBinaryRepository,
|
AgentBinaryRepository,
|
||||||
AgentRetrievalError,
|
AgentRetrievalError,
|
||||||
|
FileAgentConfigurationRepository,
|
||||||
IAgentBinaryRepository,
|
IAgentBinaryRepository,
|
||||||
|
IAgentConfigurationRepository,
|
||||||
IFileRepository,
|
IFileRepository,
|
||||||
LocalStorageFileRepository,
|
LocalStorageFileRepository,
|
||||||
)
|
)
|
||||||
|
@ -31,11 +33,14 @@ def initialize_services(data_dir: Path) -> DIContainer:
|
||||||
container.register_instance(AWSInstance, AWSInstance())
|
container.register_instance(AWSInstance, AWSInstance())
|
||||||
|
|
||||||
container.register_instance(
|
container.register_instance(
|
||||||
IFileRepository, LocalStorageFileRepository(data_dir / "custom_pbas")
|
IFileRepository, LocalStorageFileRepository(data_dir / "runtime_data")
|
||||||
)
|
)
|
||||||
container.register_instance(AWSService, container.resolve(AWSService))
|
container.register_instance(AWSService, container.resolve(AWSService))
|
||||||
container.register_instance(IAgentBinaryRepository, _build_agent_binary_repository())
|
container.register_instance(IAgentBinaryRepository, _build_agent_binary_repository())
|
||||||
container.register_instance(LocalMonkeyRunService, container.resolve(LocalMonkeyRunService))
|
container.register_instance(LocalMonkeyRunService, container.resolve(LocalMonkeyRunService))
|
||||||
|
container.register_instance(
|
||||||
|
IAgentConfigurationRepository, container.resolve(FileAgentConfigurationRepository)
|
||||||
|
)
|
||||||
|
|
||||||
# This is temporary until we get DI all worked out.
|
# This is temporary until we get DI all worked out.
|
||||||
PostBreachFilesService.initialize(container.resolve(IFileRepository))
|
PostBreachFilesService.initialize(container.resolve(IFileRepository))
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
PLUGIN_NAME = "bond"
|
||||||
|
PLUGIN_OPTIONS = {"gun": "Walther PPK", "car": "Aston Martin DB5"}
|
||||||
|
PLUGIN_CONFIGURATION = {"name": PLUGIN_NAME, "options": PLUGIN_OPTIONS}
|
||||||
|
|
||||||
|
LINUX_COMMAND = "a"
|
||||||
|
LINUX_FILENAME = "b"
|
||||||
|
WINDOWS_COMMAND = "c"
|
||||||
|
WINDOWS_FILENAME = "d"
|
||||||
|
CUSTOM_PBA_CONFIGURATION = {
|
||||||
|
"linux_command": LINUX_COMMAND,
|
||||||
|
"linux_filename": LINUX_FILENAME,
|
||||||
|
"windows_command": WINDOWS_COMMAND,
|
||||||
|
"windows_filename": WINDOWS_FILENAME,
|
||||||
|
}
|
||||||
|
|
||||||
|
BLOCKED_IPS = ["10.0.0.1", "192.168.1.1"]
|
||||||
|
INACCESSIBLE_SUBNETS = ["172.0.0.0/24", "172.2.2.0/24", "192.168.56.0/24"]
|
||||||
|
LOCAL_NETWORK_SCAN = True
|
||||||
|
SUBNETS = ["10.0.0.2", "10.0.0.2/16"]
|
||||||
|
SCAN_TARGET_CONFIGURATION = {
|
||||||
|
"blocked_ips": BLOCKED_IPS,
|
||||||
|
"inaccessible_subnets": INACCESSIBLE_SUBNETS,
|
||||||
|
"local_network_scan": LOCAL_NETWORK_SCAN,
|
||||||
|
"subnets": SUBNETS,
|
||||||
|
}
|
||||||
|
|
||||||
|
TIMEOUT = 2.525
|
||||||
|
ICMP_CONFIGURATION = {"timeout": TIMEOUT}
|
||||||
|
|
||||||
|
PORTS = [8080, 443]
|
||||||
|
TCP_SCAN_CONFIGURATION = {"timeout": TIMEOUT, "ports": PORTS}
|
||||||
|
|
||||||
|
FINGERPRINTERS = [{"name": "mssql", "options": {}}]
|
||||||
|
NETWORK_SCAN_CONFIGURATION = {
|
||||||
|
"tcp": TCP_SCAN_CONFIGURATION,
|
||||||
|
"icmp": ICMP_CONFIGURATION,
|
||||||
|
"fingerprinters": FINGERPRINTERS,
|
||||||
|
"targets": SCAN_TARGET_CONFIGURATION,
|
||||||
|
}
|
||||||
|
|
||||||
|
BRUTE_FORCE = [
|
||||||
|
{"name": "ex1", "options": {}, "supported_os": ["LINUX"]},
|
||||||
|
{
|
||||||
|
"name": "ex2",
|
||||||
|
"options": {"smb_download_timeout": 10},
|
||||||
|
"supported_os": ["LINUX", "WINDOWS"],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
VULNERABILITY = [
|
||||||
|
{
|
||||||
|
"name": "ex3",
|
||||||
|
"options": {"smb_download_timeout": 10},
|
||||||
|
"supported_os": ["WINDOWS"],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
EXPLOITATION_CONFIGURATION = {
|
||||||
|
"options": {"http_ports": PORTS},
|
||||||
|
"brute_force": BRUTE_FORCE,
|
||||||
|
"vulnerability": VULNERABILITY,
|
||||||
|
}
|
||||||
|
|
||||||
|
PROPAGATION_CONFIGURATION = {
|
||||||
|
"maximum_depth": 5,
|
||||||
|
"network_scan": NETWORK_SCAN_CONFIGURATION,
|
||||||
|
"exploitation": EXPLOITATION_CONFIGURATION,
|
||||||
|
}
|
||||||
|
|
||||||
|
AGENT_CONFIGURATION = {
|
||||||
|
"keep_tunnel_open_time": 30,
|
||||||
|
"custom_pbas": CUSTOM_PBA_CONFIGURATION,
|
||||||
|
"post_breach_actions": [PLUGIN_CONFIGURATION],
|
||||||
|
"credential_collectors": [PLUGIN_CONFIGURATION],
|
||||||
|
"payloads": [PLUGIN_CONFIGURATION],
|
||||||
|
"propagation": PROPAGATION_CONFIGURATION,
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
from .single_file_repository import SingleFileRepository
|
|
@ -0,0 +1,23 @@
|
||||||
|
import io
|
||||||
|
from typing import BinaryIO
|
||||||
|
|
||||||
|
from monkey_island.cc.repository import FileRetrievalError, IFileRepository
|
||||||
|
|
||||||
|
|
||||||
|
class SingleFileRepository(IFileRepository):
|
||||||
|
def __init__(self):
|
||||||
|
self._file = None
|
||||||
|
|
||||||
|
def save_file(self, unsafe_file_name: str, file_contents: BinaryIO):
|
||||||
|
self._file = io.BytesIO(file_contents.read())
|
||||||
|
|
||||||
|
def open_file(self, unsafe_file_name: str) -> BinaryIO:
|
||||||
|
if self._file is None:
|
||||||
|
raise FileRetrievalError()
|
||||||
|
return self._file
|
||||||
|
|
||||||
|
def delete_file(self, unsafe_file_name: str):
|
||||||
|
self._file = None
|
||||||
|
|
||||||
|
def delete_all_files(self):
|
||||||
|
self.delete_file("")
|
|
@ -1,5 +1,34 @@
|
||||||
|
from tests.common.example_agent_configuration import (
|
||||||
|
AGENT_CONFIGURATION,
|
||||||
|
BLOCKED_IPS,
|
||||||
|
CUSTOM_PBA_CONFIGURATION,
|
||||||
|
EXPLOITATION_CONFIGURATION,
|
||||||
|
FINGERPRINTERS,
|
||||||
|
ICMP_CONFIGURATION,
|
||||||
|
INACCESSIBLE_SUBNETS,
|
||||||
|
LINUX_COMMAND,
|
||||||
|
LINUX_FILENAME,
|
||||||
|
LOCAL_NETWORK_SCAN,
|
||||||
|
NETWORK_SCAN_CONFIGURATION,
|
||||||
|
PLUGIN_CONFIGURATION,
|
||||||
|
PLUGIN_NAME,
|
||||||
|
PLUGIN_OPTIONS,
|
||||||
|
PORTS,
|
||||||
|
PROPAGATION_CONFIGURATION,
|
||||||
|
SCAN_TARGET_CONFIGURATION,
|
||||||
|
SUBNETS,
|
||||||
|
TCP_SCAN_CONFIGURATION,
|
||||||
|
TIMEOUT,
|
||||||
|
WINDOWS_COMMAND,
|
||||||
|
WINDOWS_FILENAME,
|
||||||
|
)
|
||||||
|
|
||||||
from common import OperatingSystems
|
from common import OperatingSystems
|
||||||
from common.configuration import AgentConfiguration, AgentConfigurationSchema
|
from common.configuration import (
|
||||||
|
DEFAULT_AGENT_CONFIGURATION,
|
||||||
|
AgentConfiguration,
|
||||||
|
AgentConfigurationSchema,
|
||||||
|
)
|
||||||
from common.configuration.agent_sub_configuration_schemas import (
|
from common.configuration.agent_sub_configuration_schemas import (
|
||||||
CustomPBAConfigurationSchema,
|
CustomPBAConfigurationSchema,
|
||||||
ExploitationConfigurationSchema,
|
ExploitationConfigurationSchema,
|
||||||
|
@ -20,30 +49,14 @@ from common.configuration.agent_sub_configurations import (
|
||||||
PropagationConfiguration,
|
PropagationConfiguration,
|
||||||
)
|
)
|
||||||
|
|
||||||
NAME = "bond"
|
|
||||||
OPTIONS = {"gun": "Walther PPK", "car": "Aston Martin DB5"}
|
|
||||||
PLUGIN_CONFIGURATION = {"name": NAME, "options": OPTIONS}
|
|
||||||
|
|
||||||
|
|
||||||
def test_build_plugin_configuration():
|
def test_build_plugin_configuration():
|
||||||
schema = PluginConfigurationSchema()
|
schema = PluginConfigurationSchema()
|
||||||
|
|
||||||
config = schema.load(PLUGIN_CONFIGURATION)
|
config = schema.load(PLUGIN_CONFIGURATION)
|
||||||
|
|
||||||
assert config.name == NAME
|
assert config.name == PLUGIN_NAME
|
||||||
assert config.options == OPTIONS
|
assert config.options == PLUGIN_OPTIONS
|
||||||
|
|
||||||
|
|
||||||
LINUX_COMMAND = "a"
|
|
||||||
LINUX_FILENAME = "b"
|
|
||||||
WINDOWS_COMMAND = "c"
|
|
||||||
WINDOWS_FILENAME = "d"
|
|
||||||
CUSTOM_PBA_CONFIGURATION = {
|
|
||||||
"linux_command": LINUX_COMMAND,
|
|
||||||
"linux_filename": LINUX_FILENAME,
|
|
||||||
"windows_command": WINDOWS_COMMAND,
|
|
||||||
"windows_filename": WINDOWS_FILENAME,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_custom_pba_configuration_schema():
|
def test_custom_pba_configuration_schema():
|
||||||
|
@ -57,18 +70,6 @@ def test_custom_pba_configuration_schema():
|
||||||
assert config.windows_filename == WINDOWS_FILENAME
|
assert config.windows_filename == WINDOWS_FILENAME
|
||||||
|
|
||||||
|
|
||||||
BLOCKED_IPS = ["10.0.0.1", "192.168.1.1"]
|
|
||||||
INACCESSIBLE_SUBNETS = ["172.0.0.0/24", "172.2.2.0/24", "192.168.56.0/24"]
|
|
||||||
LOCAL_NETWORK_SCAN = True
|
|
||||||
SUBNETS = ["10.0.0.2", "10.0.0.2/16"]
|
|
||||||
SCAN_TARGET_CONFIGURATION = {
|
|
||||||
"blocked_ips": BLOCKED_IPS,
|
|
||||||
"inaccessible_subnets": INACCESSIBLE_SUBNETS,
|
|
||||||
"local_network_scan": LOCAL_NETWORK_SCAN,
|
|
||||||
"subnets": SUBNETS,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_scan_target_configuration():
|
def test_scan_target_configuration():
|
||||||
schema = ScanTargetConfigurationSchema()
|
schema = ScanTargetConfigurationSchema()
|
||||||
|
|
||||||
|
@ -80,10 +81,6 @@ def test_scan_target_configuration():
|
||||||
assert config.subnets == SUBNETS
|
assert config.subnets == SUBNETS
|
||||||
|
|
||||||
|
|
||||||
TIMEOUT = 2.525
|
|
||||||
ICMP_CONFIGURATION = {"timeout": TIMEOUT}
|
|
||||||
|
|
||||||
|
|
||||||
def test_icmp_scan_configuration_schema():
|
def test_icmp_scan_configuration_schema():
|
||||||
schema = ICMPScanConfigurationSchema()
|
schema = ICMPScanConfigurationSchema()
|
||||||
|
|
||||||
|
@ -92,11 +89,6 @@ def test_icmp_scan_configuration_schema():
|
||||||
assert config.timeout == TIMEOUT
|
assert config.timeout == TIMEOUT
|
||||||
|
|
||||||
|
|
||||||
PORTS = [8080, 443]
|
|
||||||
|
|
||||||
TCP_SCAN_CONFIGURATION = {"timeout": TIMEOUT, "ports": PORTS}
|
|
||||||
|
|
||||||
|
|
||||||
def test_tcp_scan_configuration_schema():
|
def test_tcp_scan_configuration_schema():
|
||||||
schema = TCPScanConfigurationSchema()
|
schema = TCPScanConfigurationSchema()
|
||||||
|
|
||||||
|
@ -106,15 +98,6 @@ def test_tcp_scan_configuration_schema():
|
||||||
assert config.ports == PORTS
|
assert config.ports == PORTS
|
||||||
|
|
||||||
|
|
||||||
FINGERPRINTERS = [{"name": "mssql", "options": {}}]
|
|
||||||
NETWORK_SCAN_CONFIGURATION = {
|
|
||||||
"tcp": TCP_SCAN_CONFIGURATION,
|
|
||||||
"icmp": ICMP_CONFIGURATION,
|
|
||||||
"fingerprinters": FINGERPRINTERS,
|
|
||||||
"targets": SCAN_TARGET_CONFIGURATION,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_network_scan_configuration():
|
def test_network_scan_configuration():
|
||||||
schema = NetworkScanConfigurationSchema()
|
schema = NetworkScanConfigurationSchema()
|
||||||
|
|
||||||
|
@ -155,28 +138,6 @@ def test_exploiter_configuration_schema():
|
||||||
assert config.supported_os == supported_os
|
assert config.supported_os == supported_os
|
||||||
|
|
||||||
|
|
||||||
BRUTE_FORCE = [
|
|
||||||
{"name": "ex1", "options": {}, "supported_os": ["LINUX"]},
|
|
||||||
{
|
|
||||||
"name": "ex2",
|
|
||||||
"options": {"smb_download_timeout": 10},
|
|
||||||
"supported_os": ["LINUX", "WINDOWS"],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
VULNERABILITY = [
|
|
||||||
{
|
|
||||||
"name": "ex3",
|
|
||||||
"options": {"smb_download_timeout": 10},
|
|
||||||
"supported_os": ["WINDOWS"],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
EXPLOITATION_CONFIGURATION = {
|
|
||||||
"options": {"http_ports": PORTS},
|
|
||||||
"brute_force": BRUTE_FORCE,
|
|
||||||
"vulnerability": VULNERABILITY,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_exploitation_configuration():
|
def test_exploitation_configuration():
|
||||||
schema = ExploitationConfigurationSchema()
|
schema = ExploitationConfigurationSchema()
|
||||||
|
|
||||||
|
@ -187,13 +148,6 @@ def test_exploitation_configuration():
|
||||||
assert config_dict == EXPLOITATION_CONFIGURATION
|
assert config_dict == EXPLOITATION_CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
PROPAGATION_CONFIGURATION = {
|
|
||||||
"maximum_depth": 5,
|
|
||||||
"network_scan": NETWORK_SCAN_CONFIGURATION,
|
|
||||||
"exploitation": EXPLOITATION_CONFIGURATION,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_propagation_configuration():
|
def test_propagation_configuration():
|
||||||
schema = PropagationConfigurationSchema()
|
schema = PropagationConfigurationSchema()
|
||||||
|
|
||||||
|
@ -208,17 +162,9 @@ def test_propagation_configuration():
|
||||||
|
|
||||||
|
|
||||||
def test_agent_configuration():
|
def test_agent_configuration():
|
||||||
agent_configuration = {
|
|
||||||
"keep_tunnel_open_time": 30,
|
|
||||||
"custom_pbas": CUSTOM_PBA_CONFIGURATION,
|
|
||||||
"post_breach_actions": [PLUGIN_CONFIGURATION],
|
|
||||||
"credential_collectors": [PLUGIN_CONFIGURATION],
|
|
||||||
"payloads": [PLUGIN_CONFIGURATION],
|
|
||||||
"propagation": PROPAGATION_CONFIGURATION,
|
|
||||||
}
|
|
||||||
schema = AgentConfigurationSchema()
|
schema = AgentConfigurationSchema()
|
||||||
|
|
||||||
config = schema.load(agent_configuration)
|
config = schema.load(AGENT_CONFIGURATION)
|
||||||
config_dict = schema.dump(config)
|
config_dict = schema.dump(config)
|
||||||
|
|
||||||
assert isinstance(config, AgentConfiguration)
|
assert isinstance(config, AgentConfiguration)
|
||||||
|
@ -228,4 +174,12 @@ def test_agent_configuration():
|
||||||
assert isinstance(config.credential_collectors[0], PluginConfiguration)
|
assert isinstance(config.credential_collectors[0], PluginConfiguration)
|
||||||
assert isinstance(config.payloads[0], PluginConfiguration)
|
assert isinstance(config.payloads[0], PluginConfiguration)
|
||||||
assert isinstance(config.propagation, PropagationConfiguration)
|
assert isinstance(config.propagation, PropagationConfiguration)
|
||||||
assert config_dict == agent_configuration
|
assert config_dict == AGENT_CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_agent_configuration():
|
||||||
|
schema = AgentConfigurationSchema()
|
||||||
|
|
||||||
|
config = schema.loads(DEFAULT_AGENT_CONFIGURATION)
|
||||||
|
|
||||||
|
assert isinstance(config, AgentConfiguration)
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
from tests.common.example_agent_configuration import AGENT_CONFIGURATION
|
||||||
|
from tests.monkey_island import SingleFileRepository
|
||||||
|
|
||||||
|
from common.configuration import DEFAULT_AGENT_CONFIGURATION, AgentConfigurationSchema
|
||||||
|
from monkey_island.cc.repository import FileAgentConfigurationRepository
|
||||||
|
|
||||||
|
|
||||||
|
def test_store_agent_config():
|
||||||
|
repository = FileAgentConfigurationRepository(SingleFileRepository())
|
||||||
|
schema = AgentConfigurationSchema()
|
||||||
|
agent_configuration = schema.load(AGENT_CONFIGURATION)
|
||||||
|
|
||||||
|
repository.store_configuration(agent_configuration)
|
||||||
|
retrieved_agent_configuration = repository.get_configuration()
|
||||||
|
|
||||||
|
assert retrieved_agent_configuration == agent_configuration
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_default_agent_config():
|
||||||
|
repository = FileAgentConfigurationRepository(SingleFileRepository())
|
||||||
|
schema = AgentConfigurationSchema()
|
||||||
|
default_agent_configuration = schema.loads(DEFAULT_AGENT_CONFIGURATION)
|
||||||
|
|
||||||
|
retrieved_agent_configuration = repository.get_configuration()
|
||||||
|
|
||||||
|
assert retrieved_agent_configuration == default_agent_configuration
|
|
@ -1,12 +1,10 @@
|
||||||
import io
|
|
||||||
from typing import BinaryIO
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from tests.common import StubDIContainer
|
from tests.common import StubDIContainer
|
||||||
|
from tests.monkey_island import SingleFileRepository
|
||||||
from tests.unit_tests.monkey_island.conftest import get_url_for_resource
|
from tests.unit_tests.monkey_island.conftest import get_url_for_resource
|
||||||
from tests.utils import raise_
|
from tests.utils import raise_
|
||||||
|
|
||||||
from monkey_island.cc.repository import FileRetrievalError, IFileRepository
|
from monkey_island.cc.repository import IFileRepository
|
||||||
from monkey_island.cc.resources.pba_file_upload import LINUX_PBA_TYPE, WINDOWS_PBA_TYPE, FileUpload
|
from monkey_island.cc.resources.pba_file_upload import LINUX_PBA_TYPE, WINDOWS_PBA_TYPE, FileUpload
|
||||||
|
|
||||||
TEST_FILE_CONTENTS = b"m0nk3y"
|
TEST_FILE_CONTENTS = b"m0nk3y"
|
||||||
|
@ -40,28 +38,9 @@ def mock_get_config_value(monkeypatch):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MockFileRepository(IFileRepository):
|
|
||||||
def __init__(self):
|
|
||||||
self._file = None
|
|
||||||
|
|
||||||
def save_file(self, unsafe_file_name: str, file_contents: BinaryIO):
|
|
||||||
self._file = io.BytesIO(file_contents.read())
|
|
||||||
|
|
||||||
def open_file(self, unsafe_file_name: str) -> BinaryIO:
|
|
||||||
if self._file is None:
|
|
||||||
raise FileRetrievalError()
|
|
||||||
return self._file
|
|
||||||
|
|
||||||
def delete_file(self, unsafe_file_name: str):
|
|
||||||
self._file = None
|
|
||||||
|
|
||||||
def delete_all_files(self):
|
|
||||||
self.delete_file("")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def file_repository():
|
def file_repository():
|
||||||
return MockFileRepository()
|
return SingleFileRepository()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
Loading…
Reference in New Issue