forked from p15670423/monkey
Merge pull request #1638 from guardicore/1538-strip-credentials-from-agent-config
1538 strip credentials from agent config
This commit is contained in:
commit
2455d34c7f
|
@ -32,6 +32,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Hostname system info collector. #1535
|
- Hostname system info collector. #1535
|
||||||
- Max iterations and timeout between iterations config options. #1600
|
- Max iterations and timeout between iterations config options. #1600
|
||||||
- MITRE ATT&CK configuration screen. #1532
|
- MITRE ATT&CK configuration screen. #1532
|
||||||
|
- Propagation credentials from "GET /api/monkey/<string:guid>" endpoint. #1538
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- A bug in network map page that caused delay of telemetry log loading. #1545
|
- A bug in network map page that caused delay of telemetry log loading. #1545
|
||||||
|
|
|
@ -208,7 +208,7 @@ class ControlClient(object):
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
reply = requests.get( # noqa: DUO123
|
reply = requests.get( # noqa: DUO123
|
||||||
"https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID),
|
"https://%s/api/monkey/%s/legacy" % (WormConfiguration.current_server, GUID),
|
||||||
verify=False,
|
verify=False,
|
||||||
proxies=ControlClient.proxies,
|
proxies=ControlClient.proxies,
|
||||||
timeout=MEDIUM_REQUEST_TIMEOUT,
|
timeout=MEDIUM_REQUEST_TIMEOUT,
|
||||||
|
|
|
@ -19,37 +19,51 @@ class ControlChannel(IControlChannel):
|
||||||
self._control_channel_server = server
|
self._control_channel_server = server
|
||||||
|
|
||||||
def should_agent_stop(self) -> bool:
|
def should_agent_stop(self) -> bool:
|
||||||
if not self._control_channel_server:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get( # noqa: DUO123
|
response = requests.get( # noqa: DUO123
|
||||||
f"{self._control_channel_server}/api/monkey_control/{self._agent_id}",
|
f"{self._control_channel_server}/api/monkey_control/{self._agent_id}",
|
||||||
verify=False,
|
verify=False,
|
||||||
|
proxies=ControlClient.proxies,
|
||||||
timeout=SHORT_REQUEST_TIMEOUT,
|
timeout=SHORT_REQUEST_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
response = json.loads(response.content.decode())
|
response = json.loads(response.content.decode())
|
||||||
return response["stop_agent"]
|
return response["stop_agent"]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# TODO: Evaluate how this exception is handled; don't just log and ignore it.
|
||||||
logger.error(f"An error occurred while trying to connect to server. {e}")
|
logger.error(f"An error occurred while trying to connect to server. {e}")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def get_config(self) -> dict:
|
def get_config(self) -> dict:
|
||||||
ControlClient.load_control_config()
|
try:
|
||||||
return WormConfiguration.as_dict()
|
response = requests.get( # noqa: DUO123
|
||||||
|
"https://%s/api/monkey/%s" % (WormConfiguration.current_server, self._agent_id),
|
||||||
|
verify=False,
|
||||||
|
proxies=ControlClient.proxies,
|
||||||
|
timeout=SHORT_REQUEST_TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
return json.loads(response.content.decode())
|
||||||
|
except Exception as exc:
|
||||||
|
# TODO: Evaluate how this exception is handled; don't just log and ignore it.
|
||||||
|
logger.warning(
|
||||||
|
"Error connecting to control server %s: %s", WormConfiguration.current_server, exc
|
||||||
|
)
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_credentials_for_propagation(self) -> dict:
|
def get_credentials_for_propagation(self) -> dict:
|
||||||
if not self._control_channel_server:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get( # noqa: DUO123
|
response = requests.get( # noqa: DUO123
|
||||||
f"{self._control_channel_server}/api/propagationCredentials",
|
f"{self._control_channel_server}/api/propagationCredentials",
|
||||||
verify=False,
|
verify=False,
|
||||||
|
proxies=ControlClient.proxies,
|
||||||
timeout=SHORT_REQUEST_TIMEOUT,
|
timeout=SHORT_REQUEST_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
response = json.loads(response.content.decode())["propagation_credentials"]
|
response = json.loads(response.content.decode())["propagation_credentials"]
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# TODO: Evaluate how this exception is handled; don't just log and ignore it.
|
||||||
logger.error(f"An error occurred while trying to connect to server. {e}")
|
logger.error(f"An error occurred while trying to connect to server. {e}")
|
||||||
|
|
|
@ -30,7 +30,6 @@ from monkey_island.cc.resources.island_mode import IslandMode
|
||||||
from monkey_island.cc.resources.local_run import LocalRun
|
from monkey_island.cc.resources.local_run import LocalRun
|
||||||
from monkey_island.cc.resources.log import Log
|
from monkey_island.cc.resources.log import Log
|
||||||
from monkey_island.cc.resources.monkey import Monkey
|
from monkey_island.cc.resources.monkey import Monkey
|
||||||
from monkey_island.cc.resources.monkey_configuration import MonkeyConfiguration
|
|
||||||
from monkey_island.cc.resources.monkey_control.remote_port_check import RemotePortCheck
|
from monkey_island.cc.resources.monkey_control.remote_port_check import RemotePortCheck
|
||||||
from monkey_island.cc.resources.monkey_control.started_on_island import StartedOnIsland
|
from monkey_island.cc.resources.monkey_control.started_on_island import StartedOnIsland
|
||||||
from monkey_island.cc.resources.monkey_control.stop_agent_check import StopAgentCheck
|
from monkey_island.cc.resources.monkey_control.stop_agent_check import StopAgentCheck
|
||||||
|
@ -123,7 +122,13 @@ def init_api_resources(api):
|
||||||
api.add_resource(Root, "/api")
|
api.add_resource(Root, "/api")
|
||||||
api.add_resource(Registration, "/api/registration")
|
api.add_resource(Registration, "/api/registration")
|
||||||
api.add_resource(Authenticate, "/api/auth")
|
api.add_resource(Authenticate, "/api/auth")
|
||||||
api.add_resource(Monkey, "/api/monkey", "/api/monkey/", "/api/monkey/<string:guid>")
|
api.add_resource(
|
||||||
|
Monkey,
|
||||||
|
"/api/monkey",
|
||||||
|
"/api/monkey/",
|
||||||
|
"/api/monkey/<string:guid>",
|
||||||
|
"/api/monkey/<string:guid>/<string:config_format>",
|
||||||
|
)
|
||||||
api.add_resource(Bootloader, "/api/bootloader/<string:os>")
|
api.add_resource(Bootloader, "/api/bootloader/<string:os>")
|
||||||
api.add_resource(LocalRun, "/api/local-monkey", "/api/local-monkey/")
|
api.add_resource(LocalRun, "/api/local-monkey", "/api/local-monkey/")
|
||||||
api.add_resource(ClientRun, "/api/client-monkey", "/api/client-monkey/")
|
api.add_resource(ClientRun, "/api/client-monkey", "/api/client-monkey/")
|
||||||
|
@ -132,7 +137,6 @@ def init_api_resources(api):
|
||||||
)
|
)
|
||||||
|
|
||||||
api.add_resource(IslandMode, "/api/island-mode")
|
api.add_resource(IslandMode, "/api/island-mode")
|
||||||
api.add_resource(MonkeyConfiguration, "/api/configuration", "/api/configuration/")
|
|
||||||
api.add_resource(IslandConfiguration, "/api/configuration/island", "/api/configuration/island/")
|
api.add_resource(IslandConfiguration, "/api/configuration/island", "/api/configuration/island/")
|
||||||
api.add_resource(ConfigurationExport, "/api/configuration/export")
|
api.add_resource(ConfigurationExport, "/api/configuration/export")
|
||||||
api.add_resource(ConfigurationImport, "/api/configuration/import")
|
api.add_resource(ConfigurationImport, "/api/configuration/import")
|
||||||
|
|
|
@ -19,14 +19,20 @@ from monkey_island.cc.services.node import NodeService
|
||||||
class Monkey(flask_restful.Resource):
|
class Monkey(flask_restful.Resource):
|
||||||
|
|
||||||
# Used by monkey. can't secure.
|
# Used by monkey. can't secure.
|
||||||
def get(self, guid=None, **kw):
|
def get(self, guid=None, config_format=None, **kw):
|
||||||
NodeService.update_dead_monkeys() # refresh monkeys status
|
NodeService.update_dead_monkeys() # refresh monkeys status
|
||||||
if not guid:
|
if not guid:
|
||||||
guid = request.args.get("guid")
|
guid = request.args.get("guid")
|
||||||
|
|
||||||
if guid:
|
if guid:
|
||||||
monkey_json = mongo.db.monkey.find_one_or_404({"guid": guid})
|
monkey_json = mongo.db.monkey.find_one_or_404({"guid": guid})
|
||||||
monkey_json["config"] = ConfigService.decrypt_flat_config(monkey_json["config"])
|
# TODO: When the "legacy" format is no longer needed, update this logic and remove the
|
||||||
|
# "/api/monkey/<string:guid>/<string:config_format>" route.
|
||||||
|
if config_format == "legacy":
|
||||||
|
ConfigService.decrypt_flat_config(monkey_json["config"])
|
||||||
|
else:
|
||||||
|
ConfigService.format_flat_config_for_agent(monkey_json["config"])
|
||||||
|
|
||||||
return monkey_json
|
return monkey_json
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import json
|
|
||||||
|
|
||||||
import flask_restful
|
|
||||||
from flask import abort, jsonify, request
|
|
||||||
|
|
||||||
from monkey_island.cc.resources.auth.auth import jwt_required
|
|
||||||
from monkey_island.cc.services.config import ConfigService
|
|
||||||
|
|
||||||
|
|
||||||
class MonkeyConfiguration(flask_restful.Resource):
|
|
||||||
@jwt_required
|
|
||||||
def get(self):
|
|
||||||
return jsonify(
|
|
||||||
schema=ConfigService.get_config_schema(),
|
|
||||||
configuration=ConfigService.get_config(False, True),
|
|
||||||
)
|
|
||||||
|
|
||||||
@jwt_required
|
|
||||||
def post(self):
|
|
||||||
config_json = json.loads(request.data)
|
|
||||||
if "reset" in config_json:
|
|
||||||
ConfigService.reset_config()
|
|
||||||
else:
|
|
||||||
if not ConfigService.update_config(config_json, should_encrypt=True):
|
|
||||||
abort(400)
|
|
||||||
return self.get()
|
|
|
@ -2,6 +2,7 @@ import collections
|
||||||
import copy
|
import copy
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
from jsonschema import Draft4Validator, validators
|
from jsonschema import Draft4Validator, validators
|
||||||
|
|
||||||
|
@ -425,3 +426,20 @@ class ConfigService:
|
||||||
),
|
),
|
||||||
"exploit_ssh_keys": ConfigService.get_config_value(SSH_KEYS_PATH, should_decrypt=False),
|
"exploit_ssh_keys": ConfigService.get_config_value(SSH_KEYS_PATH, should_decrypt=False),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_flat_config_for_agent(config: Dict):
|
||||||
|
ConfigService._remove_credentials_from_flat_config(config)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _remove_credentials_from_flat_config(config: Dict):
|
||||||
|
fields_to_remove = {
|
||||||
|
"exploit_lm_hash_list",
|
||||||
|
"exploit_ntlm_hash_list",
|
||||||
|
"exploit_password_list",
|
||||||
|
"exploit_ssh_keys",
|
||||||
|
"exploit_user_list",
|
||||||
|
}
|
||||||
|
|
||||||
|
for field in fields_to_remove:
|
||||||
|
config.pop(field, None)
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
{
|
||||||
|
"HTTP_PORTS": [
|
||||||
|
80,
|
||||||
|
8080,
|
||||||
|
443,
|
||||||
|
8008,
|
||||||
|
7001,
|
||||||
|
9200
|
||||||
|
],
|
||||||
|
"PBA_linux_filename": "",
|
||||||
|
"PBA_windows_filename": "",
|
||||||
|
"alive": true,
|
||||||
|
"aws_access_key_id": "",
|
||||||
|
"aws_secret_access_key": "",
|
||||||
|
"aws_session_token": "",
|
||||||
|
"blocked_ips": [],
|
||||||
|
"command_servers": [
|
||||||
|
"10.197.94.72:5000"
|
||||||
|
],
|
||||||
|
"current_server": "10.197.94.72:5000",
|
||||||
|
"custom_PBA_linux_cmd": "",
|
||||||
|
"custom_PBA_windows_cmd": "",
|
||||||
|
"depth": 2,
|
||||||
|
"dropper_date_reference_path_linux": "/bin/sh",
|
||||||
|
"dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll",
|
||||||
|
"dropper_log_path_linux": "/tmp/user-1562",
|
||||||
|
"dropper_log_path_windows": "%temp%\\~df1562.tmp",
|
||||||
|
"dropper_set_date": true,
|
||||||
|
"dropper_target_path_linux": "/tmp/monkey",
|
||||||
|
"dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe",
|
||||||
|
"dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe",
|
||||||
|
"exploit_lm_hash_list": [],
|
||||||
|
"exploit_ntlm_hash_list": [],
|
||||||
|
"exploit_password_list": [
|
||||||
|
"root",
|
||||||
|
"123456",
|
||||||
|
"password",
|
||||||
|
"123456789",
|
||||||
|
"qwerty",
|
||||||
|
"111111",
|
||||||
|
"iloveyou"
|
||||||
|
],
|
||||||
|
"exploit_ssh_keys": [
|
||||||
|
],
|
||||||
|
"exploit_user_list": [
|
||||||
|
"Administrator",
|
||||||
|
"root",
|
||||||
|
"user",
|
||||||
|
"ubuntu"
|
||||||
|
],
|
||||||
|
"exploiter_classes": [
|
||||||
|
"SmbExploiter",
|
||||||
|
"WmiExploiter",
|
||||||
|
"SSHExploiter",
|
||||||
|
"ShellShockExploiter",
|
||||||
|
"ElasticGroovyExploiter",
|
||||||
|
"Struts2Exploiter",
|
||||||
|
"WebLogicExploiter",
|
||||||
|
"HadoopExploiter",
|
||||||
|
"MSSQLExploiter",
|
||||||
|
"DrupalExploiter",
|
||||||
|
"PowerShellExploiter"
|
||||||
|
],
|
||||||
|
"export_monkey_telems": false,
|
||||||
|
"finger_classes": [
|
||||||
|
"SMBFinger",
|
||||||
|
"SSHFinger",
|
||||||
|
"PingScanner",
|
||||||
|
"HTTPFinger",
|
||||||
|
"MySQLFinger",
|
||||||
|
"MSSQLFinger",
|
||||||
|
"ElasticFinger"
|
||||||
|
],
|
||||||
|
"inaccessible_subnets": [],
|
||||||
|
"keep_tunnel_open_time": 60,
|
||||||
|
"local_network_scan": true,
|
||||||
|
"max_depth": null,
|
||||||
|
"monkey_log_path_linux": "/tmp/user-1563",
|
||||||
|
"monkey_log_path_windows": "%temp%\\~df1563.tmp",
|
||||||
|
"ms08_067_exploit_attempts": 5,
|
||||||
|
"ping_scan_timeout": 1000,
|
||||||
|
"post_breach_actions": [
|
||||||
|
"CommunicateAsBackdoorUser",
|
||||||
|
"ModifyShellStartupFiles",
|
||||||
|
"HiddenFiles",
|
||||||
|
"TrapCommand",
|
||||||
|
"ChangeSetuidSetgid",
|
||||||
|
"ScheduleJobs",
|
||||||
|
"Timestomping",
|
||||||
|
"AccountDiscovery"
|
||||||
|
],
|
||||||
|
"ransomware": {
|
||||||
|
"encryption": {
|
||||||
|
"enabled": true,
|
||||||
|
"directories": {
|
||||||
|
"linux_target_dir": "",
|
||||||
|
"windows_target_dir": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"other_behaviors": {
|
||||||
|
"readme": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"skip_exploit_if_file_exist": false,
|
||||||
|
"smb_download_timeout": 300,
|
||||||
|
"smb_service_name": "InfectionMonkey",
|
||||||
|
"started_on_island": false,
|
||||||
|
"subnet_scan_list": [],
|
||||||
|
"system_info_collector_classes": [
|
||||||
|
"AwsCollector",
|
||||||
|
"ProcessListCollector",
|
||||||
|
"MimikatzCollector"
|
||||||
|
],
|
||||||
|
"tcp_scan_get_banner": true,
|
||||||
|
"tcp_scan_interval": 0,
|
||||||
|
"tcp_scan_timeout": 3000,
|
||||||
|
"tcp_target_ports": [
|
||||||
|
22,
|
||||||
|
2222,
|
||||||
|
445,
|
||||||
|
135,
|
||||||
|
3389,
|
||||||
|
80,
|
||||||
|
8080,
|
||||||
|
443,
|
||||||
|
8008,
|
||||||
|
3306,
|
||||||
|
7001,
|
||||||
|
8088
|
||||||
|
],
|
||||||
|
"user_to_add": "Monkey_IUSER_SUPPORT",
|
||||||
|
"victims_max_exploit": 100,
|
||||||
|
"victims_max_find": 100
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
# Without these imports pytests can't use fixtures,
|
# Without these imports pytests can't use fixtures,
|
||||||
# because they are not found
|
# because they are not found
|
||||||
import json
|
import json
|
||||||
import os
|
from typing import Dict
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from tests.unit_tests.monkey_island.cc.mongomock_fixtures import * # noqa: F401,F403,E402
|
from tests.unit_tests.monkey_island.cc.mongomock_fixtures import * # noqa: F401,F403,E402
|
||||||
from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_based_encryption import ( # noqa: E501
|
from tests.unit_tests.monkey_island.cc.server_utils.encryption.test_password_based_encryption import ( # noqa: E501
|
||||||
|
FLAT_PLAINTEXT_MONKEY_CONFIG_FILENAME,
|
||||||
MONKEY_CONFIGS_DIR_PATH,
|
MONKEY_CONFIGS_DIR_PATH,
|
||||||
STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME,
|
STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME,
|
||||||
)
|
)
|
||||||
|
@ -14,12 +15,24 @@ from monkey_island.cc.server_utils.encryption import unlock_datastore_encryptor
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def monkey_config(data_for_tests_dir):
|
def load_monkey_config(data_for_tests_dir) -> Dict:
|
||||||
plaintext_monkey_config_standard_path = os.path.join(
|
def inner(filename: str) -> Dict:
|
||||||
data_for_tests_dir, MONKEY_CONFIGS_DIR_PATH, STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME
|
config_path = (
|
||||||
|
data_for_tests_dir / MONKEY_CONFIGS_DIR_PATH / FLAT_PLAINTEXT_MONKEY_CONFIG_FILENAME
|
||||||
)
|
)
|
||||||
plaintext_config = json.loads(open(plaintext_monkey_config_standard_path, "r").read())
|
return json.loads(open(config_path, "r").read())
|
||||||
return plaintext_config
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def monkey_config(load_monkey_config):
|
||||||
|
return load_monkey_config(STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flat_monkey_config(load_monkey_config):
|
||||||
|
return load_monkey_config(FLAT_PLAINTEXT_MONKEY_CONFIG_FILENAME)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
@ -15,6 +15,7 @@ pytestmark = pytest.mark.slow
|
||||||
|
|
||||||
MONKEY_CONFIGS_DIR_PATH = "monkey_configs"
|
MONKEY_CONFIGS_DIR_PATH = "monkey_configs"
|
||||||
STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME = "monkey_config_standard.json"
|
STANDARD_PLAINTEXT_MONKEY_CONFIG_FILENAME = "monkey_config_standard.json"
|
||||||
|
FLAT_PLAINTEXT_MONKEY_CONFIG_FILENAME = "flat_config.json"
|
||||||
PASSWORD = "hello123"
|
PASSWORD = "hello123"
|
||||||
INCORRECT_PASSWORD = "goodbye321"
|
INCORRECT_PASSWORD = "goodbye321"
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,6 @@ from monkey_island.cc.services.config import ConfigService
|
||||||
# monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
|
# monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
|
||||||
|
|
||||||
|
|
||||||
class MockClass:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function", autouse=True)
|
@pytest.fixture(scope="function", autouse=True)
|
||||||
def mock_port(monkeypatch, PORT):
|
def mock_port(monkeypatch, PORT):
|
||||||
monkeypatch.setattr("monkey_island.cc.services.config.ISLAND_PORT", PORT)
|
monkeypatch.setattr("monkey_island.cc.services.config.ISLAND_PORT", PORT)
|
||||||
|
@ -27,3 +23,13 @@ def test_set_server_ips_in_config_current_server(config, IPS, PORT):
|
||||||
ConfigService.set_server_ips_in_config(config)
|
ConfigService.set_server_ips_in_config(config)
|
||||||
expected_config_current_server = f"{IPS[0]}:{PORT}"
|
expected_config_current_server = f"{IPS[0]}:{PORT}"
|
||||||
assert config["internal"]["island_server"]["current_server"] == expected_config_current_server
|
assert config["internal"]["island_server"]["current_server"] == expected_config_current_server
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_config_for_agent__credentials_removed(flat_monkey_config):
|
||||||
|
ConfigService.format_flat_config_for_agent(flat_monkey_config)
|
||||||
|
|
||||||
|
assert "exploit_lm_hash_list" not in flat_monkey_config
|
||||||
|
assert "exploit_ntlm_hash_list" not in flat_monkey_config
|
||||||
|
assert "exploit_password_list" not in flat_monkey_config
|
||||||
|
assert "exploit_ssh_keys" not in flat_monkey_config
|
||||||
|
assert "exploit_user_list" not in flat_monkey_config
|
||||||
|
|
Loading…
Reference in New Issue