Merge pull request #2103 from guardicore/2092-modify-blackbox-tests
Modify BB tests to use new configuration objects
This commit is contained in:
commit
4d3fb03da2
|
@ -1,2 +1 @@
|
||||||
logs/
|
logs/
|
||||||
/blackbox/tests/performance/telemetry_sample
|
|
||||||
|
|
|
@ -16,8 +16,6 @@ Either run pytest from `/monkey` directory or set `PYTHONPATH` environment varia
|
||||||
Blackbox tests have following parameters:
|
Blackbox tests have following parameters:
|
||||||
- `--island=IP` Sets island's IP
|
- `--island=IP` Sets island's IP
|
||||||
- `--no-gcp` (Optional) Use for no interaction with the cloud (local test).
|
- `--no-gcp` (Optional) Use for no interaction with the cloud (local test).
|
||||||
- `--quick-performance-tests` (Optional) If enabled performance tests won't reset island and won't send telemetries,
|
|
||||||
instead will just test performance of endpoints in already present island state.
|
|
||||||
|
|
||||||
Example run command:
|
Example run command:
|
||||||
|
|
||||||
|
@ -26,26 +24,3 @@ Example run command:
|
||||||
#### Running in PyCharm
|
#### Running in PyCharm
|
||||||
Configure a PyTest configuration with the additional arguments `-s --island=35.207.152.72:5000`, and to run from
|
Configure a PyTest configuration with the additional arguments `-s --island=35.207.152.72:5000`, and to run from
|
||||||
directory `monkey\envs\monkey_zoo\blackbox`.
|
directory `monkey\envs\monkey_zoo\blackbox`.
|
||||||
|
|
||||||
### Running telemetry performance test
|
|
||||||
|
|
||||||
**Before running performance test make sure browser is not sending requests to island!**
|
|
||||||
|
|
||||||
To run telemetry performance test follow these steps:
|
|
||||||
0. Set no password protection on the island.
|
|
||||||
Make sure the island parameter is an IP address(not localhost) as the name resolution will increase the time for requests.
|
|
||||||
1. Gather monkey telemetries.
|
|
||||||
1. Enable "Export monkey telemetries" in Configuration -> Internal -> Tests if you don't have
|
|
||||||
exported telemetries already.
|
|
||||||
2. Run monkey and wait until infection is done.
|
|
||||||
3. All telemetries are gathered in `monkey/telem_sample`. If not, restart the island process.
|
|
||||||
2. Run telemetry performance test.
|
|
||||||
1. Move directory `monkey/telem_sample` to `envs/monkey_zoo/blackbox/tests/performance/telemetry_sample`
|
|
||||||
2. (Optional) Use `envs/monkey_zoo/blackbox/tests/performance/telem_sample_parsing/sample_multiplier/sample_multiplier.py` to multiply
|
|
||||||
telemetries gathered.
|
|
||||||
1. Run `sample_multiplier.py` script with working directory set to `monkey\envs\monkey_zoo\blackbox`
|
|
||||||
2. Pass integer to indicate the multiplier. For example running `telem_parser.py 4` will replicate
|
|
||||||
telemetries 4 times.
|
|
||||||
3. If you're using pycharm check "Emulate terminal in output console" on debug/run configuration.
|
|
||||||
3. Add a `--run-performance-tests` flag to blackbox scripts to run performance tests as part of BlackBox tests.
|
|
||||||
You can run a single test separately by adding `-k 'test_telem_performance'` option.
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
|
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
|
||||||
from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog
|
from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog
|
||||||
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
||||||
|
|
||||||
|
|
||||||
class CommunicationAnalyzer(Analyzer):
|
class CommunicationAnalyzer(Analyzer):
|
||||||
def __init__(self, island_client, machine_ips):
|
def __init__(self, island_client: MonkeyIslandClient, machine_ips: Iterable[str]):
|
||||||
self.island_client = island_client
|
self.island_client = island_client
|
||||||
self.machine_ips = machine_ips
|
self.machine_ips = machine_ips
|
||||||
self.log = AnalyzerLog(self.__class__.__name__)
|
self.log = AnalyzerLog(self.__class__.__name__)
|
||||||
|
@ -19,6 +22,6 @@ class CommunicationAnalyzer(Analyzer):
|
||||||
self.log.add_entry("Monkey from {} communicated back".format(machine_ip))
|
self.log.add_entry("Monkey from {} communicated back".format(machine_ip))
|
||||||
return all_monkeys_communicated
|
return all_monkeys_communicated
|
||||||
|
|
||||||
def did_monkey_communicate_back(self, machine_ip):
|
def did_monkey_communicate_back(self, machine_ip: str):
|
||||||
query = {"ip_addresses": {"$elemMatch": {"$eq": machine_ip}}}
|
query = {"ip_addresses": {"$elemMatch": {"$eq": machine_ip}}}
|
||||||
return len(self.island_client.find_monkeys_in_db(query)) > 0
|
return len(self.island_client.find_monkeys_in_db(query)) > 0
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class PerformanceAnalyzer(Analyzer):
|
|
||||||
def __init__(
|
|
||||||
self, performance_test_config: PerformanceTestConfig, endpoint_timings: Dict[str, timedelta]
|
|
||||||
):
|
|
||||||
self.performance_test_config = performance_test_config
|
|
||||||
self.endpoint_timings = endpoint_timings
|
|
||||||
|
|
||||||
def analyze_test_results(self):
|
|
||||||
# Calculate total time and check each endpoint
|
|
||||||
single_page_time_less_then_max = True
|
|
||||||
total_time = timedelta()
|
|
||||||
for endpoint, elapsed in self.endpoint_timings.items():
|
|
||||||
total_time += elapsed
|
|
||||||
if elapsed > self.performance_test_config.max_allowed_single_page_time:
|
|
||||||
single_page_time_less_then_max = False
|
|
||||||
|
|
||||||
total_time_less_then_max = total_time < self.performance_test_config.max_allowed_total_time
|
|
||||||
|
|
||||||
PerformanceAnalyzer.log_slowest_endpoints(self.endpoint_timings)
|
|
||||||
LOGGER.info(f"Total time is {str(total_time)}")
|
|
||||||
|
|
||||||
performance_is_good_enough = total_time_less_then_max and single_page_time_less_then_max
|
|
||||||
|
|
||||||
if self.performance_test_config.break_on_timeout and not performance_is_good_enough:
|
|
||||||
LOGGER.warning(
|
|
||||||
"Calling breakpoint - pausing to enable investigation of island. "
|
|
||||||
"Type 'c' to continue once you're done "
|
|
||||||
"investigating. Type 'p timings' and 'p total_time' to see performance information."
|
|
||||||
)
|
|
||||||
breakpoint()
|
|
||||||
|
|
||||||
return performance_is_good_enough
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def log_slowest_endpoints(endpoint_timings, max_endpoints_to_display=100):
|
|
||||||
slow_endpoint_list = list(endpoint_timings.items())
|
|
||||||
slow_endpoint_list.sort(key=lambda x: x[1], reverse=True)
|
|
||||||
slow_endpoint_list = slow_endpoint_list[:max_endpoints_to_display]
|
|
||||||
for endpoint in slow_endpoint_list:
|
|
||||||
LOGGER.info(f"{endpoint[0]} took {str(endpoint[1])}")
|
|
|
@ -1,9 +1,6 @@
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import dpath.util
|
|
||||||
|
|
||||||
from common.config_value_paths import LM_HASH_LIST_PATH, NTLM_HASH_LIST_PATH, USER_LIST_PATH
|
|
||||||
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
|
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
|
||||||
from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog
|
from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
||||||
|
@ -36,9 +33,11 @@ class ZerologonAnalyzer(Analyzer):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_relevant_credentials(config: dict):
|
def _get_relevant_credentials(config: dict):
|
||||||
credentials_on_island = []
|
credentials_on_island = []
|
||||||
credentials_on_island.extend(dpath.util.get(config["configuration"], USER_LIST_PATH))
|
# TODO: Pull configured credentials and put usernames, nt and lm hashes into
|
||||||
credentials_on_island.extend(dpath.util.get(config["configuration"], NTLM_HASH_LIST_PATH))
|
# credentials_island
|
||||||
credentials_on_island.extend(dpath.util.get(config["configuration"], LM_HASH_LIST_PATH))
|
# credentials_on_island.extend(dpath.util.get(config["configuration"], USER_LIST_PATH))
|
||||||
|
# credentials_on_island.extend(dpath.util.get(config["configuration"], NTLM_HASH_LIST_PATH))
|
||||||
|
# credentials_on_island.extend(dpath.util.get(config["configuration"], LM_HASH_LIST_PATH))
|
||||||
return credentials_on_island
|
return credentials_on_island
|
||||||
|
|
||||||
def _is_all_credentials_in_list(self, all_creds: List[str]) -> bool:
|
def _is_all_credentials_in_list(self, all_creds: List[str]) -> bool:
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
|
|
||||||
|
|
||||||
# Disables a lot of config values not required for a specific feature test
|
|
||||||
class BaseTemplate(ConfigTemplate):
|
|
||||||
|
|
||||||
config_values = {
|
|
||||||
"basic.exploiters.exploiter_classes": [],
|
|
||||||
"basic_network.scope.local_network_scan": False,
|
|
||||||
"basic_network.scope.depth": 1,
|
|
||||||
"internal.classes.finger_classes": ["HTTPFinger"],
|
|
||||||
"internal.monkey.system_info.system_info_collector_classes": [],
|
|
||||||
"monkey.post_breach.post_breach_actions": [],
|
|
||||||
"internal.general.keep_tunnel_open_time": 0,
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigTemplate(ABC):
|
|
||||||
@property
|
|
||||||
@abstractmethod
|
|
||||||
def config_values(self) -> dict:
|
|
||||||
pass
|
|
|
@ -1,42 +0,0 @@
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class Depth1A(ConfigTemplate):
|
|
||||||
config_values = copy(BaseTemplate.config_values)
|
|
||||||
# Tests:
|
|
||||||
# Hadoop (10.2.2.2, 10.2.2.3)
|
|
||||||
# Log4shell (10.2.3.55, 10.2.3.56, 10.2.3.49, 10.2.3.50, 10.2.3.51, 10.2.3.52)
|
|
||||||
# MSSQL (10.2.2.16)
|
|
||||||
# SMB mimikatz password stealing and brute force (10.2.2.14 and 10.2.2.15)
|
|
||||||
config_values.update(
|
|
||||||
{
|
|
||||||
"basic.exploiters.exploiter_classes": [
|
|
||||||
"HadoopExploiter",
|
|
||||||
"Log4ShellExploiter",
|
|
||||||
"MSSQLExploiter",
|
|
||||||
"SmbExploiter",
|
|
||||||
"SSHExploiter",
|
|
||||||
],
|
|
||||||
"basic_network.scope.subnet_scan_list": [
|
|
||||||
"10.2.2.2",
|
|
||||||
"10.2.2.3",
|
|
||||||
"10.2.3.55",
|
|
||||||
"10.2.3.56",
|
|
||||||
"10.2.3.49",
|
|
||||||
"10.2.3.50",
|
|
||||||
"10.2.3.51",
|
|
||||||
"10.2.3.52",
|
|
||||||
"10.2.2.16",
|
|
||||||
"10.2.2.14",
|
|
||||||
"10.2.2.15",
|
|
||||||
],
|
|
||||||
"basic.credentials.exploit_password_list": ["Ivrrw5zEzs", "Xk8VDTsC"],
|
|
||||||
"basic.credentials.exploit_user_list": ["m0nk3y"],
|
|
||||||
"monkey.system_info.system_info_collector_classes": [
|
|
||||||
"MimikatzCollector",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,23 +0,0 @@
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class Depth2A(ConfigTemplate):
|
|
||||||
config_values = copy(BaseTemplate.config_values)
|
|
||||||
# SSH password and key brute-force, key stealing (10.2.2.11, 10.2.2.12)
|
|
||||||
config_values.update(
|
|
||||||
{
|
|
||||||
"basic.exploiters.exploiter_classes": [
|
|
||||||
"SSHExploiter",
|
|
||||||
],
|
|
||||||
"basic_network.scope.subnet_scan_list": [
|
|
||||||
"10.2.2.11",
|
|
||||||
"10.2.2.12",
|
|
||||||
],
|
|
||||||
"basic_network.scope.depth": 2,
|
|
||||||
"basic.credentials.exploit_password_list": ["^NgDvY59~8"],
|
|
||||||
"basic.credentials.exploit_user_list": ["m0nk3y"],
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,48 +0,0 @@
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class Depth3A(ConfigTemplate):
|
|
||||||
config_values = copy(BaseTemplate.config_values)
|
|
||||||
|
|
||||||
# Tests:
|
|
||||||
# Powershell (10.2.3.45, 10.2.3.46, 10.2.3.47, 10.2.3.48)
|
|
||||||
# Tunneling (SSH brute force) (10.2.2.9, 10.2.1.10, 10.2.0.12, 10.2.0.11)
|
|
||||||
# WMI pass the hash (10.2.2.15)
|
|
||||||
config_values.update(
|
|
||||||
{
|
|
||||||
"basic.exploiters.exploiter_classes": [
|
|
||||||
"PowerShellExploiter",
|
|
||||||
"SSHExploiter",
|
|
||||||
"WmiExploiter",
|
|
||||||
],
|
|
||||||
"basic_network.scope.subnet_scan_list": [
|
|
||||||
"10.2.2.9",
|
|
||||||
"10.2.3.45",
|
|
||||||
"10.2.3.46",
|
|
||||||
"10.2.3.47",
|
|
||||||
"10.2.3.48",
|
|
||||||
"10.2.1.10",
|
|
||||||
"10.2.0.12",
|
|
||||||
"10.2.0.11",
|
|
||||||
"10.2.2.15",
|
|
||||||
],
|
|
||||||
"basic.credentials.exploit_password_list": [
|
|
||||||
"Passw0rd!",
|
|
||||||
"3Q=(Ge(+&w]*",
|
|
||||||
"`))jU7L(w}",
|
|
||||||
"t67TC5ZDmz",
|
|
||||||
],
|
|
||||||
"basic_network.scope.depth": 3,
|
|
||||||
"internal.general.keep_tunnel_open_time": 20,
|
|
||||||
"basic.credentials.exploit_user_list": ["m0nk3y", "m0nk3y-user"],
|
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
|
||||||
"internal.exploits.exploit_ntlm_hash_list": [
|
|
||||||
"d0f0132b308a0c4e5d1029cc06f48692",
|
|
||||||
"5da0889ea2081aa79f6852294cba4a5e",
|
|
||||||
"50c9987a6bf1ac59398df9f911122c9b",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,21 +0,0 @@
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class PowerShellCredentialsReuse(ConfigTemplate):
|
|
||||||
config_values = copy(BaseTemplate.config_values)
|
|
||||||
|
|
||||||
config_values.update(
|
|
||||||
{
|
|
||||||
"basic.exploiters.exploiter_classes": ["PowerShellExploiter"],
|
|
||||||
"basic_network.scope.subnet_scan_list": [
|
|
||||||
"10.2.3.46",
|
|
||||||
],
|
|
||||||
"basic_network.scope.depth": 2,
|
|
||||||
"internal.classes.finger_classes": [],
|
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [5985, 5986],
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,24 +0,0 @@
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class SmbPth(ConfigTemplate):
|
|
||||||
config_values = copy(BaseTemplate.config_values)
|
|
||||||
|
|
||||||
config_values.update(
|
|
||||||
{
|
|
||||||
"basic.exploiters.exploiter_classes": ["SmbExploiter"],
|
|
||||||
"basic_network.scope.subnet_scan_list": ["10.2.2.15"],
|
|
||||||
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
|
|
||||||
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
|
||||||
"internal.classes.finger_classes": ["SMBFinger", "HTTPFinger"],
|
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [445],
|
|
||||||
"internal.classes.exploits.exploit_ntlm_hash_list": [
|
|
||||||
"5da0889ea2081aa79f6852294cba4a5e",
|
|
||||||
"50c9987a6bf1ac59398df9f911122c9b",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,22 +0,0 @@
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class WmiMimikatz(ConfigTemplate):
|
|
||||||
config_values = copy(BaseTemplate.config_values)
|
|
||||||
|
|
||||||
config_values.update(
|
|
||||||
{
|
|
||||||
"basic.exploiters.exploiter_classes": ["WmiExploiter"],
|
|
||||||
"basic_network.scope.subnet_scan_list": ["10.2.2.14", "10.2.2.15"],
|
|
||||||
"basic.credentials.exploit_password_list": ["Password1!", "Ivrrw5zEzs"],
|
|
||||||
"basic.credentials.exploit_user_list": ["Administrator", "m0nk3y", "user"],
|
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [135],
|
|
||||||
"monkey.system_info.system_info_collector_classes": [
|
|
||||||
"MimikatzCollector",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,20 +0,0 @@
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class Zerologon(ConfigTemplate):
|
|
||||||
|
|
||||||
config_values = copy(BaseTemplate.config_values)
|
|
||||||
|
|
||||||
config_values.update(
|
|
||||||
{
|
|
||||||
"basic.exploiters.exploiter_classes": ["ZerologonExploiter", "SmbExploiter"],
|
|
||||||
"basic_network.scope.subnet_scan_list": ["10.2.2.25"],
|
|
||||||
# Empty list to make sure ZeroLogon adds "Administrator" username
|
|
||||||
"basic.credentials.exploit_user_list": [],
|
|
||||||
"internal.network.tcp_scanner.HTTP_PORTS": [],
|
|
||||||
"internal.network.tcp_scanner.tcp_target_ports": [135, 445],
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -14,19 +14,6 @@ def pytest_addoption(parser):
|
||||||
default=False,
|
default=False,
|
||||||
help="Use for no interaction with the cloud.",
|
help="Use for no interaction with the cloud.",
|
||||||
)
|
)
|
||||||
parser.addoption(
|
|
||||||
"--quick-performance-tests",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="If enabled performance tests won't reset island and won't send telemetries, "
|
|
||||||
"instead will just test performance of already present island state.",
|
|
||||||
)
|
|
||||||
parser.addoption(
|
|
||||||
"--run-performance-tests",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="If enabled performance tests will be run.",
|
|
||||||
)
|
|
||||||
parser.addoption(
|
parser.addoption(
|
||||||
"--skip-powershell-reuse",
|
"--skip-powershell-reuse",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
|
@ -45,19 +32,7 @@ def no_gcp(request):
|
||||||
return request.config.getoption("--no-gcp")
|
return request.config.getoption("--no-gcp")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def quick_performance_tests(request):
|
|
||||||
return request.config.getoption("--quick-performance-tests")
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
if "run_performance_tests" in item.keywords and not item.config.getoption(
|
|
||||||
"--run-performance-tests"
|
|
||||||
):
|
|
||||||
pytest.skip(
|
|
||||||
"Skipping performance test because " "--run-performance-tests flag isn't specified."
|
|
||||||
)
|
|
||||||
|
|
||||||
if "skip_powershell_reuse" in item.keywords and item.config.getoption(
|
if "skip_powershell_reuse" in item.keywords and item.config.getoption(
|
||||||
"--skip-powershell-reuse"
|
"--skip-powershell-reuse"
|
||||||
):
|
):
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import json
|
|
||||||
|
|
||||||
import dpath.util
|
|
||||||
from typing_extensions import Type
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
|
||||||
|
|
||||||
|
|
||||||
class IslandConfigParser:
|
|
||||||
@staticmethod
|
|
||||||
def get_raw_config(
|
|
||||||
config_template: Type[ConfigTemplate], island_client: MonkeyIslandClient
|
|
||||||
) -> str:
|
|
||||||
response = island_client.get_config()
|
|
||||||
config = IslandConfigParser.apply_template_to_config(
|
|
||||||
config_template, response["configuration"]
|
|
||||||
)
|
|
||||||
return json.dumps(config)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def apply_template_to_config(config_template: Type[ConfigTemplate], config: dict) -> dict:
|
|
||||||
for path, value in config_template.config_values.items():
|
|
||||||
dpath.util.set(config, path, value, ".")
|
|
||||||
return config
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_ips_of_targets(raw_config):
|
|
||||||
return dpath.util.get(json.loads(raw_config), "basic_network.scope.subnet_scan_list", ".")
|
|
|
@ -5,7 +5,10 @@ from typing import Union
|
||||||
|
|
||||||
from bson import json_util
|
from bson import json_util
|
||||||
|
|
||||||
|
from common.configuration import AgentConfiguration
|
||||||
|
from common.credentials import Credentials
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests
|
||||||
|
from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration
|
||||||
|
|
||||||
SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5
|
SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5
|
||||||
MONKEY_TEST_ENDPOINT = "api/test/monkey"
|
MONKEY_TEST_ENDPOINT = "api/test/monkey"
|
||||||
|
@ -27,15 +30,26 @@ class MonkeyIslandClient(object):
|
||||||
return self.requests.get("api")
|
return self.requests.get("api")
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
return json.loads(self.requests.get("api/configuration/island").content)
|
return json.loads(self.requests.get("api/agent-configuration").content)
|
||||||
|
|
||||||
@avoid_race_condition
|
@avoid_race_condition
|
||||||
def import_config(self, config_contents):
|
def import_config(self, test_configuration: TestConfiguration):
|
||||||
_ = self.requests.post("api/configuration/island", data=config_contents)
|
self.requests.post_json(
|
||||||
|
"api/agent-configuration",
|
||||||
|
json=AgentConfiguration.to_mapping(test_configuration.agent_configuration),
|
||||||
|
)
|
||||||
|
serialized_propagation_credentials = [
|
||||||
|
Credentials.to_mapping(credentials)
|
||||||
|
for credentials in test_configuration.propagation_credentials
|
||||||
|
]
|
||||||
|
self.requests.post_json(
|
||||||
|
"/api/propagation-credentials/configured-credentials",
|
||||||
|
json=serialized_propagation_credentials,
|
||||||
|
)
|
||||||
|
|
||||||
@avoid_race_condition
|
@avoid_race_condition
|
||||||
def run_monkey_local(self):
|
def run_monkey_local(self):
|
||||||
response = self.requests.post_json("api/local-monkey", data={"action": "run"})
|
response = self.requests.post_json("api/local-monkey", json={"action": "run"})
|
||||||
if MonkeyIslandClient.monkey_ran_successfully(response):
|
if MonkeyIslandClient.monkey_ran_successfully(response):
|
||||||
LOGGER.info("Running the monkey.")
|
LOGGER.info("Running the monkey.")
|
||||||
else:
|
else:
|
||||||
|
@ -49,7 +63,7 @@ class MonkeyIslandClient(object):
|
||||||
@avoid_race_condition
|
@avoid_race_condition
|
||||||
def kill_all_monkeys(self):
|
def kill_all_monkeys(self):
|
||||||
response = self.requests.post_json(
|
response = self.requests.post_json(
|
||||||
"api/monkey-control/stop-all-agents", data={"kill_time": time.time()}
|
"api/monkey-control/stop-all-agents", json={"kill_time": time.time()}
|
||||||
)
|
)
|
||||||
if response.ok:
|
if response.ok:
|
||||||
LOGGER.info("Killing all monkeys after the test.")
|
LOGGER.info("Killing all monkeys after the test.")
|
||||||
|
@ -67,10 +81,6 @@ class MonkeyIslandClient(object):
|
||||||
LOGGER.error("Failed to reset the environment.")
|
LOGGER.error("Failed to reset the environment.")
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
@avoid_race_condition
|
|
||||||
def set_scenario(self, scenario):
|
|
||||||
self.requests.post_json("api/island-mode", {"mode": scenario})
|
|
||||||
|
|
||||||
def find_monkeys_in_db(self, query):
|
def find_monkeys_in_db(self, query):
|
||||||
if query is None:
|
if query is None:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
|
@ -110,13 +120,3 @@ class MonkeyIslandClient(object):
|
||||||
def is_all_monkeys_dead(self):
|
def is_all_monkeys_dead(self):
|
||||||
query = {"dead": False}
|
query = {"dead": False}
|
||||||
return len(self.find_monkeys_in_db(query)) == 0
|
return len(self.find_monkeys_in_db(query)) == 0
|
||||||
|
|
||||||
def clear_caches(self):
|
|
||||||
"""
|
|
||||||
Tries to clear caches.
|
|
||||||
:raises: If error (by error code), raises the error
|
|
||||||
:return: The response
|
|
||||||
"""
|
|
||||||
response = self.requests.get("api/test/clear-caches")
|
|
||||||
response.raise_for_status()
|
|
||||||
return response
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod
|
|
||||||
|
|
||||||
ISLAND_USERNAME = "test"
|
ISLAND_USERNAME = "test"
|
||||||
ISLAND_PASSWORD = "test"
|
ISLAND_PASSWORD = "test"
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -25,28 +22,6 @@ class MonkeyIslandRequests(object):
|
||||||
def __init__(self, server_address):
|
def __init__(self, server_address):
|
||||||
self.addr = "https://{IP}/".format(IP=server_address)
|
self.addr = "https://{IP}/".format(IP=server_address)
|
||||||
self.token = self.try_get_jwt_from_server()
|
self.token = self.try_get_jwt_from_server()
|
||||||
self.supported_request_methods = {
|
|
||||||
SupportedRequestMethod.GET: self.get,
|
|
||||||
SupportedRequestMethod.POST: self.post,
|
|
||||||
SupportedRequestMethod.PATCH: self.patch,
|
|
||||||
SupportedRequestMethod.DELETE: self.delete,
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_request_time(self, url, method: SupportedRequestMethod, data=None):
|
|
||||||
response = self.send_request_by_method(url, method, data)
|
|
||||||
if response.ok:
|
|
||||||
LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120].strip()}")
|
|
||||||
return response.elapsed
|
|
||||||
else:
|
|
||||||
LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}")
|
|
||||||
# instead of raising for status, mark failed responses as maxtime
|
|
||||||
return timedelta.max
|
|
||||||
|
|
||||||
def send_request_by_method(self, url, method=SupportedRequestMethod.GET, data=None):
|
|
||||||
if data:
|
|
||||||
return self.supported_request_methods[method](url, data)
|
|
||||||
else:
|
|
||||||
return self.supported_request_methods[method](url)
|
|
||||||
|
|
||||||
def try_get_jwt_from_server(self):
|
def try_get_jwt_from_server(self):
|
||||||
try:
|
try:
|
||||||
|
@ -108,9 +83,9 @@ class MonkeyIslandRequests(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
@_Decorators.refresh_jwt_token
|
@_Decorators.refresh_jwt_token
|
||||||
def post_json(self, url, data: Dict):
|
def post_json(self, url, json: Dict):
|
||||||
return requests.post( # noqa: DUO123
|
return requests.post( # noqa: DUO123
|
||||||
self.addr + url, json=data, headers=self.get_jwt_header(), verify=False
|
self.addr + url, json=json, headers=self.get_jwt_header(), verify=False
|
||||||
)
|
)
|
||||||
|
|
||||||
@_Decorators.refresh_jwt_token
|
@_Decorators.refresh_jwt_token
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
class SupportedRequestMethod(Enum):
|
|
||||||
GET = "GET"
|
|
||||||
POST = "POST"
|
|
||||||
PATCH = "PATCH"
|
|
||||||
DELETE = "DELETE"
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration
|
||||||
|
|
||||||
|
|
||||||
|
def get_target_ips(test_configuration: TestConfiguration) -> Iterable[str]:
|
||||||
|
return test_configuration.agent_configuration.propagation.network_scan.targets.subnets
|
|
@ -3,31 +3,29 @@ import os
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from typing_extensions import Type
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer
|
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer
|
||||||
from envs.monkey_zoo.blackbox.analyzers.zerologon_analyzer import ZerologonAnalyzer
|
from envs.monkey_zoo.blackbox.analyzers.zerologon_analyzer import ZerologonAnalyzer
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.depth_1_a import Depth1A
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.depth_2_a import Depth2A
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.depth_3_a import Depth3A
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.powershell_credentials_reuse import (
|
|
||||||
PowerShellCredentialsReuse,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.wmi_mimikatz import WmiMimikatz
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.zerologon import Zerologon
|
|
||||||
from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_TEST_MACHINE_LIST
|
from envs.monkey_zoo.blackbox.gcp_test_machine_list import GCP_TEST_MACHINE_LIST
|
||||||
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
||||||
|
from envs.monkey_zoo.blackbox.island_client.test_configuration_parser import get_target_ips
|
||||||
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
|
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
|
||||||
|
from envs.monkey_zoo.blackbox.test_configurations import (
|
||||||
|
depth_1_a_test_configuration,
|
||||||
|
depth_2_a_test_configuration,
|
||||||
|
depth_3_a_test_configuration,
|
||||||
|
powershell_credentials_reuse_test_configuration,
|
||||||
|
smb_pth_test_configuration,
|
||||||
|
wmi_mimikatz_test_configuration,
|
||||||
|
zerologon_test_configuration,
|
||||||
|
)
|
||||||
|
from envs.monkey_zoo.blackbox.test_configurations.test_configuration import TestConfiguration
|
||||||
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
|
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
|
||||||
from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
|
from envs.monkey_zoo.blackbox.utils.gcp_machine_handlers import (
|
||||||
initialize_gcp_client,
|
initialize_gcp_client,
|
||||||
start_machines,
|
start_machines,
|
||||||
stop_machines,
|
stop_machines,
|
||||||
)
|
)
|
||||||
from monkey_island.cc.services.mode.mode_enum import IslandModeEnum
|
|
||||||
|
|
||||||
DEFAULT_TIMEOUT_SECONDS = 2 * 60 + 30
|
DEFAULT_TIMEOUT_SECONDS = 2 * 60 + 30
|
||||||
MACHINE_BOOTUP_WAIT_SECONDS = 30
|
MACHINE_BOOTUP_WAIT_SECONDS = 30
|
||||||
|
@ -64,7 +62,7 @@ def wait_machine_bootup():
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="class")
|
||||||
def island_client(island, quick_performance_tests):
|
def island_client(island):
|
||||||
client_established = False
|
client_established = False
|
||||||
try:
|
try:
|
||||||
island_client_object = MonkeyIslandClient(island)
|
island_client_object = MonkeyIslandClient(island)
|
||||||
|
@ -74,9 +72,6 @@ def island_client(island, quick_performance_tests):
|
||||||
finally:
|
finally:
|
||||||
if not client_established:
|
if not client_established:
|
||||||
pytest.exit("BB tests couldn't establish communication to the island.")
|
pytest.exit("BB tests couldn't establish communication to the island.")
|
||||||
if not quick_performance_tests:
|
|
||||||
island_client_object.reset_env()
|
|
||||||
island_client_object.set_scenario(IslandModeEnum.ADVANCED.value)
|
|
||||||
yield island_client_object
|
yield island_client_object
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,13 +81,13 @@ class TestMonkeyBlackbox:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run_exploitation_test(
|
def run_exploitation_test(
|
||||||
island_client: MonkeyIslandClient,
|
island_client: MonkeyIslandClient,
|
||||||
config_template: Type[ConfigTemplate],
|
test_configuration: TestConfiguration,
|
||||||
test_name: str,
|
test_name: str,
|
||||||
timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS,
|
timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS,
|
||||||
):
|
):
|
||||||
raw_config = IslandConfigParser.get_raw_config(config_template, island_client)
|
|
||||||
analyzer = CommunicationAnalyzer(
|
analyzer = CommunicationAnalyzer(
|
||||||
island_client, IslandConfigParser.get_ips_of_targets(raw_config)
|
island_client,
|
||||||
|
get_target_ips(test_configuration),
|
||||||
)
|
)
|
||||||
log_handler = TestLogsHandler(
|
log_handler = TestLogsHandler(
|
||||||
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
|
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
|
||||||
|
@ -100,7 +95,7 @@ class TestMonkeyBlackbox:
|
||||||
ExploitationTest(
|
ExploitationTest(
|
||||||
name=test_name,
|
name=test_name,
|
||||||
island_client=island_client,
|
island_client=island_client,
|
||||||
raw_config=raw_config,
|
test_configuration=test_configuration,
|
||||||
analyzers=[analyzer],
|
analyzers=[analyzer],
|
||||||
timeout=timeout_in_seconds,
|
timeout=timeout_in_seconds,
|
||||||
log_handler=log_handler,
|
log_handler=log_handler,
|
||||||
|
@ -113,20 +108,26 @@ class TestMonkeyBlackbox:
|
||||||
# If test_depth_1_a() is run first, some test will fail because machines are not yet fully
|
# If test_depth_1_a() is run first, some test will fail because machines are not yet fully
|
||||||
# booted. Running test_depth_2_a() first gives slow VMs extra time to boot.
|
# booted. Running test_depth_2_a() first gives slow VMs extra time to boot.
|
||||||
def test_depth_2_a(self, island_client):
|
def test_depth_2_a(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, Depth2A, "Depth2A test suite")
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, depth_2_a_test_configuration, "Depth2A test suite"
|
||||||
|
)
|
||||||
|
|
||||||
def test_depth_1_a(self, island_client):
|
def test_depth_1_a(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, Depth1A, "Depth1A test suite")
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, depth_1_a_test_configuration, "Depth1A test suite"
|
||||||
|
)
|
||||||
|
|
||||||
def test_depth_3_a(self, island_client):
|
def test_depth_3_a(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, Depth3A, "Depth3A test suite")
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, depth_3_a_test_configuration, "Depth3A test suite"
|
||||||
|
)
|
||||||
|
|
||||||
# Not grouped because can only be ran on windows
|
# Not grouped because can only be ran on windows
|
||||||
@pytest.mark.skip_powershell_reuse
|
@pytest.mark.skip_powershell_reuse
|
||||||
def test_powershell_exploiter_credentials_reuse(self, island_client):
|
def test_powershell_exploiter_credentials_reuse(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
island_client,
|
island_client,
|
||||||
PowerShellCredentialsReuse,
|
powershell_credentials_reuse_test_configuration,
|
||||||
"PowerShell_Remoting_exploiter_credentials_reuse",
|
"PowerShell_Remoting_exploiter_credentials_reuse",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -138,10 +139,10 @@ class TestMonkeyBlackbox:
|
||||||
"aad3b435b51404eeaad3b435b51404ee",
|
"aad3b435b51404eeaad3b435b51404ee",
|
||||||
"2864b62ea4496934a5d6e86f50b834a5",
|
"2864b62ea4496934a5d6e86f50b834a5",
|
||||||
]
|
]
|
||||||
raw_config = IslandConfigParser.get_raw_config(Zerologon, island_client)
|
|
||||||
zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds)
|
zero_logon_analyzer = ZerologonAnalyzer(island_client, expected_creds)
|
||||||
communication_analyzer = CommunicationAnalyzer(
|
communication_analyzer = CommunicationAnalyzer(
|
||||||
island_client, IslandConfigParser.get_ips_of_targets(raw_config)
|
island_client,
|
||||||
|
get_target_ips(zerologon_test_configuration),
|
||||||
)
|
)
|
||||||
log_handler = TestLogsHandler(
|
log_handler = TestLogsHandler(
|
||||||
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
|
test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()
|
||||||
|
@ -149,7 +150,7 @@ class TestMonkeyBlackbox:
|
||||||
ExploitationTest(
|
ExploitationTest(
|
||||||
name=test_name,
|
name=test_name,
|
||||||
island_client=island_client,
|
island_client=island_client,
|
||||||
raw_config=raw_config,
|
test_configuration=zerologon_test_configuration,
|
||||||
analyzers=[zero_logon_analyzer, communication_analyzer],
|
analyzers=[zero_logon_analyzer, communication_analyzer],
|
||||||
timeout=DEFAULT_TIMEOUT_SECONDS + 30,
|
timeout=DEFAULT_TIMEOUT_SECONDS + 30,
|
||||||
log_handler=log_handler,
|
log_handler=log_handler,
|
||||||
|
@ -159,9 +160,11 @@ class TestMonkeyBlackbox:
|
||||||
# Consider grouping when more depth 1 exploiters collide with group depth_1_a
|
# Consider grouping when more depth 1 exploiters collide with group depth_1_a
|
||||||
def test_wmi_and_mimikatz_exploiters(self, island_client):
|
def test_wmi_and_mimikatz_exploiters(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
island_client, WmiMimikatz, "WMI_exploiter,_mimikatz"
|
island_client, wmi_mimikatz_test_configuration, "WMI_exploiter,_mimikatz"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Not grouped because it's depth 1 but conflicts with SMB exploiter in group depth_1_a
|
# Not grouped because it's depth 1 but conflicts with SMB exploiter in group depth_1_a
|
||||||
def test_smb_pth(self, island_client):
|
def test_smb_pth(self, island_client):
|
||||||
TestMonkeyBlackbox.run_exploitation_test(island_client, SmbPth, "SMB_PTH")
|
TestMonkeyBlackbox.run_exploitation_test(
|
||||||
|
island_client, smb_pth_test_configuration, "SMB_PTH"
|
||||||
|
)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from .test_configuration import TestConfiguration
|
from .test_configuration import TestConfiguration
|
||||||
from .zerologon import zerologon_test_configuration
|
|
||||||
from .depth_1_a import depth_1_a_test_configuration
|
from .depth_1_a import depth_1_a_test_configuration
|
||||||
from .depth_2_a import depth_2_a_test_configuration
|
from .depth_2_a import depth_2_a_test_configuration
|
||||||
from .depth_3_a import depth_3_a_test_configuration
|
from .depth_3_a import depth_3_a_test_configuration
|
||||||
from .powershell_credentials_reuse import powershell_credentials_reuse_test_configuration
|
from .powershell_credentials_reuse import powershell_credentials_reuse_test_configuration
|
||||||
from .smb_pth import smb_pth_test_configuration
|
from .smb_pth import smb_pth_test_configuration
|
||||||
from .wmi_mimikatz import wmi_mimikatz_test_configuration
|
from .wmi_mimikatz import wmi_mimikatz_test_configuration
|
||||||
|
from .zerologon import zerologon_test_configuration
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
from envs.monkey_zoo.blackbox.island_client.test_configuration_parser import get_target_ips
|
||||||
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
||||||
from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
|
from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
|
||||||
|
|
||||||
|
@ -13,16 +13,16 @@ LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ExploitationTest(BasicTest):
|
class ExploitationTest(BasicTest):
|
||||||
def __init__(self, name, island_client, raw_config, analyzers, timeout, log_handler):
|
def __init__(self, name, island_client, test_configuration, analyzers, timeout, log_handler):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.island_client = island_client
|
self.island_client = island_client
|
||||||
self.raw_config = raw_config
|
self.test_configuration = test_configuration
|
||||||
self.analyzers = analyzers
|
self.analyzers = analyzers
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.log_handler = log_handler
|
self.log_handler = log_handler
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.island_client.import_config(self.raw_config)
|
self.island_client.import_config(self.test_configuration)
|
||||||
self.print_test_starting_info()
|
self.print_test_starting_info()
|
||||||
try:
|
try:
|
||||||
self.island_client.run_monkey_local()
|
self.island_client.run_monkey_local()
|
||||||
|
@ -36,7 +36,7 @@ class ExploitationTest(BasicTest):
|
||||||
|
|
||||||
def print_test_starting_info(self):
|
def print_test_starting_info(self):
|
||||||
LOGGER.info("Started {} test".format(self.name))
|
LOGGER.info("Started {} test".format(self.name))
|
||||||
machine_list = ", ".join(IslandConfigParser.get_ips_of_targets(self.raw_config))
|
machine_list = ", ".join(get_target_ips(self.test_configuration))
|
||||||
LOGGER.info(f"Machines participating in test: {machine_list}")
|
LOGGER.info(f"Machines participating in test: {machine_list}")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
import logging
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.analyzers.performance_analyzer import PerformanceAnalyzer
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod
|
|
||||||
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class EndpointPerformanceTest(BasicTest):
|
|
||||||
def __init__(self, name, test_config: PerformanceTestConfig, island_client: MonkeyIslandClient):
|
|
||||||
self.name = name
|
|
||||||
self.test_config = test_config
|
|
||||||
self.island_client = island_client
|
|
||||||
|
|
||||||
def run(self) -> bool:
|
|
||||||
# Collect timings for all pages
|
|
||||||
endpoint_timings = {}
|
|
||||||
for endpoint in self.test_config.endpoints_to_test:
|
|
||||||
self.island_client.clear_caches()
|
|
||||||
endpoint_timings[endpoint] = self.island_client.requests.get_request_time(
|
|
||||||
endpoint, SupportedRequestMethod.GET
|
|
||||||
)
|
|
||||||
analyzer = PerformanceAnalyzer(self.test_config, endpoint_timings)
|
|
||||||
|
|
||||||
return analyzer.analyze_test_results()
|
|
|
@ -1,40 +0,0 @@
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_workflow import (
|
|
||||||
PerformanceTestWorkflow,
|
|
||||||
)
|
|
||||||
|
|
||||||
MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2)
|
|
||||||
MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5)
|
|
||||||
|
|
||||||
MAP_RESOURCES = [
|
|
||||||
"api/netmap",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class MapGenerationTest(PerformanceTest):
|
|
||||||
|
|
||||||
TEST_NAME = "Map generation performance test"
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, island_client, raw_config, analyzers, timeout, log_handler, break_on_timeout
|
|
||||||
):
|
|
||||||
self.island_client = island_client
|
|
||||||
exploitation_test = ExploitationTest(
|
|
||||||
MapGenerationTest.TEST_NAME, island_client, raw_config, analyzers, timeout, log_handler
|
|
||||||
)
|
|
||||||
performance_config = PerformanceTestConfig(
|
|
||||||
max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
|
|
||||||
max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
|
|
||||||
endpoints_to_test=MAP_RESOURCES,
|
|
||||||
break_on_timeout=break_on_timeout,
|
|
||||||
)
|
|
||||||
self.performance_test_workflow = PerformanceTestWorkflow(
|
|
||||||
MapGenerationTest.TEST_NAME, exploitation_test, performance_config
|
|
||||||
)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.performance_test_workflow.run()
|
|
|
@ -1,37 +0,0 @@
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test_workflow import (
|
|
||||||
TelemetryPerformanceTestWorkflow,
|
|
||||||
)
|
|
||||||
|
|
||||||
MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2)
|
|
||||||
MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5)
|
|
||||||
|
|
||||||
MAP_RESOURCES = [
|
|
||||||
"api/netmap",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class MapGenerationFromTelemetryTest(PerformanceTest):
|
|
||||||
|
|
||||||
TEST_NAME = "Map generation from fake telemetries test"
|
|
||||||
|
|
||||||
def __init__(self, island_client, quick_performance_test: bool, break_on_timeout=False):
|
|
||||||
self.island_client = island_client
|
|
||||||
performance_config = PerformanceTestConfig(
|
|
||||||
max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
|
|
||||||
max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
|
|
||||||
endpoints_to_test=MAP_RESOURCES,
|
|
||||||
break_on_timeout=break_on_timeout,
|
|
||||||
)
|
|
||||||
self.performance_test_workflow = TelemetryPerformanceTestWorkflow(
|
|
||||||
MapGenerationFromTelemetryTest.TEST_NAME,
|
|
||||||
self.island_client,
|
|
||||||
performance_config,
|
|
||||||
quick_performance_test,
|
|
||||||
)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.performance_test_workflow.run()
|
|
|
@ -1,16 +0,0 @@
|
||||||
from abc import ABCMeta, abstractmethod
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
|
||||||
|
|
||||||
|
|
||||||
class PerformanceTest(BasicTest, metaclass=ABCMeta):
|
|
||||||
@abstractmethod
|
|
||||||
def __init__(
|
|
||||||
self, island_client, raw_config, analyzers, timeout, log_handler, break_on_timeout
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
@abstractmethod
|
|
||||||
def TEST_NAME(self):
|
|
||||||
pass
|
|
|
@ -1,16 +0,0 @@
|
||||||
from datetime import timedelta
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
|
|
||||||
class PerformanceTestConfig:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
max_allowed_single_page_time: timedelta,
|
|
||||||
max_allowed_total_time: timedelta,
|
|
||||||
endpoints_to_test: List[str] = None,
|
|
||||||
break_on_timeout=False,
|
|
||||||
):
|
|
||||||
self.max_allowed_single_page_time = max_allowed_single_page_time
|
|
||||||
self.max_allowed_total_time = max_allowed_total_time
|
|
||||||
self.endpoints_to_test = endpoints_to_test
|
|
||||||
self.break_on_timeout = break_on_timeout
|
|
|
@ -1,40 +0,0 @@
|
||||||
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.endpoint_performance_test import (
|
|
||||||
EndpointPerformanceTest,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
|
|
||||||
|
|
||||||
class PerformanceTestWorkflow(BasicTest):
|
|
||||||
def __init__(
|
|
||||||
self, name, exploitation_test: ExploitationTest, performance_config: PerformanceTestConfig
|
|
||||||
):
|
|
||||||
self.name = name
|
|
||||||
self.exploitation_test = exploitation_test
|
|
||||||
self.island_client = exploitation_test.island_client
|
|
||||||
self.raw_config = exploitation_test.raw_config
|
|
||||||
self.performance_config = performance_config
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.island_client.import_config(self.raw_config)
|
|
||||||
self.exploitation_test.print_test_starting_info()
|
|
||||||
try:
|
|
||||||
self.island_client.run_monkey_local()
|
|
||||||
self.exploitation_test.test_until_timeout()
|
|
||||||
finally:
|
|
||||||
self.island_client.kill_all_monkeys()
|
|
||||||
self.exploitation_test.wait_until_monkeys_die()
|
|
||||||
self.exploitation_test.wait_for_monkey_process_to_finish()
|
|
||||||
if not self.island_client.is_all_monkeys_dead():
|
|
||||||
raise RuntimeError("Can't test report times since not all Monkeys have died.")
|
|
||||||
performance_test = EndpointPerformanceTest(
|
|
||||||
self.name, self.performance_config, self.island_client
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
if not self.island_client.is_all_monkeys_dead():
|
|
||||||
raise RuntimeError("Can't test report times since not all Monkeys have died.")
|
|
||||||
assert performance_test.run()
|
|
||||||
finally:
|
|
||||||
self.exploitation_test.parse_logs()
|
|
||||||
self.island_client.reset_env()
|
|
|
@ -1,48 +0,0 @@
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_workflow import (
|
|
||||||
PerformanceTestWorkflow,
|
|
||||||
)
|
|
||||||
|
|
||||||
MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2)
|
|
||||||
MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5)
|
|
||||||
|
|
||||||
REPORT_RESOURCES = [
|
|
||||||
"api/report/security",
|
|
||||||
"api/attack/report",
|
|
||||||
"api/report/zero_trust/findings",
|
|
||||||
"api/report/zero_trust/principles",
|
|
||||||
"api/report/zero_trust/pillars",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ReportGenerationTest(PerformanceTest):
|
|
||||||
TEST_NAME = "Report generation performance test"
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, island_client, raw_config, analyzers, timeout, log_handler, break_on_timeout
|
|
||||||
):
|
|
||||||
self.island_client = island_client
|
|
||||||
exploitation_test = ExploitationTest(
|
|
||||||
ReportGenerationTest.TEST_NAME,
|
|
||||||
island_client,
|
|
||||||
raw_config,
|
|
||||||
analyzers,
|
|
||||||
timeout,
|
|
||||||
log_handler,
|
|
||||||
)
|
|
||||||
performance_config = PerformanceTestConfig(
|
|
||||||
max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
|
|
||||||
max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
|
|
||||||
endpoints_to_test=REPORT_RESOURCES,
|
|
||||||
break_on_timeout=break_on_timeout,
|
|
||||||
)
|
|
||||||
self.performance_test_workflow = PerformanceTestWorkflow(
|
|
||||||
ReportGenerationTest.TEST_NAME, exploitation_test, performance_config
|
|
||||||
)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.performance_test_workflow.run()
|
|
|
@ -1,41 +0,0 @@
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test_workflow import (
|
|
||||||
TelemetryPerformanceTestWorkflow,
|
|
||||||
)
|
|
||||||
|
|
||||||
MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2)
|
|
||||||
MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5)
|
|
||||||
|
|
||||||
REPORT_RESOURCES = [
|
|
||||||
"api/report/security",
|
|
||||||
"api/attack/report",
|
|
||||||
"api/report/zero_trust/findings",
|
|
||||||
"api/report/zero_trust/principles",
|
|
||||||
"api/report/zero_trust/pillars",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ReportGenerationFromTelemetryTest(PerformanceTest):
|
|
||||||
|
|
||||||
TEST_NAME = "Map generation from fake telemetries test"
|
|
||||||
|
|
||||||
def __init__(self, island_client, quick_performance_test, break_on_timeout=False):
|
|
||||||
self.island_client = island_client
|
|
||||||
performance_config = PerformanceTestConfig(
|
|
||||||
max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
|
|
||||||
max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
|
|
||||||
endpoints_to_test=REPORT_RESOURCES,
|
|
||||||
break_on_timeout=break_on_timeout,
|
|
||||||
)
|
|
||||||
self.performance_test_workflow = TelemetryPerformanceTestWorkflow(
|
|
||||||
ReportGenerationFromTelemetryTest.TEST_NAME,
|
|
||||||
self.island_client,
|
|
||||||
performance_config,
|
|
||||||
quick_performance_test,
|
|
||||||
)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.performance_test_workflow.run()
|
|
|
@ -1,51 +0,0 @@
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
from os import listdir, path
|
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from tqdm import tqdm
|
|
||||||
|
|
||||||
TELEM_DIR_PATH = "../envs/monkey_zoo/blackbox/tests/performance/telemetry_sample"
|
|
||||||
MAX_SAME_TYPE_TELEM_FILES = 10000
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SampleFileParser:
|
|
||||||
@staticmethod
|
|
||||||
def save_teletries_to_files(telems: List[Dict]):
|
|
||||||
for telem in tqdm(telems, desc="Telemetries saved to files", position=3):
|
|
||||||
SampleFileParser.save_telemetry_to_file(telem)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def save_telemetry_to_file(telem: Dict):
|
|
||||||
telem_filename = telem["name"] + telem["method"]
|
|
||||||
for i in range(MAX_SAME_TYPE_TELEM_FILES):
|
|
||||||
if not path.exists(path.join(TELEM_DIR_PATH, (str(i) + telem_filename))):
|
|
||||||
telem_filename = str(i) + telem_filename
|
|
||||||
break
|
|
||||||
with open(path.join(TELEM_DIR_PATH, telem_filename), "w") as file:
|
|
||||||
file.write(json.dumps(telem))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def read_telem_files() -> List[str]:
|
|
||||||
telems = []
|
|
||||||
try:
|
|
||||||
file_paths = [
|
|
||||||
path.join(TELEM_DIR_PATH, f)
|
|
||||||
for f in listdir(TELEM_DIR_PATH)
|
|
||||||
if path.isfile(path.join(TELEM_DIR_PATH, f))
|
|
||||||
]
|
|
||||||
except FileNotFoundError:
|
|
||||||
raise FileNotFoundError(
|
|
||||||
"Telemetries to send not found. "
|
|
||||||
"Refer to readme to figure out how to generate telemetries and where to put them."
|
|
||||||
)
|
|
||||||
for file_path in file_paths:
|
|
||||||
with open(file_path, "r") as telem_file:
|
|
||||||
telem_string = "".join(telem_file.readlines()).replace("\n", "")
|
|
||||||
telems.append(telem_string)
|
|
||||||
return telems
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_all_telemetries() -> List[Dict]:
|
|
||||||
return [json.loads(t) for t in SampleFileParser.read_telem_files()]
|
|
|
@ -1,25 +0,0 @@
|
||||||
from typing import List
|
|
||||||
|
|
||||||
|
|
||||||
class FakeIpGenerator:
|
|
||||||
def __init__(self):
|
|
||||||
self.fake_ip_parts = [1, 1, 1, 1]
|
|
||||||
|
|
||||||
def generate_fake_ips_for_real_ips(self, real_ips: List[str]) -> List[str]:
|
|
||||||
fake_ips = []
|
|
||||||
for i in range(len(real_ips)):
|
|
||||||
fake_ips.append(".".join(str(part) for part in self.fake_ip_parts))
|
|
||||||
self.increment_ip()
|
|
||||||
return fake_ips
|
|
||||||
|
|
||||||
def increment_ip(self):
|
|
||||||
self.fake_ip_parts[3] += 1
|
|
||||||
self.try_fix_ip_range()
|
|
||||||
|
|
||||||
def try_fix_ip_range(self):
|
|
||||||
for i in range(len(self.fake_ip_parts)):
|
|
||||||
if self.fake_ip_parts[i] > 256:
|
|
||||||
if i - 1 < 0:
|
|
||||||
raise Exception("Fake IP's out of range.")
|
|
||||||
self.fake_ip_parts[i - 1] += 1
|
|
||||||
self.fake_ip_parts[i] = 1
|
|
|
@ -1,19 +0,0 @@
|
||||||
import random
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501
|
|
||||||
FakeIpGenerator,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FakeMonkey:
|
|
||||||
def __init__(self, ips, guid, fake_ip_generator: FakeIpGenerator, on_island=False):
|
|
||||||
self.original_ips = ips
|
|
||||||
self.original_guid = guid
|
|
||||||
self.fake_ip_generator = fake_ip_generator
|
|
||||||
self.on_island = on_island
|
|
||||||
self.fake_guid = str(random.randint(1000000000000, 9999999999999)) # noqa: DUO102
|
|
||||||
self.fake_ips = fake_ip_generator.generate_fake_ips_for_real_ips(ips)
|
|
||||||
|
|
||||||
def change_fake_data(self):
|
|
||||||
self.fake_ips = self.fake_ip_generator.generate_fake_ips_for_real_ips(self.original_ips)
|
|
||||||
self.fake_guid = str(random.randint(1000000000000, 9999999999999)) # noqa: DUO102
|
|
|
@ -1,107 +0,0 @@
|
||||||
import copy
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from tqdm import tqdm
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_file_parser import (
|
|
||||||
SampleFileParser,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501
|
|
||||||
FakeIpGenerator,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_monkey import ( # noqa: E501
|
|
||||||
FakeMonkey,
|
|
||||||
)
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SampleMultiplier:
|
|
||||||
def __init__(self, multiplier: int):
|
|
||||||
self.multiplier = multiplier
|
|
||||||
self.fake_ip_generator = FakeIpGenerator()
|
|
||||||
|
|
||||||
def multiply_telems(self):
|
|
||||||
telems = SampleFileParser.get_all_telemetries()
|
|
||||||
telem_contents = [json.loads(telem["content"]) for telem in telems]
|
|
||||||
monkeys = self.get_monkeys_from_telems(telem_contents)
|
|
||||||
for i in tqdm(range(self.multiplier), desc="Batch of fabricated telemetries", position=1):
|
|
||||||
for monkey in monkeys:
|
|
||||||
monkey.change_fake_data()
|
|
||||||
fake_telem_batch = copy.deepcopy(telems)
|
|
||||||
SampleMultiplier.fabricate_monkeys_in_telems(fake_telem_batch, monkeys)
|
|
||||||
SampleMultiplier.offset_telem_times(iteration=i, telems=fake_telem_batch)
|
|
||||||
SampleFileParser.save_teletries_to_files(fake_telem_batch)
|
|
||||||
LOGGER.info("")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def fabricate_monkeys_in_telems(telems: List[Dict], monkeys: List[FakeMonkey]):
|
|
||||||
for telem in tqdm(telems, desc="Telemetries fabricated", position=2):
|
|
||||||
for monkey in monkeys:
|
|
||||||
if monkey.on_island:
|
|
||||||
continue
|
|
||||||
if (
|
|
||||||
monkey.original_guid in telem["content"]
|
|
||||||
or monkey.original_guid in telem["endpoint"]
|
|
||||||
) and not monkey.on_island:
|
|
||||||
telem["content"] = telem["content"].replace(
|
|
||||||
monkey.original_guid, monkey.fake_guid
|
|
||||||
)
|
|
||||||
telem["endpoint"] = telem["endpoint"].replace(
|
|
||||||
monkey.original_guid, monkey.fake_guid
|
|
||||||
)
|
|
||||||
for i in range(len(monkey.original_ips)):
|
|
||||||
telem["content"] = telem["content"].replace(
|
|
||||||
monkey.original_ips[i], monkey.fake_ips[i]
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def offset_telem_times(iteration: int, telems: List[Dict]):
|
|
||||||
for telem in telems:
|
|
||||||
telem["time"]["$date"] += iteration * 1000
|
|
||||||
|
|
||||||
def get_monkeys_from_telems(self, telems: List[Dict]):
|
|
||||||
island_ips = SampleMultiplier.get_island_ips_from_telems(telems)
|
|
||||||
monkeys = []
|
|
||||||
for telem in [
|
|
||||||
telem
|
|
||||||
for telem in telems
|
|
||||||
if "telem_category" in telem and telem["telem_category"] == "system_info"
|
|
||||||
]:
|
|
||||||
if "network_info" not in telem["data"]:
|
|
||||||
continue
|
|
||||||
guid = telem["monkey_guid"]
|
|
||||||
monkey_present = [monkey for monkey in monkeys if monkey.original_guid == guid]
|
|
||||||
if not monkey_present:
|
|
||||||
ips = [net_info["addr"] for net_info in telem["data"]["network_info"]["networks"]]
|
|
||||||
if set(island_ips).intersection(ips):
|
|
||||||
on_island = True
|
|
||||||
else:
|
|
||||||
on_island = False
|
|
||||||
|
|
||||||
monkeys.append(
|
|
||||||
FakeMonkey(
|
|
||||||
ips=ips,
|
|
||||||
guid=guid,
|
|
||||||
fake_ip_generator=self.fake_ip_generator,
|
|
||||||
on_island=on_island,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return monkeys
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_island_ips_from_telems(telems: List[Dict]) -> List[str]:
|
|
||||||
island_ips = []
|
|
||||||
for telem in telems:
|
|
||||||
if "config" in telem:
|
|
||||||
island_ips = telem["config"]["command_servers"]
|
|
||||||
for i in range(len(island_ips)):
|
|
||||||
island_ips[i] = island_ips[i].replace(":5000", "")
|
|
||||||
return island_ips
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
SampleMultiplier(multiplier=int(sys.argv[1])).multiply_telems()
|
|
|
@ -1,21 +0,0 @@
|
||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_multiplier.fake_ip_generator import ( # noqa: E501
|
|
||||||
FakeIpGenerator,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestFakeIpGenerator(TestCase):
|
|
||||||
def test_fake_ip_generation(self):
|
|
||||||
fake_ip_gen = FakeIpGenerator()
|
|
||||||
self.assertListEqual([1, 1, 1, 1], fake_ip_gen.fake_ip_parts)
|
|
||||||
for i in range(256):
|
|
||||||
fake_ip_gen.generate_fake_ips_for_real_ips(["1.1.1.1"])
|
|
||||||
self.assertListEqual(["1.1.2.1"], fake_ip_gen.generate_fake_ips_for_real_ips(["1.1.1.1"]))
|
|
||||||
fake_ip_gen.fake_ip_parts = [256, 256, 255, 256]
|
|
||||||
self.assertListEqual(
|
|
||||||
["256.256.255.256", "256.256.256.1"],
|
|
||||||
fake_ip_gen.generate_fake_ips_for_real_ips(["1.1.1.1", "1.1.1.2"]),
|
|
||||||
)
|
|
||||||
fake_ip_gen.fake_ip_parts = [256, 256, 256, 256]
|
|
||||||
self.assertRaises(Exception, fake_ip_gen.generate_fake_ips_for_real_ips(["1.1.1.1"]))
|
|
|
@ -1,66 +0,0 @@
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.analyzers.performance_analyzer import PerformanceAnalyzer
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.supported_request_method import SupportedRequestMethod
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telem_sample_parsing.sample_file_parser import (
|
|
||||||
SampleFileParser,
|
|
||||||
)
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
MAX_ALLOWED_SINGLE_TELEM_PARSE_TIME = timedelta(seconds=2)
|
|
||||||
MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=60)
|
|
||||||
|
|
||||||
|
|
||||||
class TelemetryPerformanceTest:
|
|
||||||
def __init__(self, island_client: MonkeyIslandClient, quick_performance_test: bool):
|
|
||||||
self.island_client = island_client
|
|
||||||
self.quick_performance_test = quick_performance_test
|
|
||||||
|
|
||||||
def test_telemetry_performance(self):
|
|
||||||
LOGGER.info("Starting telemetry performance test.")
|
|
||||||
try:
|
|
||||||
all_telemetries = SampleFileParser.get_all_telemetries()
|
|
||||||
except FileNotFoundError:
|
|
||||||
raise FileNotFoundError(
|
|
||||||
"Telemetries to send not found. "
|
|
||||||
"Refer to readme to figure out how to generate telemetries and where to put them."
|
|
||||||
)
|
|
||||||
LOGGER.info("Telemetries imported successfully.")
|
|
||||||
all_telemetries.sort(key=lambda telem: telem["time"]["$date"])
|
|
||||||
telemetry_parse_times = {}
|
|
||||||
for i in range(len(all_telemetries)):
|
|
||||||
telemetry_endpoint = TelemetryPerformanceTest.get_verbose_telemetry_endpoint(
|
|
||||||
all_telemetries[i]
|
|
||||||
)
|
|
||||||
telemetry_parse_times[telemetry_endpoint] = self.get_telemetry_time(all_telemetries[i])
|
|
||||||
LOGGER.info(f"Telemetry Nr.{i} sent out of {len(all_telemetries)} total.")
|
|
||||||
test_config = PerformanceTestConfig(
|
|
||||||
MAX_ALLOWED_SINGLE_TELEM_PARSE_TIME, MAX_ALLOWED_TOTAL_TIME
|
|
||||||
)
|
|
||||||
PerformanceAnalyzer(test_config, telemetry_parse_times).analyze_test_results()
|
|
||||||
if not self.quick_performance_test:
|
|
||||||
self.island_client.reset_env()
|
|
||||||
|
|
||||||
def get_telemetry_time(self, telemetry):
|
|
||||||
content = telemetry["content"]
|
|
||||||
url = telemetry["endpoint"]
|
|
||||||
method = SupportedRequestMethod.__getattr__(telemetry["method"])
|
|
||||||
|
|
||||||
return self.island_client.requests.get_request_time(url=url, method=method, data=content)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_verbose_telemetry_endpoint(telemetry):
|
|
||||||
telem_category = ""
|
|
||||||
if "telem_category" in telemetry["content"]:
|
|
||||||
telem_category = (
|
|
||||||
"_"
|
|
||||||
+ json.loads(telemetry["content"])["telem_category"]
|
|
||||||
+ "_"
|
|
||||||
+ telemetry["_id"]["$oid"]
|
|
||||||
)
|
|
||||||
return telemetry["endpoint"] + telem_category
|
|
|
@ -1,34 +0,0 @@
|
||||||
from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.endpoint_performance_test import (
|
|
||||||
EndpointPerformanceTest,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
|
|
||||||
from envs.monkey_zoo.blackbox.tests.performance.telemetry_performance_test import (
|
|
||||||
TelemetryPerformanceTest,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TelemetryPerformanceTestWorkflow(BasicTest):
|
|
||||||
def __init__(
|
|
||||||
self, name, island_client, performance_config: PerformanceTestConfig, quick_performance_test
|
|
||||||
):
|
|
||||||
self.name = name
|
|
||||||
self.island_client = island_client
|
|
||||||
self.performance_config = performance_config
|
|
||||||
self.quick_performance_test = quick_performance_test
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
try:
|
|
||||||
if not self.quick_performance_test:
|
|
||||||
telem_sending_test = TelemetryPerformanceTest(
|
|
||||||
island_client=self.island_client,
|
|
||||||
quick_performance_test=self.quick_performance_test,
|
|
||||||
)
|
|
||||||
telem_sending_test.test_telemetry_performance()
|
|
||||||
performance_test = EndpointPerformanceTest(
|
|
||||||
self.name, self.performance_config, self.island_client
|
|
||||||
)
|
|
||||||
assert performance_test.run()
|
|
||||||
finally:
|
|
||||||
if not self.quick_performance_test:
|
|
||||||
self.island_client.reset_env()
|
|
|
@ -1,59 +0,0 @@
|
||||||
import argparse
|
|
||||||
import pathlib
|
|
||||||
from typing import Type
|
|
||||||
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.depth_1_a import Depth1A
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.depth_2_a import Depth2A
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.depth_3_a import Depth3A
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.powershell_credentials_reuse import (
|
|
||||||
PowerShellCredentialsReuse,
|
|
||||||
)
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.smb_pth import SmbPth
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.wmi_mimikatz import WmiMimikatz
|
|
||||||
from envs.monkey_zoo.blackbox.config_templates.zerologon import Zerologon
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
|
|
||||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
|
|
||||||
|
|
||||||
DST_DIR_NAME = "generated_configs"
|
|
||||||
DST_DIR_PATH = pathlib.Path(pathlib.Path(__file__).parent.absolute(), DST_DIR_NAME)
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Generate config files.")
|
|
||||||
parser.add_argument(
|
|
||||||
"island_ip",
|
|
||||||
metavar="IP:PORT",
|
|
||||||
help="Island endpoint. Example: 123.123.123.123:5000",
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
island_client = MonkeyIslandClient(args.island_ip)
|
|
||||||
|
|
||||||
CONFIG_TEMPLATES = [
|
|
||||||
Depth1A,
|
|
||||||
Depth2A,
|
|
||||||
Depth3A,
|
|
||||||
Zerologon,
|
|
||||||
SmbPth,
|
|
||||||
WmiMimikatz,
|
|
||||||
PowerShellCredentialsReuse,
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def generate_templates():
|
|
||||||
for template in CONFIG_TEMPLATES:
|
|
||||||
save_template_as_config(template)
|
|
||||||
|
|
||||||
|
|
||||||
def save_template_as_config(template: Type[ConfigTemplate]):
|
|
||||||
file_path = pathlib.Path(DST_DIR_PATH, f"{template.__name__}.conf")
|
|
||||||
file_contents = IslandConfigParser.get_raw_config(template, island_client)
|
|
||||||
save_to_file(file_path, file_contents)
|
|
||||||
|
|
||||||
|
|
||||||
def save_to_file(file_path, contents):
|
|
||||||
with open(file_path, "w") as file:
|
|
||||||
file.write(contents)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
generate_templates()
|
|
|
@ -79,13 +79,23 @@ class AgentConfiguration:
|
||||||
except MarshmallowError as err:
|
except MarshmallowError as err:
|
||||||
raise InvalidConfigurationError(str(err))
|
raise InvalidConfigurationError(str(err))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_mapping(config: AgentConfiguration) -> Mapping[str, Any]:
|
||||||
|
"""
|
||||||
|
Serialize an AgentConfiguration to a Mapping
|
||||||
|
|
||||||
|
:param config: An AgentConfiguration
|
||||||
|
:return: A Mapping that represents the AgentConfiguration
|
||||||
|
"""
|
||||||
|
return AgentConfigurationSchema().dump(config)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_json(config: AgentConfiguration) -> str:
|
def to_json(config: AgentConfiguration) -> str:
|
||||||
"""
|
"""
|
||||||
Serialize an AgentConfiguration to JSON
|
Serialize an AgentConfiguration to JSON
|
||||||
|
|
||||||
:param config: An AgentConfiguration
|
:param config: An AgentConfiguration
|
||||||
:return: A JSON string representing the AgentConfiguration
|
:return: A JSON string that represents the AgentConfiguration
|
||||||
"""
|
"""
|
||||||
return AgentConfigurationSchema().dumps(config)
|
return AgentConfigurationSchema().dumps(config)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ from monkey_island.cc.resources.agent_controls import StopAgentCheck, StopAllAge
|
||||||
from monkey_island.cc.resources.attack.attack_report import AttackReport
|
from monkey_island.cc.resources.attack.attack_report import AttackReport
|
||||||
from monkey_island.cc.resources.auth.auth import Authenticate, init_jwt
|
from monkey_island.cc.resources.auth.auth import Authenticate, init_jwt
|
||||||
from monkey_island.cc.resources.auth.registration import Registration
|
from monkey_island.cc.resources.auth.registration import Registration
|
||||||
from monkey_island.cc.resources.blackbox.clear_caches import ClearCaches
|
|
||||||
from monkey_island.cc.resources.blackbox.log_blackbox_endpoint import LogBlackboxEndpoint
|
from monkey_island.cc.resources.blackbox.log_blackbox_endpoint import LogBlackboxEndpoint
|
||||||
from monkey_island.cc.resources.blackbox.monkey_blackbox_endpoint import MonkeyBlackboxEndpoint
|
from monkey_island.cc.resources.blackbox.monkey_blackbox_endpoint import MonkeyBlackboxEndpoint
|
||||||
from monkey_island.cc.resources.blackbox.telemetry_blackbox_endpoint import (
|
from monkey_island.cc.resources.blackbox.telemetry_blackbox_endpoint import (
|
||||||
|
@ -195,7 +194,6 @@ def init_restful_endpoints(api: FlaskDIWrapper):
|
||||||
# Note: Preferably, the API will provide a rich feature set and allow access to all of the
|
# Note: Preferably, the API will provide a rich feature set and allow access to all of the
|
||||||
# necessary data. This would make these endpoints obsolete.
|
# necessary data. This would make these endpoints obsolete.
|
||||||
api.add_resource(MonkeyBlackboxEndpoint)
|
api.add_resource(MonkeyBlackboxEndpoint)
|
||||||
api.add_resource(ClearCaches)
|
|
||||||
api.add_resource(LogBlackboxEndpoint)
|
api.add_resource(LogBlackboxEndpoint)
|
||||||
api.add_resource(TelemetryBlackboxEndpoint)
|
api.add_resource(TelemetryBlackboxEndpoint)
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ class AgentConfiguration(AbstractResource):
|
||||||
try:
|
try:
|
||||||
configuration_object = AgentConfigurationObject.from_mapping(request.json)
|
configuration_object = AgentConfigurationObject.from_mapping(request.json)
|
||||||
self._agent_configuration_repository.store_configuration(configuration_object)
|
self._agent_configuration_repository.store_configuration(configuration_object)
|
||||||
|
# API Spec: Should return 204 (NO CONTENT)
|
||||||
return make_response({}, 200)
|
return make_response({}, 200)
|
||||||
except (InvalidConfigurationError, json.JSONDecodeError) as err:
|
except (InvalidConfigurationError, json.JSONDecodeError) as err:
|
||||||
return make_response(
|
return make_response(
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
import logging
|
|
||||||
|
|
||||||
import flask_restful
|
|
||||||
|
|
||||||
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
|
||||||
from monkey_island.cc.resources.request_authentication import jwt_required
|
|
||||||
from monkey_island.cc.services.attack.attack_report import AttackReportService
|
|
||||||
from monkey_island.cc.services.reporting.report import ReportService
|
|
||||||
|
|
||||||
NOT_ALL_REPORTS_DELETED = "Not all reports have been cleared from the DB!"
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class ClearCaches(AbstractResource):
|
|
||||||
# API Spec: Action, not a resource; RPC-style endpoint?
|
|
||||||
urls = ["/api/test/clear-caches"]
|
|
||||||
"""
|
|
||||||
Used for timing tests - we want to get actual execution time of functions in BlackBox without
|
|
||||||
caching -
|
|
||||||
so we use this to clear the caches.
|
|
||||||
:note: DO NOT CALL THIS IN PRODUCTION CODE as this will slow down the user experience.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@jwt_required
|
|
||||||
def get(self, **kw):
|
|
||||||
try:
|
|
||||||
logger.warning("Trying to clear caches! Make sure this is not production")
|
|
||||||
ReportService.delete_saved_report_if_exists()
|
|
||||||
AttackReportService.delete_saved_report_if_exists()
|
|
||||||
# TODO: Monkey.clear_caches(), clear LRU caches of function in the Monkey object
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.exception(e)
|
|
||||||
flask_restful.abort(500, error_info=str(e))
|
|
||||||
|
|
||||||
if ReportService.is_report_generated() or AttackReportService.is_report_generated():
|
|
||||||
logger.error(NOT_ALL_REPORTS_DELETED)
|
|
||||||
flask_restful.abort(500, error_info=NOT_ALL_REPORTS_DELETED)
|
|
||||||
|
|
||||||
return {"success": "true"}
|
|
|
@ -29,7 +29,7 @@ class PropagationCredentials(AbstractResource):
|
||||||
return propagation_credentials, HTTPStatus.OK
|
return propagation_credentials, HTTPStatus.OK
|
||||||
|
|
||||||
def post(self, collection=None):
|
def post(self, collection=None):
|
||||||
credentials = [Credentials.from_json(c) for c in request.json]
|
credentials = [Credentials.from_mapping(c) for c in request.json]
|
||||||
|
|
||||||
if collection == _configured_collection:
|
if collection == _configured_collection:
|
||||||
self._credentials_repository.save_configured_credentials(credentials)
|
self._credentials_repository.save_configured_credentials(credentials)
|
||||||
|
|
|
@ -137,14 +137,6 @@ class AttackReportService:
|
||||||
generated_report = mongo.db.attack_report.find_one({})
|
generated_report = mongo.db.attack_report.find_one({})
|
||||||
return generated_report is not None
|
return generated_report is not None
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete_saved_report_if_exists():
|
|
||||||
delete_result = mongo.db.attack_report.delete_many({})
|
|
||||||
if mongo.db.attack_report.count_documents({}) != 0:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Attack Report cache not cleared. DeleteResult: " + delete_result.raw_result
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_techniques_for_report():
|
def get_techniques_for_report():
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -499,19 +499,6 @@ class ReportService:
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete_saved_report_if_exists():
|
|
||||||
"""
|
|
||||||
This function clears the saved report from the DB.
|
|
||||||
|
|
||||||
:raises RuntimeError if deletion failed
|
|
||||||
"""
|
|
||||||
delete_result = mongo.db.report.delete_many({})
|
|
||||||
if mongo.db.report.count_documents({}) != 0:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Report cache not cleared. DeleteResult: " + delete_result.raw_result
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_report():
|
def get_report():
|
||||||
if not ReportService.is_latest_report_exists():
|
if not ReportService.is_latest_report_exists():
|
||||||
|
|
|
@ -13,7 +13,12 @@ export function reformatConfig(config, reverse = false) {
|
||||||
}
|
}
|
||||||
formattedConfig['keep_tunnel_open_time'] = formattedConfig['advanced']['keep_tunnel_open_time'];
|
formattedConfig['keep_tunnel_open_time'] = formattedConfig['advanced']['keep_tunnel_open_time'];
|
||||||
} else {
|
} else {
|
||||||
formattedConfig['payloads'] = formattedConfig['payloads'][0]['options'];
|
if(formattedConfig['payloads'].length !== 0)
|
||||||
|
{
|
||||||
|
formattedConfig['payloads'] = formattedConfig['payloads'][0]['options'];
|
||||||
|
} else {
|
||||||
|
formattedConfig['payloads'] = {'encryption': {}, 'other_behaviors': {}}
|
||||||
|
}
|
||||||
formattedConfig['advanced'] = {};
|
formattedConfig['advanced'] = {};
|
||||||
formattedConfig['advanced']['keep_tunnel_open_time'] = formattedConfig['keep_tunnel_open_time'];
|
formattedConfig['advanced']['keep_tunnel_open_time'] = formattedConfig['keep_tunnel_open_time'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
from tests.common.example_agent_configuration import AGENT_CONFIGURATION
|
from common.configuration import DEFAULT_AGENT_CONFIGURATION
|
||||||
|
|
||||||
from common.configuration.agent_configuration import AgentConfiguration
|
|
||||||
from monkey_island.cc.repository import IAgentConfigurationRepository
|
from monkey_island.cc.repository import IAgentConfigurationRepository
|
||||||
|
|
||||||
|
|
||||||
class InMemoryAgentConfigurationRepository(IAgentConfigurationRepository):
|
class InMemoryAgentConfigurationRepository(IAgentConfigurationRepository):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._default_configuration = AgentConfiguration.from_mapping(AGENT_CONFIGURATION)
|
self._default_configuration = DEFAULT_AGENT_CONFIGURATION
|
||||||
self._configuration = self._default_configuration
|
self._configuration = self._default_configuration
|
||||||
|
|
||||||
def get_configuration(self):
|
def get_configuration(self):
|
||||||
|
|
|
@ -28,7 +28,6 @@ from tests.common.example_agent_configuration import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from common.configuration import AgentConfiguration, InvalidConfigurationError
|
from common.configuration import AgentConfiguration, InvalidConfigurationError
|
||||||
from common.configuration.agent_configuration import AgentConfigurationSchema
|
|
||||||
from common.configuration.agent_sub_configuration_schemas import (
|
from common.configuration.agent_sub_configuration_schemas import (
|
||||||
CustomPBAConfigurationSchema,
|
CustomPBAConfigurationSchema,
|
||||||
ExploitationConfigurationSchema,
|
ExploitationConfigurationSchema,
|
||||||
|
@ -178,16 +177,13 @@ def test_incorrect_type():
|
||||||
AgentConfiguration(**valid_config_dict)
|
AgentConfiguration(**valid_config_dict)
|
||||||
|
|
||||||
|
|
||||||
def test_from_dict():
|
def test_to_from_mapping():
|
||||||
schema = AgentConfigurationSchema()
|
config = AgentConfiguration.from_mapping(AGENT_CONFIGURATION)
|
||||||
dict_ = deepcopy(AGENT_CONFIGURATION)
|
|
||||||
|
|
||||||
config = AgentConfiguration.from_mapping(dict_)
|
assert AgentConfiguration.to_mapping(config) == AGENT_CONFIGURATION
|
||||||
|
|
||||||
assert schema.dump(config) == dict_
|
|
||||||
|
|
||||||
|
|
||||||
def test_from_dict__invalid_data():
|
def test_from_mapping__invalid_data():
|
||||||
dict_ = deepcopy(AGENT_CONFIGURATION)
|
dict_ = deepcopy(AGENT_CONFIGURATION)
|
||||||
dict_["payloads"] = "payloads"
|
dict_["payloads"] = "payloads"
|
||||||
|
|
||||||
|
@ -195,14 +191,11 @@ def test_from_dict__invalid_data():
|
||||||
AgentConfiguration.from_mapping(dict_)
|
AgentConfiguration.from_mapping(dict_)
|
||||||
|
|
||||||
|
|
||||||
def test_from_json():
|
def test_to_from_json():
|
||||||
schema = AgentConfigurationSchema()
|
original_config = AgentConfiguration.from_mapping(AGENT_CONFIGURATION)
|
||||||
dict_ = deepcopy(AGENT_CONFIGURATION)
|
config_json = AgentConfiguration.to_json(original_config)
|
||||||
|
|
||||||
config = AgentConfiguration.from_json(json.dumps(dict_))
|
assert AgentConfiguration.from_json(config_json) == original_config
|
||||||
|
|
||||||
assert isinstance(config, AgentConfiguration)
|
|
||||||
assert schema.dump(config) == dict_
|
|
||||||
|
|
||||||
|
|
||||||
def test_from_json__invalid_data():
|
def test_from_json__invalid_data():
|
||||||
|
@ -211,9 +204,3 @@ def test_from_json__invalid_data():
|
||||||
|
|
||||||
with pytest.raises(InvalidConfigurationError):
|
with pytest.raises(InvalidConfigurationError):
|
||||||
AgentConfiguration.from_json(json.dumps(invalid_dict))
|
AgentConfiguration.from_json(json.dumps(invalid_dict))
|
||||||
|
|
||||||
|
|
||||||
def test_to_json():
|
|
||||||
config = deepcopy(AGENT_CONFIGURATION)
|
|
||||||
|
|
||||||
assert json.loads(AgentConfiguration.to_json(config)) == AGENT_CONFIGURATION
|
|
||||||
|
|
|
@ -6,8 +6,13 @@ from tests.common.example_agent_configuration import AGENT_CONFIGURATION
|
||||||
from tests.monkey_island import InMemoryAgentConfigurationRepository
|
from tests.monkey_island import InMemoryAgentConfigurationRepository
|
||||||
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 common.configuration import AgentConfiguration
|
||||||
from monkey_island.cc.repository import IAgentConfigurationRepository
|
from monkey_island.cc.repository import IAgentConfigurationRepository
|
||||||
from monkey_island.cc.resources.agent_configuration import AgentConfiguration
|
from monkey_island.cc.resources.agent_configuration import (
|
||||||
|
AgentConfiguration as AgentConfigurationResource,
|
||||||
|
)
|
||||||
|
|
||||||
|
AGENT_CONFIGURATION_URL = get_url_for_resource(AgentConfigurationResource)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -21,30 +26,25 @@ def flask_client(build_flask_client):
|
||||||
|
|
||||||
|
|
||||||
def test_agent_configuration_endpoint(flask_client):
|
def test_agent_configuration_endpoint(flask_client):
|
||||||
agent_configuration_url = get_url_for_resource(AgentConfiguration)
|
resp = flask_client.post(
|
||||||
|
AGENT_CONFIGURATION_URL,
|
||||||
flask_client.post(
|
json=AgentConfiguration.to_mapping(AGENT_CONFIGURATION),
|
||||||
agent_configuration_url, data=json.dumps(AGENT_CONFIGURATION), follow_redirects=True
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
resp = flask_client.get(agent_configuration_url)
|
assert resp.status_code == 200
|
||||||
|
resp = flask_client.get(AGENT_CONFIGURATION_URL)
|
||||||
|
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert json.loads(resp.data) == AGENT_CONFIGURATION
|
assert json.loads(resp.data) == AGENT_CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
def test_agent_configuration_invalid_config(flask_client):
|
def test_agent_configuration_invalid_config(flask_client):
|
||||||
agent_configuration_url = get_url_for_resource(AgentConfiguration)
|
resp = flask_client.post(AGENT_CONFIGURATION_URL, json={"invalid_config": "invalid_stuff"})
|
||||||
|
|
||||||
resp = flask_client.post(
|
|
||||||
agent_configuration_url, data=json.dumps({"invalid_config": "invalid_stuff"})
|
|
||||||
)
|
|
||||||
|
|
||||||
assert resp.status_code == 400
|
assert resp.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
def test_agent_configuration_invalid_json(flask_client):
|
def test_agent_configuration_invalid_json(flask_client):
|
||||||
agent_configuration_url = get_url_for_resource(AgentConfiguration)
|
resp = flask_client.post(AGENT_CONFIGURATION_URL, data="InvalidJson!")
|
||||||
|
|
||||||
resp = flask_client.post(agent_configuration_url, data="InvalidJson!")
|
|
||||||
|
|
||||||
assert resp.status_code == 400
|
assert resp.status_code == 400
|
||||||
|
|
|
@ -89,8 +89,8 @@ def test_propagation_credentials_endpoint__post_stolen(flask_client, credentials
|
||||||
resp = flask_client.post(
|
resp = flask_client.post(
|
||||||
url,
|
url,
|
||||||
json=[
|
json=[
|
||||||
Credentials.to_json(LM_HASH_CREDENTIALS),
|
Credentials.to_mapping(LM_HASH_CREDENTIALS),
|
||||||
Credentials.to_json(NT_HASH_CREDENTIALS),
|
Credentials.to_mapping(NT_HASH_CREDENTIALS),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
assert resp.status_code == HTTPStatus.NO_CONTENT
|
assert resp.status_code == HTTPStatus.NO_CONTENT
|
||||||
|
@ -134,8 +134,8 @@ def test_propagation_credentials_endpoint__post_not_found(flask_client):
|
||||||
resp = flask_client.post(
|
resp = flask_client.post(
|
||||||
NON_EXISTENT_COLLECTION_URL,
|
NON_EXISTENT_COLLECTION_URL,
|
||||||
json=[
|
json=[
|
||||||
Credentials.to_json(LM_HASH_CREDENTIALS),
|
Credentials.to_mapping(LM_HASH_CREDENTIALS),
|
||||||
Credentials.to_json(NT_HASH_CREDENTIALS),
|
Credentials.to_mapping(NT_HASH_CREDENTIALS),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
assert resp.status_code == HTTPStatus.NOT_FOUND
|
assert resp.status_code == HTTPStatus.NOT_FOUND
|
||||||
|
|
Loading…
Reference in New Issue