Merge pull request #2056 from guardicore/2003-remove-old-schema

Remove old config schema
This commit is contained in:
Mike Salvatore 2022-07-06 11:03:58 -04:00 committed by GitHub
commit 833513e383
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 33 additions and 1246 deletions

View File

@ -1,52 +0,0 @@
{
"id": "JFXftJml8DpmuCPBA9rL",
"name": "Add details about your new PBA",
"task": {
"dod": "You should add your new PBA's details to the configuration.",
"tests": [],
"hints": [
"Have a look at the details of the other techniques."
]
},
"content": [
{
"type": "text",
"text": "In order to make sure that the new `ScheduleJobs` PBA is shown in the configuration on the Monkey Island, you need to add its details to the configuration file(s). <br><br>\n\nSince this particular PBA is related to the MITRE techniques [T1168](https://attack.mitre.org/techniques/T1168) and [T1053](https://attack.mitre.org/techniques/T1053), make sure to link the PBA with these techniques in the configuration as well. <br><br>\n\nEach part of the configuration has an important role \n- *enum* — contains the relevant PBA's class name(s)\n- *title* — holds the name of the PBA which is displayed in the configuration on the Monkey Island\n- *info* — consists of an elaboration on the PBA's working which is displayed in the configuration on the Monkey Island\n- *attack_techniques* — has the IDs of the MITRE techniques associated with the PBA\n\n## Manual test \nOnce you think you're done...\n- Run the Monkey Island\n- You should be able to see your new PBA under the \"Monkey\" tab in the configuration, along with its information when you click on it\n\n<img src=\"https://i.imgur.com/a5VSkL5.gif\" height=400>"
},
{
"type": "snippet",
"lines": [
" \"Removes the file afterwards.\",",
" \"attack_techniques\": [\"T1166\"],",
" },",
"* {",
"+ # Swimmer: ADD DETAILS HERE!",
"* \"type\": \"string\",",
"* \"enum\": [\"ScheduleJobs\"],",
"* \"title\": \"Job Scheduling\",",
"* \"safe\": True,",
"* \"info\": \"Attempts to create a scheduled job on the system and remove it.\",",
"* \"attack_techniques\": [\"T1168\", \"T1053\"],",
"* },",
" {",
" \"type\": \"string\",",
" \"enum\": [\"Timestomping\"],"
],
"firstLineNumber": 52,
"path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"comments": []
},
{
"type": "text",
"text": "- The PBA details in this file are reflected on the Monkey Island in the PBA configuration.\n- PBAs are also linked to the relevant MITRE techniques in this file, whose results can then be seen in the MITRE ATT&CK report on the Monkey Island."
}
],
"symbols": {},
"file_version": "2.0.3",
"meta": {
"app_version": "0.5.7-0",
"file_blobs": {
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "7d62ac36e875ca3c249d808250cb3268e4d3d68d"
}
}
}

View File

@ -1,90 +0,0 @@
{
"id": "afMu3y3ny5lnrYFWl3EI",
"name": "Add a new Post Breach Action (PBA)",
"task": {
"dod": "You should add a new PBA to the Monkey which discovers all user accounts on the machine.",
"tests": [],
"hints": [
"See `ScheduleJobs` PBA for an example of a PBA which only uses shell commands.",
"Make sure to add the PBA to the configuration as well.",
"MITRE ATT&CK technique T1087 articulates that adversaries may attempt to get a listing of accounts on a system or within an environment which can help them determine which accounts can aid in follow-on behavior. Therefore, the AccountDiscovery PBA is relevant to it which will enable the ATT&CK technique and show it in ATT&CK report."
]
},
"content": [
{
"type": "text",
"text": "Read our [documentation](https://www.guardicore.com/infectionmonkey/docs/development/adding-post-breach-actions/) about adding a new PBA.\n\nAfter that we want you to add the AccountDiscovery PBA. The commands that add users for Windows and Linux can be retrieved from \\`get\\_commands\\_to\\_discover\\_accounts\\` — make sure you see how to use this function correctly.\n\nNote that the PBA should impact the T1087 MITRE technique as well.\n\n**Manual test to confirm**\n--------------------------\n\n1. Run the Monkey Island.\n \n2. Make sure your new PBA is enabled by default in the config. For this test, disable network scanning, exploiting, and all other PBAs.\n \n3. Run the Monkey Agent.\n \n4. See the PBA in the security report and in the MITRE report under the relevant technique."
},
{
"type": "snippet",
"lines": [
" POST_BREACH_JOB_SCHEDULING = \"Schedule jobs\"",
" POST_BREACH_TIMESTOMPING = \"Modify files' timestamps\"",
" POST_BREACH_SIGNED_SCRIPT_PROXY_EXEC = \"Signed script proxy execution\"",
"*POST_BREACH_ACCOUNT_DISCOVERY = \"Account discovery\"",
"+# SWIMMER: Put the new const here!",
" POST_BREACH_CLEAR_CMD_HISTORY = \"Clear command history\""
],
"firstLineNumber": 7,
"path": "monkey/common/common_consts/post_breach_consts.py",
"comments": []
},
{
"type": "snippet",
"lines": [
" ",
" class AccountDiscovery(PBA):",
" def __init__(self, telemetry_messenger: ITelemetryMessenger):",
"* linux_cmds, windows_cmds = get_commands_to_discover_accounts()",
"+ # SWIMMER: Implement here!",
"* super().__init__(",
"+ pass",
"* telemetry_messenger,",
"* POST_BREACH_ACCOUNT_DISCOVERY,",
"* linux_cmd=\" \".join(linux_cmds),",
"* windows_cmd=windows_cmds,",
"* )"
],
"firstLineNumber": 8,
"path": "monkey/infection_monkey/post_breach/actions/discover_accounts.py",
"comments": []
},
{
"type": "snippet",
"lines": [
" \"with the help of a pre-existing signed script.\",",
" \"attack_techniques\": [\"T1216\"],",
" },",
"* {",
"+ # SWIMMER: Add details here!",
"* \"type\": \"string\",",
"* \"enum\": [\"AccountDiscovery\"],",
"* \"title\": \"Account Discovery\",",
"* \"safe\": True,",
"* \"info\": \"Attempts to get a listing of user accounts on the system.\",",
"* \"attack_techniques\": [\"T1087\"],",
"* },",
" {",
" \"type\": \"string\",",
" \"enum\": [\"ClearCommandHistory\"],"
],
"firstLineNumber": 78,
"path": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"comments": []
},
{
"type": "text",
"text": "Many PBAs use shell commands or scripts — see `Timestomping` and `AccountDiscovery`.\n\nOn the other hand, some are less straightforward. You can override functions and implement new classes depending on what is required, to implement complicated PBAs — see `SignedScriptProxyExecution` and `ModifyShellStartupFiles`. \n \n\nThis PBA, along with the others, will run on a system after it has been breached. The purpose of this code is to test whether target systems allow attackers to gather details about all the user accounts that are present on a system or in an environment."
}
],
"symbols": {},
"file_version": "2.0.3",
"meta": {
"app_version": "0.6.6-2",
"file_blobs": {
"monkey/common/common_consts/post_breach_consts.py": "19b6c4f19b7223f115976a0050ca04ab97e52f8e",
"monkey/infection_monkey/post_breach/actions/discover_accounts.py": "a153cf5b6185c9771414fc5ae49d441efc7294b6",
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": "d6831ed63b17f327d719a05840d7e51202fa5ccb"
}
}
}

View File

@ -1,2 +0,0 @@
MIMIKATZ_COLLECTOR = "MimikatzCollector"
SSH_COLLECTOR = "SSHCollector"

View File

@ -1,5 +0,0 @@
# Defined in UI on ValidationFormats.js
IP_RANGE = "ip-range"
IP = "ip"
VALID_RANSOMWARE_TARGET_PATH_LINUX = "valid-ransomware-target-path-linux"
VALID_RANSOMWARE_TARGET_PATH_WINDOWS = "valid-ransomware-target-path-windows"

View File

@ -5,6 +5,7 @@ class Config(EmbeddedDocument):
COLLECTION_NAME = "config"
# TODO: Fix this comment
"""
No need to define this schema here. It will change often and is already is defined in
monkey_island.cc.services.config_schema.

View File

@ -24,7 +24,7 @@ class IslandConfiguration(AbstractResource):
# API Spec: Makes more sense to have a PATCH request for this since the resource,
# i.e. the configuration, is being updated.
if "reset" in config_json:
ConfigService.reset_config()
pass
else:
if not ConfigService.update_config(config_json, should_encrypt=True):
abort(400)

View File

@ -6,20 +6,21 @@ from common.utils.attack_utils import ScanStatus
from common.utils.code_utils import abstractstatic
from monkey_island.cc.database import mongo
from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations
from monkey_island.cc.services.config_schema.config_schema import SCHEMA
from monkey_island.cc.services.attack.attack_schema import SCHEMA as ATTACK_SCHEMA
from monkey_island.cc.services.config_schema.config_schema_per_attack_technique import (
ConfigSchemaPerAttackTechnique,
)
logger = logging.getLogger(__name__)
UNSCANNED_MESSAGE = (
"The configuration options corresponding to this ATT&CK technique were not "
"enabled in the configuration."
)
class AttackTechnique(object, metaclass=abc.ABCMeta):
"""Abstract class for ATT&CK report components"""
config_schema_per_attack_technique = None
@property
@abc.abstractmethod
def unscanned_msg(self):
@ -120,52 +121,13 @@ class AttackTechnique(object, metaclass=abc.ABCMeta):
:return: message string
"""
if status == ScanStatus.UNSCANNED.value:
if not cls.config_schema_per_attack_technique:
cls.config_schema_per_attack_technique = (
ConfigSchemaPerAttackTechnique().get_config_schema_per_attack_technique(SCHEMA)
)
unscanned_msg = cls._get_unscanned_msg_with_reasons(
cls.unscanned_msg, cls.config_schema_per_attack_technique
)
unscanned_msg = UNSCANNED_MESSAGE
return unscanned_msg
elif status == ScanStatus.SCANNED.value:
return cls.scanned_msg
else:
return cls.used_msg
@classmethod
def _get_unscanned_msg_with_reasons(
cls, unscanned_msg: str, config_schema_per_attack_technique: Dict
):
reasons = []
if len(cls.relevant_systems) == 1:
reasons.append(f"- Monkey did not run on any {cls.relevant_systems[0]} systems.")
if cls.tech_id in config_schema_per_attack_technique:
reasons.append(
"- The following configuration options were disabled or empty:<br/>"
f"{cls._get_relevant_config_values(config_schema_per_attack_technique)}"
)
if reasons:
unscanned_msg = (
unscanned_msg.strip(".")
+ " due to one of the following reasons:\n"
+ "\n".join(reasons)
)
return unscanned_msg
@classmethod
def _get_relevant_config_values(cls, config_schema_per_attack_technique: Dict):
config_options = ""
for config_type in config_schema_per_attack_technique[cls.tech_id]:
config_options += (
f"- {config_type}"
f"{', '.join(config_schema_per_attack_technique[cls.tech_id][config_type])}<br/>"
)
return config_options
@classmethod
def technique_title(cls):
"""

View File

@ -1,11 +1,8 @@
import collections
import copy
import functools
import logging
from typing import Dict, List
from jsonschema import Draft4Validator, validators
from common.config_value_paths import (
LM_HASH_LIST_PATH,
NTLM_HASH_LIST_PATH,
@ -23,8 +20,6 @@ from monkey_island.cc.server_utils.encryption import (
encrypt_dict,
get_datastore_encryptor,
)
from monkey_island.cc.services.config_schema.config_schema import SCHEMA
from monkey_island.cc.services.post_breach_files import PostBreachFilesService
logger = logging.getLogger(__name__)
@ -43,8 +38,6 @@ SENSITIVE_SSH_KEY_FIELDS = [
class ConfigService:
default_config = None
def __init__(self):
pass
@ -124,10 +117,6 @@ class ConfigService:
return flat_config_json
@staticmethod
def get_config_schema():
return SCHEMA
# Not added to interface because it's doable by get_config_field + set_config_field
@staticmethod
def add_item_to_config_set_if_dont_exist(item_path_array, item_value, should_encrypt):
@ -216,76 +205,10 @@ class ConfigService:
ConfigService.set_config_value(PBA_LINUX_FILENAME_PATH, linux_filename)
ConfigService.set_config_value(PBA_WINDOWS_FILENAME_PATH, windows_filename)
@staticmethod
def init_default_config():
if ConfigService.default_config is None:
default_validating_draft4_validator = ConfigService._extend_config_with_default(
Draft4Validator
)
config = {}
default_validating_draft4_validator(SCHEMA).validate(config)
ConfigService.default_config = config
@staticmethod
def get_default_config(should_encrypt=False):
ConfigService.init_default_config()
config = copy.deepcopy(ConfigService.default_config)
if should_encrypt:
ConfigService.encrypt_config(config)
logger.info("Default config was called")
return config
@staticmethod
def init_config():
if ConfigService.get_config(should_decrypt=False) != {}:
return
ConfigService.reset_config()
@staticmethod
def reset_config():
PostBreachFilesService.remove_PBA_files()
logger.info("Monkey config reset was called")
@staticmethod
def _extend_config_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
def set_defaults(validator, properties, instance, schema):
# Do it only for root.
if instance != {}:
return
for property1, subschema1 in list(properties.items()):
main_dict = {}
for property2, subschema2 in list(subschema1["properties"].items()):
sub_dict = {}
for property3, subschema3 in list(subschema2["properties"].items()):
if "default" in subschema3:
sub_dict[property3] = subschema3["default"]
elif "properties" in subschema3:
layer_3_dict = {}
for property4, subschema4 in list(subschema3["properties"].items()):
if "properties" in subschema4:
raise ValueError(
"monkey/monkey_island/cc/services/config.py "
"can't handle 5 level config. "
"Either change back the config or refactor."
)
if "default" in subschema4:
layer_3_dict[property4] = subschema4["default"]
sub_dict[property3] = layer_3_dict
main_dict[property2] = sub_dict
instance.setdefault(property1, main_dict)
for error in validate_properties(validator, properties, instance, schema):
yield error
return validators.extend(
validator_class,
{"properties": set_defaults},
)
@staticmethod
def decrypt_config(config):

View File

@ -1,65 +0,0 @@
BASIC = {
"title": "Exploits",
"type": "object",
"primary": True,
"properties": {
"exploiters": {
"title": "Exploiters",
"type": "object",
"description": "Choose which exploiters the Monkey will attempt.",
"properties": {
"exploiter_classes": {
"title": "Exploiters",
"type": "array",
"uniqueItems": True,
"items": {"$ref": "#/definitions/exploiter_classes"},
"default": [
"SmbExploiter",
"WmiExploiter",
"SSHExploiter",
"Log4ShellExploiter",
"HadoopExploiter",
"MSSQLExploiter",
"PowerShellExploiter",
],
}
},
},
"credentials": {
"title": "Credentials",
"type": "object",
"properties": {
"exploit_user_list": {
"title": "Exploit user list",
"type": "array",
"uniqueItems": True,
"items": {"type": "string"},
"default": ["Administrator", "root", "user"],
"description": "List of user names that will be used by exploiters that need "
"credentials, like "
"SSH brute-forcing.",
},
"exploit_password_list": {
"title": "Exploit password list",
"type": "array",
"uniqueItems": True,
"items": {
"type": "string",
},
"default": [
"root",
"123456",
"password",
"123456789",
"qwerty",
"111111",
"iloveyou",
],
"description": "List of passwords that will be used by exploiters that need "
"credentials, like "
"SSH brute-forcing.",
},
},
},
},
}

View File

@ -1,96 +0,0 @@
from common.common_consts.validation_formats import IP, IP_RANGE
from monkey_island.cc.services.utils.typographic_symbols import WARNING_SIGN
BASIC_NETWORK = {
"title": "Network",
"type": "object",
"properties": {
"scope": {
"title": "Scope",
"type": "object",
"properties": {
"info_box": {
"info": 'The Monkey scans its subnet if "Local network scan" is checked. '
'Additionally, the Monkey scans machines according to "Scan '
'target list".',
},
"blocked_ips": {
"title": "Blocked IPs",
"type": "array",
"uniqueItems": True,
"items": {
"type": "string",
"format": IP,
},
"default": [],
"description": "List of IPs that the Monkey will not scan.",
},
"local_network_scan": {
"title": "Local network scan",
"type": "boolean",
"default": True,
"description": "Determines whether the Monkey will scan the local subnets of "
"machines it runs on, "
"in addition to the IPs that are configured manually in the "
'"Scan target list".',
},
"depth": {
"title": "Scan depth",
"type": "integer",
"minimum": 1,
"default": 2,
"description": "Amount of hops allowed for the Monkey to spread from the "
"Island server. \n"
+ WARNING_SIGN
+ " Note that setting this value too high may result in the "
"Monkey propagating too far, "
'if the "Local network scan" is enabled.',
},
"subnet_scan_list": {
"title": "Scan target list",
"type": "array",
"uniqueItems": True,
"items": {"type": "string", "format": IP_RANGE},
"default": [],
"description": "List of targets the Monkey will try to scan. Targets can be "
"IPs, subnets or hosts."
" Examples:\n"
'\tTarget a specific IP: "192.168.0.1"\n'
"\tTarget a subnet using a network range: "
'"192.168.0.5-192.168.0.20"\n'
'\tTarget a subnet using an IP mask: "192.168.0.5/24"\n'
'\tTarget a specific host: "printer.example"',
},
},
},
"network_analysis": {
"title": "Network Analysis",
"type": "object",
"properties": {
"inaccessible_subnets": {
"title": "Network segmentation testing",
"type": "array",
"uniqueItems": True,
"items": {"type": "string", "format": IP_RANGE},
"default": [],
"description": "Test for network segmentation by providing a list of network "
"segments "
"that should NOT be accessible to each other.\n\n"
"For example, if you configured the following three segments: "
'"10.0.0.0/24", "11.0.0.2/32", and "12.2.3.0/24", '
"a Monkey running on 10.0.0.5 will try to access machines in "
"the following subnets: "
"11.0.0.2/32, 12.2.3.0/24. An alert on successful cross-segment "
"connections "
"will be shown in the reports. \n\n"
"Network segments can be IPs, subnets or hosts. Examples:\n"
'\tDefine a single-IP segment: "192.168.0.1"\n'
"\tDefine a segment using a network range: "
'"192.168.0.5-192.168.0.20"\n'
'\tDefine a segment using an subnet IP mask: "192.168.0.5/24"\n'
'\tDefine a single-host segment: "printer.example"',
}
},
},
},
}

View File

@ -1,35 +0,0 @@
from monkey_island.cc.services.config_schema.basic import BASIC
from monkey_island.cc.services.config_schema.basic_network import BASIC_NETWORK
from monkey_island.cc.services.config_schema.definitions.credential_collector_classes import (
CREDENTIAL_COLLECTORS,
)
from monkey_island.cc.services.config_schema.definitions.exploiter_classes import EXPLOITER_CLASSES
from monkey_island.cc.services.config_schema.definitions.finger_classes import FINGER_CLASSES
from monkey_island.cc.services.config_schema.definitions.post_breach_actions import (
POST_BREACH_ACTIONS,
)
from monkey_island.cc.services.config_schema.internal import INTERNAL
from monkey_island.cc.services.config_schema.monkey import MONKEY
from monkey_island.cc.services.config_schema.ransomware import RANSOMWARE
SCHEMA = {
"title": "Monkey",
"type": "object",
# Newly added definitions should also be added to
# monkey/monkey_island/cc/ui/src/components/utils/SafeOptionValidator.js so that
# users will not accidentally chose unsafe options
"definitions": {
"exploiter_classes": EXPLOITER_CLASSES,
"credential_collectors": CREDENTIAL_COLLECTORS,
"post_breach_actions": POST_BREACH_ACTIONS,
"finger_classes": FINGER_CLASSES,
},
"properties": {
"basic": BASIC,
"basic_network": BASIC_NETWORK,
"monkey": MONKEY,
"ransomware": RANSOMWARE,
"internal": INTERNAL,
},
"options": {"collapsed": True},
}

View File

@ -1,83 +0,0 @@
from typing import Dict, List
class ConfigSchemaPerAttackTechnique:
def __init__(self) -> None:
self.reverse_schema = {}
def get_config_schema_per_attack_technique(
self, schema: Dict
) -> Dict[str, Dict[str, List[str]]]:
"""
example: \
{ \
"T1003": { \
"System Info Collectors": [ \
"Mimikatz collector", \
] \
} \
} \
:return: dictionary mapping each attack technique to relevant config \
fields
"""
self._crawl_config_schema_definitions_for_reverse_schema(schema)
self._crawl_config_schema_properties_for_reverse_schema(schema)
return self.reverse_schema
def _crawl_config_schema_definitions_for_reverse_schema(self, schema: Dict):
definitions = schema["definitions"]
for definition in definitions:
definition_type = definitions[definition]["title"]
for field in definitions[definition].get("anyOf", []):
config_field = field["title"]
for attack_technique in field.get("attack_techniques", []):
self._add_config_field_to_reverse_schema(
definition_type, config_field, attack_technique
)
def _crawl_config_schema_properties_for_reverse_schema(self, schema: Dict):
properties = schema["properties"]
for prop in properties:
property_type = properties[prop]["title"]
for category_name in properties[prop].get("properties", []):
category = properties[prop]["properties"][category_name]
self._crawl_properties(
config_option_path=property_type,
config_option=category,
)
def _crawl_properties(self, config_option_path: str, config_option: Dict):
config_option_path = (
f"{config_option_path} -> {config_option['title']}"
if "title" in config_option
else config_option_path
)
for config_option_name in config_option.get("properties", []):
new_config_option = config_option["properties"][config_option_name]
self._check_related_attack_techniques(
config_option_path=config_option_path,
config_option=new_config_option,
)
# check for "properties" and each property's related techniques recursively;
# the levels of nesting and where related techniques are declared won't
# always be fixed in the config schema
self._crawl_properties(config_option_path, new_config_option)
def _check_related_attack_techniques(self, config_option_path: str, config_option: Dict):
for attack_technique in config_option.get("related_attack_techniques", []):
# No config values could be a reason that related attack techniques are left
# unscanned. See https://github.com/guardicore/monkey/issues/1518 for more.
config_field = config_option["title"]
self._add_config_field_to_reverse_schema(
config_option_path, config_field, attack_technique
)
def _add_config_field_to_reverse_schema(
self, definition_type: str, config_field: str, attack_technique: str
) -> None:
self.reverse_schema.setdefault(attack_technique, {})
self.reverse_schema[attack_technique].setdefault(definition_type, [])
self.reverse_schema[attack_technique][definition_type].append(config_field)

View File

@ -1,25 +0,0 @@
from common.common_consts.credential_collector_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
CREDENTIAL_COLLECTORS = {
"title": "Credential Collectors",
"description": "Click on a credential collector to find out what it collects.",
"type": "string",
"anyOf": [
{
"type": "string",
"enum": [MIMIKATZ_COLLECTOR],
"title": "Mimikatz Credentials Collector",
"safe": True,
"info": "Collects credentials from Windows credential manager.",
"attack_techniques": ["T1003", "T1005"],
},
{
"type": "string",
"enum": [SSH_COLLECTOR],
"title": "SSH Credentials Collector",
"safe": True,
"info": "Searches users' home directories and collects SSH keypairs.",
"attack_techniques": ["T1005", "T1145"],
},
],
}

View File

@ -1,105 +0,0 @@
from monkey_island.cc.services.utils.typographic_symbols import WARNING_SIGN
EXPLOITER_CLASSES = {
"title": "Exploiters",
"description": "Click on exploiter to get more information about it."
+ WARNING_SIGN
+ " Note that using unsafe exploits may cause crashes of the exploited "
"machine/service.",
"type": "string",
"anyOf": [
{
"type": "string",
"enum": ["SmbExploiter"],
"title": "SMB Exploiter",
"safe": True,
"attack_techniques": ["T1110", "T1075", "T1035"],
"info": "Brute forces using credentials provided by user and"
" hashes gathered by mimikatz.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference"
"/exploiters/smbexec/",
},
{
"type": "string",
"enum": ["WmiExploiter"],
"title": "WMI Exploiter",
"safe": True,
"attack_techniques": ["T1110", "T1106"],
"info": "Brute forces WMI (Windows Management Instrumentation) "
"using credentials provided by user and hashes gathered by "
"mimikatz.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference"
"/exploiters/wmiexec/",
},
{
"type": "string",
"enum": ["MSSQLExploiter"],
"title": "MSSQL Exploiter",
"safe": True,
"attack_techniques": ["T1110"],
"info": "Tries to brute force into MsSQL server and uses insecure "
"configuration to execute commands on server.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference"
"/exploiters/mssql/",
},
{
"type": "string",
"enum": ["SSHExploiter"],
"title": "SSH Exploiter",
"safe": True,
"attack_techniques": ["T1110", "T1145", "T1106"],
"info": "Brute forces using credentials provided by user and SSH keys "
"gathered from systems.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference"
"/exploiters/sshexec/",
},
{
"type": "string",
"enum": ["HadoopExploiter"],
"title": "Hadoop/Yarn Exploiter",
"safe": True,
"info": "Remote code execution on HADOOP server with YARN and default settings. "
"Logic based on "
"https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/hadoop/",
},
{
"type": "string",
"enum": ["ZerologonExploiter"],
"title": "Zerologon Exploiter",
"safe": False,
"info": "Exploits a privilege escalation vulnerability (CVE-2020-1472) in a Windows "
"server domain controller (DC) by using the Netlogon Remote Protocol (MS-NRPC). "
"This exploiter changes the password of a Windows server DC account, steals "
"credentials, and then attempts to restore the original DC password. The victim DC "
"will be unable to communicate with other DCs until the original "
"password has been restored. If Infection Monkey fails to restore the "
"password automatically, you'll have to do it manually. For more "
"information, see the documentation.",
"link": "https://www.guardicore.com/infectionmonkey"
"/docs/reference/exploiters/zerologon/",
},
{
"type": "string",
"enum": ["PowerShellExploiter"],
"title": "PowerShell Remoting Exploiter",
"info": "Exploits PowerShell remote execution setups. PowerShell Remoting uses Windows "
"Remote Management (WinRM) to allow users to run PowerShell commands on remote "
"computers.",
"safe": True,
"link": "https://www.guardicore.com/infectionmonkey"
"/docs/reference/exploiters/powershell",
},
{
"type": "string",
"enum": ["Log4ShellExploiter"],
"title": "Log4Shell Exploiter",
"safe": True,
"info": "Exploits a software vulnerability (CVE-2021-44228) in Apache Log4j, a Java "
"logging framework. Exploitation is attempted on the following services — "
"Apache Solr, Apache Tomcat, Logstash.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference"
"/exploiters/log4shell/",
},
],
}

View File

@ -1,48 +0,0 @@
FINGER_CLASSES = {
"title": "Fingerprinters",
"description": "Fingerprint modules collect info about external services "
"Infection Monkey scans.",
"type": "string",
"anyOf": [
{
"type": "string",
"enum": ["SMBFinger"],
"title": "SMB Fingerprinter",
"safe": True,
"info": "Figures out if SMB is running and what's the version of it.",
"attack_techniques": ["T1210"],
},
{
"type": "string",
"enum": ["SSHFinger"],
"title": "SSH Fingerprinter",
"safe": True,
"info": "Figures out if SSH is running.",
"attack_techniques": ["T1210"],
},
{
"type": "string",
"enum": ["HTTPFinger"],
"title": "HTTP Fingerprinter",
"safe": True,
"info": "Checks if host has HTTP/HTTPS ports open.",
},
{
"type": "string",
"enum": ["MSSQLFinger"],
"title": "MSSQL Fingerprinter",
"safe": True,
"info": "Checks if Microsoft SQL service is running and tries to gather "
"information about it.",
"attack_techniques": ["T1210"],
},
{
"type": "string",
"enum": ["ElasticFinger"],
"title": "Elastic Fingerprinter",
"safe": True,
"info": "Checks if ElasticSearch is running and attempts to find it's " "version.",
"attack_techniques": ["T1210"],
},
],
}

View File

@ -1,105 +0,0 @@
POST_BREACH_ACTIONS = {
"title": "Post-Breach Actions",
"description": "Runs scripts/commands on infected machines. These actions safely simulate what "
"an adversary might do after breaching a new machine. Used in ATT&CK and Zero trust reports.",
"type": "string",
"anyOf": [
{
"type": "string",
"enum": ["CommunicateAsBackdoorUser"],
"title": "Communicate as Backdoor User",
"safe": True,
"info": "Attempts to create a new user, create HTTPS requests as that "
"user and delete the user "
"afterwards.",
"attack_techniques": ["T1136"],
},
{
"type": "string",
"enum": ["ModifyShellStartupFiles"],
"title": "Modify Shell Startup Files",
"safe": True,
"info": "Attempts to modify shell startup files, like ~/.profile, "
"~/.bashrc, ~/.bash_profile "
"in linux, and profile.ps1 in windows. Reverts modifications done"
" afterwards.",
"attack_techniques": ["T1156", "T1504"],
},
{
"type": "string",
"enum": ["HiddenFiles"],
"title": "Hidden Files and Directories",
"safe": True,
"info": "Attempts to create a hidden file and remove it afterward.",
"attack_techniques": ["T1158"],
},
{
"type": "string",
"enum": ["TrapCommand"],
"title": "Trap Command",
"safe": True,
"info": "On Linux systems, attempts to trap a terminate signal in order "
"to execute a command upon receiving that signal. Removes the trap afterwards.",
"attack_techniques": ["T1154"],
},
{
"type": "string",
"enum": ["ChangeSetuidSetgid"],
"title": "Setuid and Setgid",
"safe": True,
"info": "On Linux systems, attempts to set the setuid and setgid bits of "
"a new file. "
"Removes the file afterwards.",
"attack_techniques": ["T1166"],
},
{
"type": "string",
"enum": ["ScheduleJobs"],
"title": "Job Scheduling",
"safe": True,
"info": "Attempts to create a scheduled job on the system and remove it.",
"attack_techniques": ["T1168", "T1053"],
},
{
"type": "string",
"enum": ["Timestomping"],
"title": "Timestomping",
"safe": True,
"info": "Creates a temporary file and attempts to modify its time "
"attributes. Removes the file afterwards.",
"attack_techniques": ["T1099"],
},
{
"type": "string",
"enum": ["SignedScriptProxyExecution"],
"title": "Signed Script Proxy Execution",
"safe": False,
"info": "On Windows systems, attempts to execute an arbitrary file "
"with the help of a pre-existing signed script.",
"attack_techniques": ["T1216"],
},
{
"type": "string",
"enum": ["AccountDiscovery"],
"title": "Account Discovery",
"safe": True,
"info": "Attempts to get a listing of user accounts on the system.",
"attack_techniques": ["T1087"],
},
{
"type": "string",
"enum": ["ClearCommandHistory"],
"title": "Clear Command History",
"safe": False,
"info": "Attempts to clear the command history.",
"attack_techniques": ["T1146"],
},
{
"type": "string",
"enum": ["ProcessListCollection"],
"title": "Process List Collector",
"safe": True,
"info": "Collects a list of running processes on the machine.",
},
],
}

View File

@ -1,135 +0,0 @@
INTERNAL = {
"title": "Internal",
"type": "object",
"properties": {
"general": {
"title": "General",
"type": "object",
"properties": {
"keep_tunnel_open_time": {
"title": "Keep tunnel open time",
"type": "integer",
"default": 30,
"description": "Time to keep tunnel open before going down after last exploit "
"(in seconds)",
},
},
},
"network": {
"title": "Network",
"type": "object",
"properties": {
"tcp_scanner": {
"title": "TCP scanner",
"type": "object",
"properties": {
"HTTP_PORTS": {
"title": "HTTP ports",
"type": "array",
"uniqueItems": True,
"items": {"type": "integer"},
"default": [80, 8080, 443, 8008, 7001, 9200, 8983, 9600],
"description": "List of ports the monkey will check if are being used "
"for HTTP",
},
"tcp_target_ports": {
"title": "TCP target ports",
"type": "array",
"uniqueItems": True,
"items": {"type": "integer"},
"default": [
22,
2222,
445,
135,
3389,
80,
8080,
443,
8008,
3306,
7001,
8088,
5985,
5986,
],
"description": "List of TCP ports the monkey will check whether "
"they're open",
},
"tcp_scan_timeout": {
"title": "TCP scan timeout",
"type": "integer",
"default": 3000,
"description": "Maximum time (in milliseconds) "
"to wait for TCP response",
},
},
},
"ping_scanner": {
"title": "Ping scanner",
"type": "object",
"properties": {
"ping_scan_timeout": {
"title": "Ping scan timeout",
"type": "integer",
"default": 1000,
"description": "Maximum time (in milliseconds) to wait for ping "
"response",
}
},
},
},
},
"classes": {
"title": "Classes",
"type": "object",
"properties": {
"finger_classes": {
"title": "Fingerprint classes",
"type": "array",
"uniqueItems": True,
"items": {"$ref": "#/definitions/finger_classes"},
"default": [
"SMBFinger",
"SSHFinger",
"HTTPFinger",
"MSSQLFinger",
"ElasticFinger",
],
}
},
},
"exploits": {
"title": "Exploits",
"type": "object",
"properties": {
"exploit_lm_hash_list": {
"title": "Exploit LM hash list",
"type": "array",
"uniqueItems": True,
"items": {"type": "string"},
"default": [],
"description": "List of LM hashes to use on exploits using credentials",
"related_attack_techniques": ["T1075"],
},
"exploit_ntlm_hash_list": {
"title": "Exploit NTLM hash list",
"type": "array",
"uniqueItems": True,
"items": {"type": "string"},
"default": [],
"description": "List of NTLM hashes to use on exploits using credentials",
"related_attack_techniques": ["T1075"],
},
"exploit_ssh_keys": {
"title": "SSH key pairs list",
"type": "array",
"uniqueItems": True,
"default": [],
"items": {"type": "string"},
"description": "List of SSH key pairs to use, when trying to ssh into servers",
},
},
},
},
}

View File

@ -1,93 +0,0 @@
from common.common_consts.credential_collector_names import MIMIKATZ_COLLECTOR, SSH_COLLECTOR
MONKEY = {
"title": "Monkey",
"type": "object",
"properties": {
"post_breach": {
"title": "Post breach",
"type": "object",
"properties": {
"custom_PBA_linux_cmd": {
"title": "Linux post-breach command",
"type": "string",
"default": "",
"description": "Command to be executed after breaching. "
"Use this field to run custom commands or execute uploaded "
"files on exploited machines.\nExample: "
'"chmod +x ./my_script.sh; ./my_script.sh ; rm ./my_script.sh"',
},
"PBA_linux_file": {
"title": "Linux post-breach file",
"type": "string",
"format": "data-url",
"description": "File to be uploaded after breaching. "
"Use the 'Linux post-breach command' field to "
"change permissions, run, or delete the file. "
"Reference your file by filename.",
},
"custom_PBA_windows_cmd": {
"title": "Windows post-breach command",
"type": "string",
"default": "",
"description": "Command to be executed after breaching. "
"Use this field to run custom commands or execute uploaded "
"files on exploited machines.\nExample: "
'"my_script.bat & del my_script.bat"',
},
"PBA_windows_file": {
"title": "Windows post-breach file",
"type": "string",
"format": "data-url",
"description": "File to be uploaded after breaching. "
"Use the 'Windows post-breach command' field to "
"change permissions, run, or delete the file. "
"Reference your file by filename.",
},
"PBA_windows_filename": {
"title": "Windows PBA filename",
"type": "string",
"default": "",
},
"PBA_linux_filename": {
"title": "Linux PBA filename",
"type": "string",
"default": "",
},
"post_breach_actions": {
"title": "Post breach actions",
"type": "array",
"uniqueItems": True,
"items": {"$ref": "#/definitions/post_breach_actions"},
"default": [
"CommunicateAsBackdoorUser",
"ModifyShellStartupFiles",
"HiddenFiles",
"TrapCommand",
"ChangeSetuidSetgid",
"ScheduleJobs",
"Timestomping",
"AccountDiscovery",
"ProcessListCollection",
],
},
},
},
"credential_collectors": {
"title": "Credential collection",
"type": "object",
"properties": {
"credential_collectors": {
"title": "Credential collectors",
"type": "array",
"uniqueItems": True,
"items": {"$ref": "#/definitions/credential_collectors"},
"default": [
MIMIKATZ_COLLECTOR,
SSH_COLLECTOR,
],
},
},
},
},
}

View File

@ -1,71 +0,0 @@
from common.common_consts.validation_formats import (
VALID_RANSOMWARE_TARGET_PATH_LINUX,
VALID_RANSOMWARE_TARGET_PATH_WINDOWS,
)
RANSOMWARE = {
"title": "Ransomware",
"type": "object",
"properties": {
"encryption": {
"title": "Simulation",
"type": "object",
"description": "To simulate ransomware encryption, you'll need to provide Infection "
"Monkey with files that it can safely encrypt. On each machine where you would like "
"the ransomware simulation to run, create a directory and put some files in it."
"\n\nProvide the path to the directory that was created on each machine.",
"properties": {
"enabled": {
"title": "Encrypt files",
"type": "boolean",
"default": True,
"description": "Ransomware encryption will be simulated by flipping every bit "
"in the files contained within the target directories.",
},
"info_box": {
"info": "No files will be encrypted if a directory is not specified or doesn't "
"exist on a victim machine.",
},
"directories": {
"title": "Directories to encrypt",
"type": "object",
"properties": {
"linux_target_dir": {
"title": "Linux target directory",
"type": "string",
"format": VALID_RANSOMWARE_TARGET_PATH_LINUX,
"default": "",
"description": "A path to a directory on Linux systems that contains "
"files that you will allow Infection Monkey to encrypt. If no "
"directory is specified, no files will be encrypted.",
},
"windows_target_dir": {
"title": "Windows target directory",
"type": "string",
"format": VALID_RANSOMWARE_TARGET_PATH_WINDOWS,
"default": "",
"description": "A path to a directory on Windows systems that contains "
"files that you will allow Infection Monkey to encrypt. If no "
"directory is specified, no files will be encrypted.",
},
},
},
"text_box": {
"text": "Note: A README.txt will be left in the specified target " "directory.",
},
},
},
"other_behaviors": {
"title": "Other behavior",
"type": "object",
"properties": {
"readme": {
"title": "Create a README.txt file",
"type": "boolean",
"default": True,
"description": "Creates a README.txt ransomware note on infected systems.",
}
},
},
},
}

View File

@ -392,6 +392,7 @@ class ReportService:
@staticmethod
def get_config_exploits():
exploits_config_value = EXPLOITER_CLASSES_PATH
# TODO: Return default config here
default_exploits = ConfigService.get_default_config(False)
for namespace in exploits_config_value:
default_exploits = default_exploits[namespace]

View File

@ -1 +0,0 @@
WARNING_SIGN = " \u26A0"

View File

@ -1,20 +1,13 @@
from enum import Enum
import pytest
from common.utils.attack_utils import ScanStatus
from monkey_island.cc.services.attack.technique_reports.__init__ import AttackTechnique
from monkey_island.cc.services.attack.technique_reports.__init__ import (
UNSCANNED_MESSAGE,
AttackTechnique,
)
@pytest.fixture(scope="function", autouse=True)
def mock_config_schema_per_attack_technique(monkeypatch, fake_schema):
monkeypatch.setattr(
("monkey_island.cc.services.attack.technique_reports.__init__.SCHEMA"),
fake_schema,
)
class FakeAttackTechnique_TwoRelevantSystems(AttackTechnique):
class FakeAttackTechnique(AttackTechnique):
tech_id = "T0000"
relevant_systems = ["System 1", "System 2"]
unscanned_msg = "UNSCANNED"
@ -25,83 +18,22 @@ class FakeAttackTechnique_TwoRelevantSystems(AttackTechnique):
pass
class ExpectedMsgs_TwoRelevantSystems(Enum):
UNSCANNED: str = (
"UNSCANNED due to one of the following reasons:\n"
"- The following configuration options were disabled or empty:<br/>"
"- Definition Type 1 — Config Option 1, Config Option 2<br/>"
"- Definition Type 2 — Config Option 5, Config Option 6<br/>"
"- Property Type 1 -> Category 1 — Config Option 1<br/>"
"- Property Type 2 -> Category 1 — Config Option 1<br/>"
"- Property Type 2 -> Category 2 -> Config Option 1 — Config Option 1.1<br/>"
"- Property Type 2 -> Category 2 -> Config Option 2 — Config Option 2.1<br/>"
"- Property Type 2 -> Category 2 -> Config Option 2 -> Config Option 2.1 — Config Option "
"2.1.1<br/>"
)
class ExpectedMsgs(Enum):
UNSCANNED: str = UNSCANNED_MESSAGE
SCANNED: str = "SCANNED"
USED: str = "USED"
class FakeAttackTechnique_OneRelevantSystem(AttackTechnique):
tech_id = "T0001"
relevant_systems = ["System 1"]
unscanned_msg = "UNSCANNED"
scanned_msg = "SCANNED"
used_msg = "USED"
def get_report_data():
pass
def test_get_message_by_status_unscanned():
technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.UNSCANNED.value)
assert technique_msg == ExpectedMsgs.UNSCANNED.value
class ExpectedMsgs_OneRelevantSystem(Enum):
UNSCANNED: str = (
"UNSCANNED due to one of the following reasons:\n"
"- Monkey did not run on any System 1 systems.\n"
"- The following configuration options were disabled or empty:<br/>"
"- Definition Type 1 — Config Option 1<br/>"
"- Definition Type 2 — Config Option 5<br/>"
)
SCANNED: str = "SCANNED"
USED: str = "USED"
def test_get_message_by_status_scanned():
technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.SCANNED.value)
assert technique_msg == ExpectedMsgs.SCANNED.value
def test_get_message_by_status_unscanned_two_relevant_systems():
technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status(
ScanStatus.UNSCANNED.value
)
assert technique_msg == ExpectedMsgs_TwoRelevantSystems.UNSCANNED.value
def test_get_message_by_status_scanned_two_relevant_systems():
technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status(
ScanStatus.SCANNED.value
)
assert technique_msg == ExpectedMsgs_TwoRelevantSystems.SCANNED.value
def test_get_message_by_status_used_two_relevant_systems():
technique_msg = FakeAttackTechnique_TwoRelevantSystems.get_message_by_status(
ScanStatus.USED.value
)
assert technique_msg == ExpectedMsgs_TwoRelevantSystems.USED.value
def test_get_message_by_status_unscanned_one_relevant_system():
technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status(
ScanStatus.UNSCANNED.value
)
assert technique_msg == ExpectedMsgs_OneRelevantSystem.UNSCANNED.value
def test_get_message_by_status_scanned_one_relevant_system():
technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status(
ScanStatus.SCANNED.value
)
assert technique_msg == ExpectedMsgs_OneRelevantSystem.SCANNED.value
def test_get_message_by_status_used_one_relevant_system():
technique_msg = FakeAttackTechnique_OneRelevantSystem.get_message_by_status(
ScanStatus.USED.value
)
assert technique_msg == ExpectedMsgs_OneRelevantSystem.USED.value
def test_get_message_by_status_used():
technique_msg = FakeAttackTechnique.get_message_by_status(ScanStatus.USED.value)
assert technique_msg == ExpectedMsgs.USED.value

View File

@ -1,25 +0,0 @@
from monkey_island.cc.services.config_schema.config_schema_per_attack_technique import (
ConfigSchemaPerAttackTechnique,
)
REVERSE_FAKE_SCHEMA = {
"T0000": {
"Definition Type 1": ["Config Option 1", "Config Option 2"],
"Definition Type 2": ["Config Option 5", "Config Option 6"],
"Property Type 1 -> Category 1": ["Config Option 1"],
"Property Type 2 -> Category 1": ["Config Option 1"],
"Property Type 2 -> Category 2 -> Config Option 1": ["Config Option 1.1"],
"Property Type 2 -> Category 2 -> Config Option 2": ["Config Option 2.1"],
"Property Type 2 -> Category 2 -> Config Option 2 -> Config Option 2.1": [
"Config Option 2.1.1"
],
},
"T0001": {"Definition Type 1": ["Config Option 1"], "Definition Type 2": ["Config Option 5"]},
}
def test_get_config_schema_per_attack_technique(monkeypatch, fake_schema):
assert (
ConfigSchemaPerAttackTechnique().get_config_schema_per_attack_technique(fake_schema)
== REVERSE_FAKE_SCHEMA
)

View File

@ -15,7 +15,9 @@ def PORT():
@pytest.fixture
def config(monkeypatch):
config = ConfigService.get_default_config(True)
# Note: ConfigService is going away, don't try to fix the UT using
# this fixture
config = ConfigService.get_config(True)
return config

View File

@ -14,7 +14,9 @@ fake_ip_address = "192.168.56.1"
def fake_mongo(monkeypatch, uses_encryptor):
mongo = mongoengine.connection.get_connection()
monkeypatch.setattr("monkey_island.cc.services.config.mongo", mongo)
config = ConfigService.get_default_config()
# Note: ConfigService is going away, don't try to fix UT using
# this fixture
config = ConfigService.get_config()
ConfigService.update_config(config, should_encrypt=True)