forked from p15670423/monkey
Merge pull request #2032 from guardicore/1960-flat-config-changes
Change config flattening logic to return new schema
This commit is contained in:
commit
b959763318
|
@ -8,6 +8,7 @@ from typing import Any, Dict, List
|
|||
|
||||
from jsonschema import Draft4Validator, validators
|
||||
|
||||
from common import OperatingSystems
|
||||
from common.config_value_paths import (
|
||||
LM_HASH_LIST_PATH,
|
||||
NTLM_HASH_LIST_PATH,
|
||||
|
@ -357,6 +358,7 @@ class ConfigService:
|
|||
ConfigService._format_payloads_from_flat_config(config)
|
||||
ConfigService._format_pbas_from_flat_config(config)
|
||||
ConfigService._format_propagation_from_flat_config(config)
|
||||
ConfigService._format_credential_collectors(config)
|
||||
|
||||
# Ok, I'll admit this is just sort of jammed in here. But this code is going away very soon.
|
||||
del config["HTTP_PORTS"]
|
||||
|
@ -376,9 +378,18 @@ class ConfigService:
|
|||
for field in fields_to_remove:
|
||||
config.pop(field, None)
|
||||
|
||||
@staticmethod
|
||||
def _format_credential_collectors(config: Dict):
|
||||
collectors = [
|
||||
{"name": collector, "options": {}} for collector in config["credential_collectors"]
|
||||
]
|
||||
config["credential_collectors"] = collectors
|
||||
|
||||
@staticmethod
|
||||
def _format_payloads_from_flat_config(config: Dict):
|
||||
config.setdefault("payloads", {})["ransomware"] = config["ransomware"]
|
||||
config.setdefault("payloads", []).append(
|
||||
{"name": "ransomware", "options": config["ransomware"]}
|
||||
)
|
||||
config.pop("ransomware", None)
|
||||
|
||||
@staticmethod
|
||||
|
@ -388,9 +399,9 @@ class ConfigService:
|
|||
flat_windows_command_field = "custom_PBA_windows_cmd"
|
||||
flat_windows_filename_field = "PBA_windows_filename"
|
||||
|
||||
formatted_pbas_config = {}
|
||||
for pba in config.get("post_breach_actions", []):
|
||||
formatted_pbas_config[pba] = {}
|
||||
formatted_pbas_config = [
|
||||
{"name": pba, "options": {}} for pba in config.get("post_breach_actions", [])
|
||||
]
|
||||
|
||||
config["custom_pbas"] = {
|
||||
"linux_command": config.get(flat_linux_command_field, ""),
|
||||
|
@ -408,24 +419,24 @@ class ConfigService:
|
|||
|
||||
@staticmethod
|
||||
def _format_propagation_from_flat_config(config: Dict):
|
||||
formatted_propagation_config = {"network_scan": {}, "targets": {}}
|
||||
formatted_propagation_config = {"network_scan": {}, "maximum_depth": {}, "exploitation": {}}
|
||||
|
||||
formatted_propagation_config[
|
||||
"network_scan"
|
||||
] = ConfigService._format_network_scan_from_flat_config(config)
|
||||
|
||||
formatted_propagation_config["targets"] = ConfigService._format_targets_from_flat_config(
|
||||
config
|
||||
)
|
||||
formatted_propagation_config[
|
||||
"exploiters"
|
||||
"exploitation"
|
||||
] = ConfigService._format_exploiters_from_flat_config(config)
|
||||
|
||||
formatted_propagation_config["maximum_depth"] = config["depth"]
|
||||
del config["depth"]
|
||||
|
||||
config["propagation"] = formatted_propagation_config
|
||||
|
||||
@staticmethod
|
||||
def _format_network_scan_from_flat_config(config: Dict) -> Dict[str, Any]:
|
||||
formatted_network_scan_config = {"tcp": {}, "icmp": {}, "fingerprinters": []}
|
||||
formatted_network_scan_config = {"tcp": {}, "icmp": {}, "fingerprinters": [], "targets": {}}
|
||||
|
||||
formatted_network_scan_config["tcp"] = ConfigService._format_tcp_scan_from_flat_config(
|
||||
config
|
||||
|
@ -437,6 +448,10 @@ class ConfigService:
|
|||
"fingerprinters"
|
||||
] = ConfigService._format_fingerprinters_from_flat_config(config)
|
||||
|
||||
formatted_network_scan_config["targets"] = ConfigService._format_targets_from_flat_config(
|
||||
config
|
||||
)
|
||||
|
||||
return formatted_network_scan_config
|
||||
|
||||
@staticmethod
|
||||
|
@ -447,7 +462,7 @@ class ConfigService:
|
|||
|
||||
formatted_tcp_scan_config = {}
|
||||
|
||||
formatted_tcp_scan_config["timeout_ms"] = config[flat_tcp_timeout_field]
|
||||
formatted_tcp_scan_config["timeout"] = config[flat_tcp_timeout_field]
|
||||
|
||||
ports = ConfigService._union_tcp_and_http_ports(
|
||||
config[flat_tcp_ports_field], config[flat_http_ports_field]
|
||||
|
@ -471,7 +486,7 @@ class ConfigService:
|
|||
flat_ping_timeout_field = "ping_scan_timeout"
|
||||
|
||||
formatted_icmp_scan_config = {}
|
||||
formatted_icmp_scan_config["timeout_ms"] = config[flat_ping_timeout_field]
|
||||
formatted_icmp_scan_config["timeout"] = config[flat_ping_timeout_field]
|
||||
|
||||
config.pop(flat_ping_timeout_field, None)
|
||||
|
||||
|
@ -519,9 +534,7 @@ class ConfigService:
|
|||
formatted_scan_targets_config[flat_local_network_scan_field] = config[
|
||||
flat_local_network_scan_field
|
||||
]
|
||||
formatted_scan_targets_config[flat_subnet_scan_list_field] = config[
|
||||
flat_subnet_scan_list_field
|
||||
]
|
||||
formatted_scan_targets_config["subnets"] = config[flat_subnet_scan_list_field]
|
||||
|
||||
config.pop(flat_blocked_ips_field, None)
|
||||
config.pop(flat_inaccessible_subnets_field, None)
|
||||
|
@ -586,14 +599,14 @@ class ConfigService:
|
|||
formatted_config: Dict,
|
||||
) -> Dict[str, List[Dict[str, Any]]]:
|
||||
supported_os = {
|
||||
"HadoopExploiter": ["linux", "windows"],
|
||||
"Log4ShellExploiter": ["linux", "windows"],
|
||||
"MSSQLExploiter": ["windows"],
|
||||
"PowerShellExploiter": ["windows"],
|
||||
"SSHExploiter": ["linux"],
|
||||
"SmbExploiter": ["windows"],
|
||||
"WmiExploiter": ["windows"],
|
||||
"ZerologonExploiter": ["windows"],
|
||||
"HadoopExploiter": [OperatingSystems.LINUX, OperatingSystems.WINDOWS],
|
||||
"Log4ShellExploiter": [OperatingSystems.LINUX, OperatingSystems.WINDOWS],
|
||||
"MSSQLExploiter": [OperatingSystems.WINDOWS],
|
||||
"PowerShellExploiter": [OperatingSystems.WINDOWS],
|
||||
"SSHExploiter": [OperatingSystems.LINUX],
|
||||
"SmbExploiter": [OperatingSystems.WINDOWS],
|
||||
"WmiExploiter": [OperatingSystems.WINDOWS],
|
||||
"ZerologonExploiter": [OperatingSystems.WINDOWS],
|
||||
}
|
||||
new_config = copy.deepcopy(formatted_config)
|
||||
for exploiter in chain(new_config["brute_force"], new_config["vulnerability"]):
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
import bson
|
||||
from bson.json_util import dumps
|
||||
|
@ -11,19 +12,27 @@ def normalize_obj(obj):
|
|||
del obj["_id"]
|
||||
|
||||
for key, value in list(obj.items()):
|
||||
if isinstance(value, bson.objectid.ObjectId):
|
||||
obj[key] = str(value)
|
||||
if isinstance(value, datetime):
|
||||
obj[key] = str(value)
|
||||
if isinstance(value, dict):
|
||||
obj[key] = normalize_obj(value)
|
||||
if isinstance(value, list):
|
||||
for i in range(0, len(value)):
|
||||
if isinstance(value[i], dict):
|
||||
value[i] = normalize_obj(value[i])
|
||||
obj[key][i] = _normalize_value(value[i])
|
||||
else:
|
||||
obj[key] = _normalize_value(value)
|
||||
return obj
|
||||
|
||||
|
||||
def _normalize_value(value):
|
||||
if type(value) == dict:
|
||||
return normalize_obj(value)
|
||||
if isinstance(value, bson.objectid.ObjectId):
|
||||
return str(value)
|
||||
if isinstance(value, datetime):
|
||||
return str(value)
|
||||
if issubclass(type(value), Enum):
|
||||
return value.name
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def output_json(obj, code, headers=None):
|
||||
obj = normalize_obj(obj)
|
||||
resp = make_response(dumps(obj), code)
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
],
|
||||
"PBA_linux_filename": "test.sh",
|
||||
"PBA_windows_filename": "test.ps1",
|
||||
"alive": true,
|
||||
"blocked_ips": ["192.168.1.1", "192.168.1.100"],
|
||||
"custom_PBA_linux_cmd": "bash test.sh",
|
||||
"custom_PBA_windows_cmd": "powershell test.ps1",
|
||||
|
@ -27,6 +26,7 @@
|
|||
"private_key": "my_private_key"
|
||||
}
|
||||
],
|
||||
"credential_collectors": ["MimikatzCollector", "SSHCollector"],
|
||||
"exploit_user_list": [
|
||||
"Administrator",
|
||||
"root",
|
||||
|
@ -53,7 +53,6 @@
|
|||
"inaccessible_subnets": ["10.0.0.0/24", "10.0.10.0/24"],
|
||||
"keep_tunnel_open_time": 60,
|
||||
"local_network_scan": true,
|
||||
"max_depth": null,
|
||||
"ping_scan_timeout": 1000,
|
||||
"post_breach_actions": [
|
||||
"CommunicateAsBackdoorUser",
|
||||
|
@ -75,9 +74,6 @@
|
|||
}
|
||||
},
|
||||
"subnet_scan_list": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
|
||||
"system_info_collector_classes": [
|
||||
"MimikatzCollector"
|
||||
],
|
||||
"tcp_scan_timeout": 3000,
|
||||
"tcp_target_ports": [
|
||||
22,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import pytest
|
||||
|
||||
from common import OperatingSystems
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
|
||||
# If tests fail because config path is changed, sync with
|
||||
|
@ -25,34 +26,33 @@ def test_format_config_for_agent__credentials_removed():
|
|||
|
||||
def test_format_config_for_agent__ransomware_payload():
|
||||
expected_ransomware_options = {
|
||||
"ransomware": {
|
||||
"encryption": {
|
||||
"enabled": True,
|
||||
"directories": {
|
||||
"linux_target_dir": "/tmp/ransomware-target",
|
||||
"windows_target_dir": "C:\\windows\\temp\\ransomware-target",
|
||||
},
|
||||
"encryption": {
|
||||
"enabled": True,
|
||||
"directories": {
|
||||
"linux_target_dir": "/tmp/ransomware-target",
|
||||
"windows_target_dir": "C:\\windows\\temp\\ransomware-target",
|
||||
},
|
||||
"other_behaviors": {"readme": True},
|
||||
}
|
||||
},
|
||||
"other_behaviors": {"readme": True},
|
||||
}
|
||||
|
||||
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
||||
|
||||
assert "payloads" in flat_monkey_config
|
||||
assert flat_monkey_config["payloads"] == expected_ransomware_options
|
||||
assert flat_monkey_config["payloads"][0]["name"] == "ransomware"
|
||||
assert flat_monkey_config["payloads"][0]["options"] == expected_ransomware_options
|
||||
|
||||
assert "ransomware" not in flat_monkey_config
|
||||
|
||||
|
||||
def test_format_config_for_agent__pbas():
|
||||
expected_pbas_config = {
|
||||
"CommunicateAsBackdoorUser": {},
|
||||
"ModifyShellStartupFiles": {},
|
||||
"ScheduleJobs": {},
|
||||
"Timestomping": {},
|
||||
"AccountDiscovery": {},
|
||||
}
|
||||
expected_pbas_config = [
|
||||
{"name": "CommunicateAsBackdoorUser", "options": {}},
|
||||
{"name": "ModifyShellStartupFiles", "options": {}},
|
||||
{"name": "ScheduleJobs", "options": {}},
|
||||
{"name": "Timestomping", "options": {}},
|
||||
{"name": "AccountDiscovery", "options": {}},
|
||||
]
|
||||
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
||||
|
||||
assert "post_breach_actions" in flat_monkey_config
|
||||
|
@ -93,32 +93,14 @@ def test_format_config_for_agent__propagation():
|
|||
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
||||
|
||||
assert "propagation" in flat_monkey_config
|
||||
assert "targets" in flat_monkey_config["propagation"]
|
||||
assert "network_scan" in flat_monkey_config["propagation"]
|
||||
assert "exploiters" in flat_monkey_config["propagation"]
|
||||
|
||||
|
||||
def test_format_config_for_agent__propagation_targets():
|
||||
expected_targets = {
|
||||
"blocked_ips": ["192.168.1.1", "192.168.1.100"],
|
||||
"inaccessible_subnets": ["10.0.0.0/24", "10.0.10.0/24"],
|
||||
"local_network_scan": True,
|
||||
"subnet_scan_list": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
|
||||
}
|
||||
|
||||
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
||||
|
||||
assert flat_monkey_config["propagation"]["targets"] == expected_targets
|
||||
assert "blocked_ips" not in flat_monkey_config
|
||||
assert "inaccessible_subnets" not in flat_monkey_config
|
||||
assert "local_network_scan" not in flat_monkey_config
|
||||
assert "subnet_scan_list" not in flat_monkey_config
|
||||
assert "exploitation" in flat_monkey_config["propagation"]
|
||||
|
||||
|
||||
def test_format_config_for_agent__network_scan():
|
||||
expected_network_scan_config = {
|
||||
"tcp": {
|
||||
"timeout_ms": 3000,
|
||||
"timeout": 3000,
|
||||
"ports": [
|
||||
22,
|
||||
80,
|
||||
|
@ -136,7 +118,13 @@ def test_format_config_for_agent__network_scan():
|
|||
],
|
||||
},
|
||||
"icmp": {
|
||||
"timeout_ms": 1000,
|
||||
"timeout": 1000,
|
||||
},
|
||||
"targets": {
|
||||
"blocked_ips": ["192.168.1.1", "192.168.1.100"],
|
||||
"inaccessible_subnets": ["10.0.0.0/24", "10.0.10.0/24"],
|
||||
"local_network_scan": True,
|
||||
"subnets": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
|
||||
},
|
||||
"fingerprinters": [
|
||||
{"name": "elastic", "options": {}},
|
||||
|
@ -161,36 +149,69 @@ def test_format_config_for_agent__network_scan():
|
|||
assert "finger_classes" not in flat_monkey_config
|
||||
|
||||
|
||||
def test_format_config_for_agent__propagation_network_scan_targets():
|
||||
expected_targets = {
|
||||
"blocked_ips": ["192.168.1.1", "192.168.1.100"],
|
||||
"inaccessible_subnets": ["10.0.0.0/24", "10.0.10.0/24"],
|
||||
"local_network_scan": True,
|
||||
"subnets": ["192.168.1.50", "192.168.56.0/24", "10.0.33.0/30"],
|
||||
}
|
||||
|
||||
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
||||
|
||||
assert flat_monkey_config["propagation"]["network_scan"]["targets"] == expected_targets
|
||||
assert "blocked_ips" not in flat_monkey_config
|
||||
assert "inaccessible_subnets" not in flat_monkey_config
|
||||
assert "local_network_scan" not in flat_monkey_config
|
||||
assert "subnet_scan_list" not in flat_monkey_config
|
||||
|
||||
|
||||
def test_format_config_for_agent__exploiters():
|
||||
expected_exploiters_config = {
|
||||
"options": {
|
||||
"http_ports": [80, 443, 7001, 8008, 8080, 9200],
|
||||
},
|
||||
"brute_force": [
|
||||
{"name": "MSSQLExploiter", "supported_os": ["windows"], "options": {}},
|
||||
{"name": "PowerShellExploiter", "supported_os": ["windows"], "options": {}},
|
||||
{"name": "SSHExploiter", "supported_os": ["linux"], "options": {}},
|
||||
{"name": "MSSQLExploiter", "supported_os": [OperatingSystems.WINDOWS], "options": {}},
|
||||
{
|
||||
"name": "PowerShellExploiter",
|
||||
"supported_os": [OperatingSystems.WINDOWS],
|
||||
"options": {},
|
||||
},
|
||||
{"name": "SSHExploiter", "supported_os": [OperatingSystems.LINUX], "options": {}},
|
||||
{
|
||||
"name": "SmbExploiter",
|
||||
"supported_os": ["windows"],
|
||||
"supported_os": [OperatingSystems.WINDOWS],
|
||||
"options": {"smb_download_timeout": 30},
|
||||
},
|
||||
{
|
||||
"name": "WmiExploiter",
|
||||
"supported_os": ["windows"],
|
||||
"supported_os": [OperatingSystems.WINDOWS],
|
||||
"options": {"smb_download_timeout": 30},
|
||||
},
|
||||
],
|
||||
"vulnerability": [
|
||||
{"name": "HadoopExploiter", "supported_os": ["linux", "windows"], "options": {}},
|
||||
{"name": "Log4ShellExploiter", "supported_os": ["linux", "windows"], "options": {}},
|
||||
{"name": "ZerologonExploiter", "supported_os": ["windows"], "options": {}},
|
||||
{
|
||||
"name": "HadoopExploiter",
|
||||
"supported_os": [OperatingSystems.LINUX, OperatingSystems.WINDOWS],
|
||||
"options": {},
|
||||
},
|
||||
{
|
||||
"name": "Log4ShellExploiter",
|
||||
"supported_os": [OperatingSystems.LINUX, OperatingSystems.WINDOWS],
|
||||
"options": {},
|
||||
},
|
||||
{
|
||||
"name": "ZerologonExploiter",
|
||||
"supported_os": [OperatingSystems.WINDOWS],
|
||||
"options": {},
|
||||
},
|
||||
],
|
||||
}
|
||||
flat_monkey_config = ConfigService.format_flat_config_for_agent()
|
||||
|
||||
assert "propagation" in flat_monkey_config
|
||||
assert "exploiters" in flat_monkey_config["propagation"]
|
||||
assert "exploitation" in flat_monkey_config["propagation"]
|
||||
|
||||
assert flat_monkey_config["propagation"]["exploiters"] == expected_exploiters_config
|
||||
assert flat_monkey_config["propagation"]["exploitation"] == expected_exploiters_config
|
||||
assert "exploiter_classes" not in flat_monkey_config
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from unittest import TestCase
|
||||
|
||||
import bson
|
||||
|
@ -44,3 +45,11 @@ class TestRepresentations(TestCase):
|
|||
}
|
||||
),
|
||||
)
|
||||
|
||||
def test_normalize__enum(self):
|
||||
class BogusEnum(Enum):
|
||||
bogus_val = "Bogus"
|
||||
|
||||
my_obj = {"something": "something", "my_enum": BogusEnum.bogus_val}
|
||||
|
||||
assert {"something": "something", "my_enum": "bogus_val"} == normalize_obj(my_obj)
|
||||
|
|
Loading…
Reference in New Issue