Merge remote-tracking branch 'upstream/develop' into 519/scoutsuite-integration

# Conflicts:
#	.swm/AzD8XysWg1BBXCjCDkfq.swm
#	.swm/OwcKMnALpn7tuBaJY1US.swm
#	.swm/tbxb2cGgUiJQ8Btma0fp.swm
#	monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py
This commit is contained in:
VakarisZ 2021-02-04 11:29:00 +02:00
commit 6cb4280f89
63 changed files with 778 additions and 312 deletions

View File

@ -1,6 +1,6 @@
---
name: "⌛Spike"
about: Create a spike.
about: Create a spike to investigate a cool idea.
title: ''
labels: Spike
assignees: ''
@ -9,6 +9,11 @@ assignees: ''
# Spike
<!--
A spike is a small chunk of work with the objective of gathering information.
Fill in the details below to set the parameters and expectations for the spike.
-->
## Objective
_A description of this spike's objective._

View File

@ -4,31 +4,35 @@
"dod": "Make the max victim number that Monkey will find before stopping configurable by the user instead of constant.",
"description": "# Make something configurable\n\nIn this unit, you will learn how to add a configuration option to Monkey and how to use it in the Monkey Agent code. \n\n![computer fire](https://media.giphy.com/media/7J4P7cUur2DlErijp3/giphy.gif \"computer fire\")\n\n## Why is this important?\n\nEnabling users to configure the Monkey's behaviour gives them a lot more freedom in how they want to use the Monkey and enables more use cases.\n\n## What is \"Max victims to find\"?\n\nThe Monkey has a function which finds \"victim\" machines on the network for the Monkey to try and exploit. It's called `get_victim_machines`. This function accepts an argument which limits how many machines the Monkey should find.\n\nWe want to make that value editable by the user instead of constant in the code.\n\n## Manual testing\n\n1. After you've performed the required changes, reload the Server and check your value exists in the Internal tab of the config (see image).\n\n![](https://i.imgur.com/e0XAxuV.png)\n\n2. Set the new value to 1, and run Monkey locally (from source). See that the Monkey only scans one machine.",
"summary": "* When changing config schema by adding or deleting keys, you need to update the Blackbox Test configurations as well [here](https://github.com/guardicore/monkey/tree/develop/envs/monkey_zoo/blackbox/island_configs).",
"diff": "diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py\nindex 1fbcb876..67ed19de 100644\n--- a/monkey/infection_monkey/config.py\n+++ b/monkey/infection_monkey/config.py\n@@ -120,9 +120,6 @@\n exploiter_classes = []\n system_info_collector_classes = []\n \n- # how many victims to look for in a single scan iteration\n- victims_max_find = 100\n-\n # how many victims to exploit before stopping\n victims_max_exploit = 100\n \ndiff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py\nindex 444bde45..ff23f671 100644\n--- a/monkey/infection_monkey/monkey.py\n+++ b/monkey/infection_monkey/monkey.py\n@@ -154,7 +154,7 @@\n if not self._keep_running or not WormConfiguration.alive:\n break\n \n- machines = self._network.get_victim_machines(max_find=WormConfiguration.victims_max_find,\n+ machines = self._network.get_victim_machines(max_find=100,\n stop_callback=ControlClient.check_for_stop)\n is_empty = True\n for machine in machines:\ndiff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py\nindex bdbae246..d6042d35 100644\n--- a/monkey/monkey_island/cc/services/config_schema/internal.py\n+++ b/monkey/monkey_island/cc/services/config_schema/internal.py\n@@ -40,12 +40,6 @@\n \"title\": \"Monkey\",\n \"type\": \"object\",\n \"properties\": {\n- \"victims_max_find\": {\n- \"title\": \"Max victims to find\",\n- \"type\": \"integer\",\n- \"default\": 100,\n- \"description\": \"Determines the maximum number of machines the monkey is allowed to scan\"\n- },\n \"victims_max_exploit\": {\n \"title\": \"Max victims to exploit\",\n \"type\": \"integer\",\n",
"hunksOrder": [
"monkey/infection_monkey/config.py_0",
"monkey/infection_monkey/monkey.py_0",
"monkey/monkey_island/cc/services/config_schema/internal.py_0"
],
"tests": [],
"hints": [
"Look for `victims_max_exploit` - it's rather similar."
],
"app_version": "0.2.1",
"file_version": "1.0.3",
"play_mode": "all",
"swimmPatch": {
"monkey/infection_monkey/config.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py\nindex 1fbcb876..67ed19de 100644\n--- a/monkey/infection_monkey/config.py\n+++ b/monkey/infection_monkey/config.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -120,9 +120,6 @@",
" exploiter_classes = []",
" system_info_collector_classes = []",
" ",
"- # how many victims to look for in a single scan iteration",
"- victims_max_find = 100",
"-",
" # how many victims to exploit before stopping",
" victims_max_exploit = 100",
" "
"@@ -131,8 +131,6 @@",
" exploiter_classes = []\r",
" system_info_collector_classes = []\r",
" \r",
"- # how many victims to look for in a single scan iteration\r",
"- victims_max_find = 100\r",
" \r",
" # how many victims to exploit before stopping\r",
" victims_max_exploit = 100\r"
]
}
]
@ -38,17 +42,19 @@
"fileDiffHeader": "diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py\nindex 444bde45..ff23f671 100644\n--- a/monkey/infection_monkey/monkey.py\n+++ b/monkey/infection_monkey/monkey.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -154,7 +154,7 @@",
" if not self._keep_running or not WormConfiguration.alive:",
" break",
" ",
"- machines = self._network.get_victim_machines(max_find=WormConfiguration.victims_max_find,",
"+ machines = self._network.get_victim_machines(max_find=100,",
" stop_callback=ControlClient.check_for_stop)",
" is_empty = True",
" for machine in machines:"
"@@ -159,8 +159,6 @@",
" if not self._keep_running or not WormConfiguration.alive:\r",
" break\r",
" \r",
"- machines = self._network.get_victim_machines(max_find=WormConfiguration.victims_max_find,\r",
"- stop_callback=ControlClient.check_for_stop)\r",
" is_empty = True\r",
" for machine in machines:\r",
" if ControlClient.check_for_stop():\r"
]
}
]
@ -58,24 +64,29 @@
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py\nindex bdbae246..d6042d35 100644\n--- a/monkey/monkey_island/cc/services/config_schema/internal.py\n+++ b/monkey/monkey_island/cc/services/config_schema/internal.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -40,12 +40,6 @@",
" \"title\": \"Monkey\",",
" \"type\": \"object\",",
" \"properties\": {",
"- \"victims_max_find\": {",
"- \"title\": \"Max victims to find\",",
"- \"type\": \"integer\",",
"- \"default\": 100,",
"- \"description\": \"Determines the maximum number of machines the monkey is allowed to scan\"",
"- },",
" \"victims_max_exploit\": {",
" \"title\": \"Max victims to exploit\",",
" \"type\": \"integer\","
" \"title\": \"Monkey\",\r",
" \"type\": \"object\",\r",
" \"properties\": {\r",
"- \"victims_max_find\": {\r",
"- \"title\": \"Max victims to find\",\r",
"- \"type\": \"integer\",\r",
"- \"default\": 100,\r",
"- \"description\": \"Determines the maximum number of machines the monkey is allowed to scan\"\r",
"- },\r",
" \"victims_max_exploit\": {\r",
" \"title\": \"Max victims to exploit\",\r",
" \"type\": \"integer\",\r"
]
}
]
}
}
},
"app_version": "0.3.5-1",
"file_version": "1.0.4",
"last_commit_sha_for_swimm_patch": "17ee823b086f0b027612e2d1864930d2c5593c3e"
}

View File

@ -1,30 +1,54 @@
{
"id": "JFXftJml8DpmuCPBA9rL",
"name": "Add details about your new PBA",
"dod": "WW91JTIwc2hvdWxkJTIwYWRkJTIweW91ciUyMG5ldyUyMFBCQSdzJTIwZGV0YWlscyUyMHRvJTIwdGhlJTIwY29uZmlndXJhdGlvbi4=",
"description": "SW4lMjBvcmRlciUyMHRvJTIwbWFrZSUyMHN1cmUlMjB0aGF0JTIwdGhlJTIwbmV3JTIwJTYwU2NoZWR1bGVKb2JzJTYwJTIwUEJBJTIwaXMlMjBzaG93biUyMGluJTIwdGhlJTIwY29uZmlndXJhdGlvbiUyMG9uJTIwdGhlJTIwTW9ua2V5JTIwSXNsYW5kJTJDJTIweW91JTIwbmVlZCUyMHRvJTIwYWRkJTIwaXRzJTIwZGV0YWlscyUyMHRvJTIwdGhlJTIwY29uZmlndXJhdGlvbiUyMGZpbGUocykuJTIwJTNDYnIlM0UlM0NiciUzRSUwQSUwQVNpbmNlJTIwdGhpcyUyMHBhcnRpY3VsYXIlMjBQQkElMjBpcyUyMHJlbGF0ZWQlMjB0byUyMHRoZSUyME1JVFJFJTIwdGVjaG5pcXVlcyUyMCU1QlQxMTY4JTVEKGh0dHBzJTNBJTJGJTJGYXR0YWNrLm1pdHJlLm9yZyUyRnRlY2huaXF1ZXMlMkZUMTE2OCklMjBhbmQlMjAlNUJUMTA1MyU1RChodHRwcyUzQSUyRiUyRmF0dGFjay5taXRyZS5vcmclMkZ0ZWNobmlxdWVzJTJGVDEwNTMpJTJDJTIwbWFrZSUyMHN1cmUlMjB0byUyMGxpbmslMjB0aGUlMjBQQkElMjB3aXRoJTIwdGhlc2UlMjB0ZWNobmlxdWVzJTIwaW4lMjB0aGUlMjBjb25maWd1cmF0aW9uJTIwYXMlMjB3ZWxsLiUyMCUzQ2JyJTNFJTNDYnIlM0UlMEElMEFFYWNoJTIwcGFydCUyMG9mJTIwdGhlJTIwY29uZmlndXJhdGlvbiUyMGhhcyUyMGFuJTIwaW1wb3J0YW50JTIwcm9sZSUyMCUyMCUwQS0lMjAqZW51bSolMjAlRTIlODAlOTQlMjBjb250YWlucyUyMHRoZSUyMHJlbGV2YW50JTIwUEJBJ3MlMjBjbGFzcyUyMG5hbWUocyklMEEtJTIwKnRpdGxlKiUyMCVFMiU4MCU5NCUyMGhvbGRzJTIwdGhlJTIwbmFtZSUyMG9mJTIwdGhlJTIwUEJBJTIwd2hpY2glMjBpcyUyMGRpc3BsYXllZCUyMGluJTIwdGhlJTIwY29uZmlndXJhdGlvbiUyMG9uJTIwdGhlJTIwTW9ua2V5JTIwSXNsYW5kJTBBLSUyMCppbmZvKiUyMCVFMiU4MCU5NCUyMGNvbnNpc3RzJTIwb2YlMjBhbiUyMGVsYWJvcmF0aW9uJTIwb24lMjB0aGUlMjBQQkEncyUyMHdvcmtpbmclMjB3aGljaCUyMGlzJTIwZGlzcGxheWVkJTIwaW4lMjB0aGUlMjBjb25maWd1cmF0aW9uJTIwb24lMjB0aGUlMjBNb25rZXklMjBJc2xhbmQlMEEtJTIwKmF0dGFja190ZWNobmlxdWVzKiUyMCVFMiU4MCU5NCUyMGhhcyUyMHRoZSUyMElEcyUyMG9mJTIwdGhlJTIwTUlUUkUlMjB0ZWNobmlxdWVzJTIwYXNzb2NpYXRlZCUyMHdpdGglMjB0aGUlMjBQQkElMEElMEElMjMlMjMlMjBNYW51YWwlMjB0ZXN0JTIwJTIwJTBBT25jZSUyMHlvdSUyMHRoaW5rJTIweW91J3JlJTIwZG9uZS4uLiUwQS0lMjBSdW4lMjB0aGUlMjBNb25rZXklMjBJc2xhbmQlMEEtJTIwWW91JTIwc2hvdWxkJTIwYmUlMjBhYmxlJTIwdG8lMjBzZWUlMjB5b3VyJTIwbmV3JTIwUEJBJTIwdW5kZXIlMjB0aGUlMjAlMjJNb25rZXklMjIlMjB0YWIlMjBpbiUyMHRoZSUyMGNvbmZpZ3VyYXRpb24lMkMlMjBhbG9uZyUyMHdpdGglMjBpdHMlMjBpbmZvcm1hdGlvbiUyMHdoZW4lMjB5b3UlMjBjbGljayUyMG9uJTIwaXQlMEEtJTIwRnVydGhlciUyQyUyMHdoZW4lMjB5b3UlMjBlbmFibGUlMkZkaXNhYmxlJTIwdGhlJTIwYXNzb2NpYXRlZCUyME1JVFJFJTIwdGVjaG5pcXVlcyUyMHVuZGVyJTIwdGhlJTIwQVRUJTI2Q0slMjB0YWIlMjBpbiUyMHRoZSUyMGNvbmZpZ3VyYXRpb24lMkMlMjB0aGUlMjBQQkElMjBzaG91bGQlMjBhbHNvJTIwYmUlMjBlbmFibGVkJTJGZGlzYWJsZWQlMEElMEElM0NpbWclMjBzcmMlM0QlMjJodHRwcyUzQSUyRiUyRmkuaW1ndXIuY29tJTJGYTVWU2tMNS5naWYlMjIlMjBoZWlnaHQlM0Q0MDAlM0U=",
"summary": "LSUyMFRoZSUyMFBCQSUyMGRldGFpbHMlMjBpbiUyMHRoaXMlMjBmaWxlJTIwYXJlJTIwcmVmbGVjdGVkJTIwb24lMjB0aGUlMjBNb25rZXklMjBJc2xhbmQlMjBpbiUyMHRoZSUyMFBCQSUyMGNvbmZpZ3VyYXRpb24uJTBBLSUyMFBCQXMlMjBhcmUlMjBhbHNvJTIwbGlua2VkJTIwdG8lMjB0aGUlMjByZWxldmFudCUyME1JVFJFJTIwdGVjaG5pcXVlcyUyMGluJTIwdGhpcyUyMGZpbGUlMkMlMjB3aG9zZSUyMHJlc3VsdHMlMjBjYW4lMjB0aGVuJTIwYmUlMjBzZWVuJTIwaW4lMjB0aGUlMjBNSVRSRSUyMEFUVCUyNkNLJTIwcmVwb3J0JTIwb24lMjB0aGUlMjBNb25rZXklMjBJc2xhbmQu",
"diff": "ZGlmZiUyMC0tZ2l0JTIwYSUyRm1vbmtleSUyRm1vbmtleV9pc2xhbmQlMkZjYyUyRnNlcnZpY2VzJTJGY29uZmlnX3NjaGVtYSUyRmRlZmluaXRpb25zJTJGcG9zdF9icmVhY2hfYWN0aW9ucy5weSUyMGIlMkZtb25rZXklMkZtb25rZXlfaXNsYW5kJTJGY2MlMkZzZXJ2aWNlcyUyRmNvbmZpZ19zY2hlbWElMkZkZWZpbml0aW9ucyUyRnBvc3RfYnJlYWNoX2FjdGlvbnMucHklMEFpbmRleCUyMGYxZmUwZjZmLi5jY2UzN2IyNCUyMDEwMDY0NCUwQS0tLSUyMGElMkZtb25rZXklMkZtb25rZXlfaXNsYW5kJTJGY2MlMkZzZXJ2aWNlcyUyRmNvbmZpZ19zY2hlbWElMkZkZWZpbml0aW9ucyUyRnBvc3RfYnJlYWNoX2FjdGlvbnMucHklMEElMkIlMkIlMkIlMjBiJTJGbW9ua2V5JTJGbW9ua2V5X2lzbGFuZCUyRmNjJTJGc2VydmljZXMlMkZjb25maWdfc2NoZW1hJTJGZGVmaW5pdGlvbnMlMkZwb3N0X2JyZWFjaF9hY3Rpb25zLnB5JTBBJTQwJTQwJTIwLTYyJTJDMTUlMjAlMkI2MiUyQzclMjAlNDAlNDAlMjBQT1NUX0JSRUFDSF9BQ1RJT05TJTIwJTNEJTIwJTdCJTBBJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIyUmVtb3ZlcyUyMHRoZSUyMGZpbGUlMjBhZnRlcndhcmRzLiUyMiUyQyUwQSUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMmF0dGFja190ZWNobmlxdWVzJTIyJTNBJTIwJTVCJTIyVDExNjYlMjIlNUQlMEElMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlN0QlMkMlMEEtJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTdCJTBBLSUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMnR5cGUlMjIlM0ElMjAlMjJzdHJpbmclMjIlMkMlMEEtJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIyZW51bSUyMiUzQSUyMCU1QiUwQS0lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjJTY2hlZHVsZUpvYnMlMjIlMEEtJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTVEJTJDJTBBLSUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMnRpdGxlJTIyJTNBJTIwJTIySm9iJTIwc2NoZWR1bGluZyUyMiUyQyUwQS0lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjJpbmZvJTIyJTNBJTIwJTIyQXR0ZW1wdHMlMjB0byUyMGNyZWF0ZSUyMGElMjBzY2hlZHVsZWQlMjBqb2IlMjBvbiUyMHRoZSUyMHN5c3RlbSUyMGFuZCUyMHJlbW92ZSUyMGl0LiUyMiUyQyUwQS0lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjJhdHRhY2tfdGVjaG5pcXVlcyUyMiUzQSUyMCU1QiUyMlQxMTY4JTIyJTJDJTIwJTIyVDEwNTMlMjIlNUQlMEEtJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTdEJTJDJTBBJTJCJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIzJTIwU3dpbW1lciUzQSUyMEFERCUyMERFVEFJTFMlMjBIRVJFISUwQSUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU3QiUwQSUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMnR5cGUlMjIlM0ElMjAlMjJzdHJpbmclMjIlMkMlMEElMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjJlbnVtJTIyJTNBJTIwJTVCJTBB",
"dod": "You should add your new PBA's details to the configuration.",
"description": "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- Further, when you enable/disable the associated MITRE techniques under the ATT&CK tab in the configuration, the PBA should also be enabled/disabled\n\n<img src=\"https://i.imgur.com/a5VSkL5.gif\" height=400>",
"summary": "- 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.",
"hunksOrder": [
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py_0"
],
"tests": [],
"hints": [
"Have a look at the details of the other techniques."
],
"files": {
"play_mode": "all",
"swimmPatch": {
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py": {
"index": [
"f1fe0f6f..cce37b24",
"100644"
],
"fileA": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"fileB": "monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"status": "MODIFIED",
"numLineDeletions": 9,
"numLineAdditions": 1,
"hunkContainers": [
"JTdCJTIyaHVuayUyMiUzQSU3QiUyMmhlYWRlciUyMiUzQSUyMiU0MCU0MCUyMC02MiUyQzE1JTIwJTJCNjIlMkM3JTIwJTQwJTQwJTIwUE9TVF9CUkVBQ0hfQUNUSU9OUyUyMCUzRCUyMCU3QiUyMiUyQyUyMmNoYW5nZXMlMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIyY29udGV4dCUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlNUMlMjJSZW1vdmVzJTIwdGhlJTIwZmlsZSUyMGFmdGVyd2FyZHMuJTVDJTIyJTJDJTIyJTJDJTIybGluZU51bWJlcnMlMjIlM0ElN0IlMjJhJTIyJTNBNjIlMkMlMjJiJTIyJTNBNjIlN0QlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyY29udGV4dCUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlNUMlMjJhdHRhY2tfdGVjaG5pcXVlcyU1QyUyMiUzQSUyMCU1QiU1QyUyMlQxMTY2JTVDJTIyJTVEJTIyJTJDJTIybGluZU51bWJlcnMlMjIlM0ElN0IlMjJhJTIyJTNBNjMlMkMlMjJiJTIyJTNBNjMlN0QlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyY29udGV4dCUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlN0QlMkMlMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0E2NCUyQyUyMmIlMjIlM0E2NCU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJkZWwlMjIlMkMlMjJtYXJrJTIyJTNBJTIyLSUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlN0IlMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0E2NSU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJkZWwlMjIlMkMlMjJtYXJrJTIyJTNBJTIyLSUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlNUMlMjJ0eXBlJTVDJTIyJTNBJTIwJTVDJTIyc3RyaW5nJTVDJTIyJTJDJTIyJTJDJTIybGluZU51bWJlcnMlMjIlM0ElN0IlMjJhJTIyJTNBNjYlN0QlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyZGVsJTIyJTJDJTIybWFyayUyMiUzQSUyMi0lMjIlMkMlMjJkYXRhJTIyJTNBJTIyJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTVDJTIyZW51bSU1QyUyMiUzQSUyMCU1QiUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTY3JTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmRlbCUyMiUyQyUyMm1hcmslMjIlM0ElMjItJTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU1QyUyMlNjaGVkdWxlSm9icyU1QyUyMiUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTY4JTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmRlbCUyMiUyQyUyMm1hcmslMjIlM0ElMjItJTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU1RCUyQyUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTY5JTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmRlbCUyMiUyQyUyMm1hcmslMjIlM0ElMjItJTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU1QyUyMnRpdGxlJTVDJTIyJTNBJTIwJTVDJTIySm9iJTIwc2NoZWR1bGluZyU1QyUyMiUyQyUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTcwJTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmRlbCUyMiUyQyUyMm1hcmslMjIlM0ElMjItJTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU1QyUyMmluZm8lNUMlMjIlM0ElMjAlNUMlMjJBdHRlbXB0cyUyMHRvJTIwY3JlYXRlJTIwYSUyMHNjaGVkdWxlZCUyMGpvYiUyMG9uJTIwdGhlJTIwc3lzdGVtJTIwYW5kJTIwcmVtb3ZlJTIwaXQuJTVDJTIyJTJDJTIyJTJDJTIybGluZU51bWJlcnMlMjIlM0ElN0IlMjJhJTIyJTNBNzElN0QlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyZGVsJTIyJTJDJTIybWFyayUyMiUzQSUyMi0lMjIlMkMlMjJkYXRhJTIyJTNBJTIyJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTVDJTIyYXR0YWNrX3RlY2huaXF1ZXMlNUMlMjIlM0ElMjAlNUIlNUMlMjJUMTE2OCU1QyUyMiUyQyUyMCU1QyUyMlQxMDUzJTVDJTIyJTVEJTIyJTJDJTIybGluZU51bWJlcnMlMjIlM0ElN0IlMjJhJTIyJTNBNzIlN0QlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyZGVsJTIyJTJDJTIybWFyayUyMiUzQSUyMi0lMjIlMkMlMjJkYXRhJTIyJTNBJTIyJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTdEJTJDJTIyJTJDJTIybGluZU51bWJlcnMlMjIlM0ElN0IlMjJhJTIyJTNBNzMlN0QlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyYWRkJTIyJTJDJTIybWFyayUyMiUzQSUyMiUyQiUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjMlMjBTd2ltbWVyJTNBJTIwQUREJTIwREVUQUlMUyUyMEhFUkUhJTIyJTJDJTIybGluZU51bWJlcnMlMjIlM0ElN0IlMjJiJTIyJTNBNjUlN0QlN0QlMkMlN0IlMjJ0eXBlJTIyJTNBJTIyY29udGV4dCUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlN0IlMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0E3NCUyQyUyMmIlMjIlM0E2NiU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJjb250ZXh0JTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU1QyUyMnR5cGUlNUMlMjIlM0ElMjAlNUMlMjJzdHJpbmclNUMlMjIlMkMlMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0E3NSUyQyUyMmIlMjIlM0E2NyU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJjb250ZXh0JTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCU1QyUyMmVudW0lNUMlMjIlM0ElMjAlNUIlMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0E3NiUyQyUyMmIlMjIlM0E2OCU3RCU3RCU1RCUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQSU3QiUyMnN0YXJ0TGluZSUyMiUzQTYyJTJDJTIybGluZXNDb3VudCUyMiUzQTE1JTdEJTJDJTIyYiUyMiUzQSU3QiUyMnN0YXJ0TGluZSUyMiUzQTYyJTJDJTIybGluZXNDb3VudCUyMiUzQTclN0QlN0QlN0QlN0Q="
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py\nindex f1fe0f6f..b231f96c 100644\n--- a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py\n+++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"hunks": [
{
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -68,16 +68,7 @@",
" \"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\": ["
]
}
]
}
},
"app_version": "0.1.90",
"file_version": "1.0.2"
"app_version": "0.3.5-1",
"file_version": "1.0.4",
"hunksOrder": [
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py_0"
],
"last_commit_sha_for_swimm_patch": "9d9e8168fb2c23367b9947273aa1a041687b3e2e"
}

View File

@ -4,6 +4,17 @@
"dod": "Add a system info collector that collects the machine hostname.",
"description": "# What are system info collectors?\n\nWell, the name pretty much explains it. They are Monkey classes which collect various information regarding the victim system, such as Environment, SSH Info, Process List, Netstat and more. \n\n## What should I add? \n\nA system info collector which collects the hostname of the system.\n\n## Test manually\n\nOnce you're done, make sure that your collector:\n* Appears in the Island configuration, and is enabled by default\n* The collector actually runs when executing a Monkey.\n* Results show up in the relevant places:\n * The infection map.\n * The security report.\n * The relevant MITRE techniques.\n\n**There are a lot of hints for this unit - don't be afraid to use them!**",
"summary": "System info collectors are useful to get more data for various things, such as ZT tests or MITRE techniques. Take a look at some other techniques!",
"hunksOrder": [
"monkey/common/data/system_info_collectors_names.py_0",
"monkey/infection_monkey/system_info/collectors/hostname_collector.py_0",
"monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py_0",
"monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py_1",
"monkey/monkey_island/cc/services/config_schema/monkey.py_0",
"monkey/monkey_island/cc/services/config_schema/monkey.py_1",
"monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py_0",
"monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py_0",
"monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py_1"
],
"tests": [],
"hints": [
"First thing you should do is take a look at a different collector (like EnvironmentCollector) and 100% understand how it runs, how results are relayed back to the server, and how the server processes the data.",
@ -11,13 +22,16 @@
"Take a look at SystemInfoCollector - that's the base class you'll need to implement.",
"Make sure you add the new collector to the configuration in all relevant places, including making it ON by default!"
],
"play_mode": "all",
"swimmPatch": {
"monkey/common/common_consts/system_info_collectors_names.py": {
"monkey/common/data/system_info_collectors_names.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/common/common_consts/system_info_collectors_names.py b/monkey/common/common_consts/system_info_collectors_names.py\nindex c93cb253..bce6f86e 100644\n--- a/monkey/common/common_consts/system_info_collectors_names.py\n+++ b/monkey/common/common_consts/system_info_collectors_names.py",
"fileDiffHeader": "diff --git a/monkey/common/data/system_info_collectors_names.py b/monkey/common/data/system_info_collectors_names.py\nindex 175a054e..3b478dc9 100644\n--- a/monkey/common/data/system_info_collectors_names.py\n+++ b/monkey/common/data/system_info_collectors_names.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -1,5 +1,5 @@",
" AWS_COLLECTOR = \"AwsCollector\"",
@ -32,17 +46,20 @@
},
"monkey/infection_monkey/system_info/collectors/hostname_collector.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/infection_monkey/system_info/collectors/hostname_collector.py b/monkey/infection_monkey/system_info/collectors/hostname_collector.py\nindex 0aeecd9f..4dc6701a 100644\n--- a/monkey/infection_monkey/system_info/collectors/hostname_collector.py\n+++ b/monkey/infection_monkey/system_info/collectors/hostname_collector.py",
"fileDiffHeader": "diff --git a/monkey/infection_monkey/system_info/collectors/hostname_collector.py b/monkey/infection_monkey/system_info/collectors/hostname_collector.py\nindex ae956081..bdeb5033 100644\n--- a/monkey/infection_monkey/system_info/collectors/hostname_collector.py\n+++ b/monkey/infection_monkey/system_info/collectors/hostname_collector.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -1,15 +1,5 @@",
"@@ -1,16 +1,5 @@",
" import logging",
"-import socket",
"-",
"-from common.common_consts.system_info_collectors_names import HOSTNAME_COLLECTOR",
"-from infection_monkey.system_info.system_info_collector import SystemInfoCollector",
"-from common.data.system_info_collectors_names import HOSTNAME_COLLECTOR",
"-from infection_monkey.system_info.system_info_collector import \\",
"- SystemInfoCollector",
" ",
" logger = logging.getLogger(__name__)",
" ",
@ -60,24 +77,29 @@
},
"monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py\nindex 0ea92bea..f4517b90 100644\n--- a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py\n+++ b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py\nindex 174133f4..de961fbd 100644\n--- a/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py\n+++ b/monkey/monkey_island/cc/services/config_schema/definitions/system_info_collector_classes.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -1,5 +1,5 @@",
" from common.common_consts.system_info_collectors_names import (AWS_COLLECTOR, AZURE_CRED_COLLECTOR,",
"- ENVIRONMENT_COLLECTOR, HOSTNAME_COLLECTOR,",
"+ ENVIRONMENT_COLLECTOR,",
" MIMIKATZ_COLLECTOR, PROCESS_LIST_COLLECTOR)",
" ",
" SYSTEM_INFO_COLLECTOR_CLASSES = {"
"@@ -1,7 +1,6 @@",
" from common.data.system_info_collectors_names import (AWS_COLLECTOR,",
" AZURE_CRED_COLLECTOR,",
" ENVIRONMENT_COLLECTOR,",
"- HOSTNAME_COLLECTOR,",
" MIMIKATZ_COLLECTOR,",
" PROCESS_LIST_COLLECTOR)",
" "
]
},
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -34,15 +34,7 @@",
"@@ -40,16 +39,7 @@",
" \"info\": \"If on AWS, collects more information about the AWS instance currently running on.\",",
" \"attack_techniques\": [\"T1082\"]",
" },",
@ -88,6 +110,7 @@
"- HOSTNAME_COLLECTOR",
"- ],",
"- \"title\": \"Hostname collector\",",
"- \"safe\": True,",
"- \"info\": \"Collects machine's hostname.\",",
"- \"attack_techniques\": [\"T1082\", \"T1016\"]",
"- },",
@ -100,24 +123,29 @@
},
"monkey/monkey_island/cc/services/config_schema/monkey.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/config_schema/monkey.py b/monkey/monkey_island/cc/services/config_schema/monkey.py\nindex 01d46367..84cd9123 100644\n--- a/monkey/monkey_island/cc/services/config_schema/monkey.py\n+++ b/monkey/monkey_island/cc/services/config_schema/monkey.py",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/config_schema/monkey.py b/monkey/monkey_island/cc/services/config_schema/monkey.py\nindex b47d6a15..1b1962a4 100644\n--- a/monkey/monkey_island/cc/services/config_schema/monkey.py\n+++ b/monkey/monkey_island/cc/services/config_schema/monkey.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -1,5 +1,5 @@",
" from common.common_consts.system_info_collectors_names import (AWS_COLLECTOR, AZURE_CRED_COLLECTOR,",
"- ENVIRONMENT_COLLECTOR, HOSTNAME_COLLECTOR,",
"+ ENVIRONMENT_COLLECTOR,",
" MIMIKATZ_COLLECTOR, PROCESS_LIST_COLLECTOR)",
" ",
" MONKEY = {"
"@@ -1,7 +1,6 @@",
" from common.data.system_info_collectors_names import (AWS_COLLECTOR,",
" AZURE_CRED_COLLECTOR,",
" ENVIRONMENT_COLLECTOR,",
"- HOSTNAME_COLLECTOR,",
" MIMIKATZ_COLLECTOR,",
" PROCESS_LIST_COLLECTOR)",
" "
]
},
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -85,7 +85,6 @@",
"@@ -88,7 +87,6 @@",
" \"default\": [",
" ENVIRONMENT_COLLECTOR,",
" AWS_COLLECTOR,",
@ -131,17 +159,19 @@
},
"monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py\nindex e2de4519..8764e29d 100644\n--- a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py\n+++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py\nindex e2de4519..04bc3556 100644\n--- a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py\n+++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -1,9 +1,9 @@",
" import logging",
" ",
"-from monkey_island.cc.models.monkey import Monkey",
"+# SWIMMER: This will be useful :) monkey_island.cc.models.monkey.Monkey has the useful",
"+# \"get_single_monkey_by_guid\" and \"set_hostname\" methods",
"+# \"get_single_monkey_by_guid\" and \"set_hostname\" methods.",
" ",
" logger = logging.getLogger(__name__)",
" ",
@ -155,42 +185,48 @@
},
"monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py\nindex 1d71c89c..edb1aefa 100644\n--- a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py\n+++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py\nindex 639a392c..7aa6d3a6 100644\n--- a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py\n+++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -2,11 +2,10 @@",
" import typing",
"@@ -3,14 +3,11 @@",
" ",
" from common.common_consts.system_info_collectors_names import (",
"- AWS_COLLECTOR, ENVIRONMENT_COLLECTOR, HOSTNAME_COLLECTOR, PROCESS_LIST_COLLECTOR, SCOUTSUITE_COLLECTOR)",
"+ AWS_COLLECTOR, ENVIRONMENT_COLLECTOR, PROCESS_LIST_COLLECTOR, SCOUTSUITE_COLLECTOR)",
" from monkey_island.cc.services.telemetry.processing.system_info_collectors.aws import process_aws_telemetry",
" from common.data.system_info_collectors_names import (AWS_COLLECTOR,",
" ENVIRONMENT_COLLECTOR,",
"- HOSTNAME_COLLECTOR,",
" PROCESS_LIST_COLLECTOR)",
" from monkey_island.cc.services.telemetry.processing.system_info_collectors.aws import \\",
" process_aws_telemetry",
" from monkey_island.cc.services.telemetry.processing.system_info_collectors.environment import \\",
" process_environment_telemetry",
"-from monkey_island.cc.services.telemetry.processing.system_info_collectors.hostname import process_hostname_telemetry",
" from monkey_island.cc.services.telemetry.processing.system_info_collectors.scoutsuite import \\",
" process_scout_suite_telemetry",
" from monkey_island.cc.services.telemetry.zero_trust_checks.antivirus_existence import check_antivirus_existence"
"-from monkey_island.cc.services.telemetry.processing.system_info_collectors.hostname import \\",
"- process_hostname_telemetry",
" from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import \\",
" test_antivirus_existence",
" "
]
},
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -16,7 +15,6 @@",
"@@ -19,7 +16,6 @@",
" SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS = {",
" AWS_COLLECTOR: [process_aws_telemetry],",
" ENVIRONMENT_COLLECTOR: [process_environment_telemetry],",
"- HOSTNAME_COLLECTOR: [process_hostname_telemetry],",
" PROCESS_LIST_COLLECTOR: [check_antivirus_existence],",
" SCOUTSUITE_COLLECTOR: [process_scout_suite_telemetry]",
" }"
" PROCESS_LIST_COLLECTOR: [test_antivirus_existence]",
" }",
" "
]
}
]
}
},
"app_version": "0.2.1",
"file_version": "1.0.3"
}
"app_version": "0.3.5-1",
"file_version": "1.0.4"
}

View File

@ -4,18 +4,19 @@
"dod": "You should implement a new PBA in Monkey which schedules jobs on the machine.",
"description": "You need to implement the `ScheduleJobs` PBA which creates scheduled jobs on the machine. <br><br>\n<img src=\"https://media.giphy.com/media/l0K4mVE5b5WZ1sctW/giphy.gif\" height=175><br><br>\nThe commands that add scheduled jobs for Windows and Linux can be retrieved from `get_commands_to_schedule_jobs` — make sure you understand how to use this function correctly.\n\n## Manual test \nOnce you think you're done...\n- Run the Monkey Island\n- Make sure the \"Job scheduling\" PBA is enabled in the \"Monkey\" tab in the configuration — for this test, disable network scanning, exploiting, and all other PBAs\n- Run the Monkey\n- Make sure you see the PBA with its results in the Security report as well as in the ATT&CK report under the relevant MITRE technique\n\n<img src=\"https://firebasestorage.googleapis.com/v0/b/swimmio-content/o/repositories%2F6Nlb99NtY5Fc3bSd8suH%2Fimg%2Ff0e53e6c-9dbe-41d8-9454-2b5761c3f53a.png?alt=media&token=21aa4bb8-7ebe-4dab-a739-c77e059144dd\" height=400>\n<br><br>\n<img src=\"https://firebasestorage.googleapis.com/v0/b/swimmio-content/o/repositories%2F6Nlb99NtY5Fc3bSd8suH%2Fimg%2F528389a0-35c8-4380-b6e2-353068ed01e4.png?alt=media&token=08767f55-86e2-4f51-8ecf-13fd6cc25ad5\" height=400>",
"summary": "Many other PBAs are as simple as this one, using shell commands or scripts — see `Timestomping` and `AccountDiscovery`. <br><br>\n\nHowever, for less straightforward ones, you can override functions and implement new classes depending on what is required — see `SignedScriptProxyExecution` and `ModifyShellStartupFiles`.<br><br>\n\nThis PBA, along with all the other PBAs, will run on a system after it has been breached. The purpose of this code is to test whether target systems allow attackers to schedule jobs, which they could use to run malicious code at some specified date and time.",
"diff": "diff --git a/monkey/infection_monkey/post_breach/actions/schedule_jobs.py b/monkey/infection_monkey/post_breach/actions/schedule_jobs.py\nindex d6cdd276..79a7724d 100644\n--- a/monkey/infection_monkey/post_breach/actions/schedule_jobs.py\n+++ b/monkey/infection_monkey/post_breach/actions/schedule_jobs.py\n@@ -10,11 +10,5 @@\n \"\"\"\n \n def __init__(self):\n- linux_cmds, windows_cmds = get_commands_to_schedule_jobs()\n+ pass\n-\n+ # Swimmer: IMPLEMENT HERE!\n- super(ScheduleJobs, self).__init__(name=POST_BREACH_JOB_SCHEDULING,\n- linux_cmd=' '.join(linux_cmds),\n- windows_cmd=windows_cmds)\n- \n- def run(self):\n- super(ScheduleJobs, self).run()\n",
"hunksOrder": [
"monkey/infection_monkey/post_breach/actions/schedule_jobs.py_0"
],
"tests": [],
"hints": [
"Check out the `Timestomping` PBA to get an idea about the implementation.",
"Don't forget to add code to remove the scheduled jobs!"
],
"app_version": "0.2.8",
"file_version": "1.0.4",
"play_mode": "all",
"swimmPatch": {
"monkey/infection_monkey/post_breach/actions/schedule_jobs.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/infection_monkey/post_breach/actions/schedule_jobs.py b/monkey/infection_monkey/post_breach/actions/schedule_jobs.py\nindex d6cdd276..79a7724d 100644\n--- a/monkey/infection_monkey/post_breach/actions/schedule_jobs.py\n+++ b/monkey/infection_monkey/post_breach/actions/schedule_jobs.py",
"fileDiffHeader": "diff --git a/monkey/infection_monkey/post_breach/actions/schedule_jobs.py b/monkey/infection_monkey/post_breach/actions/schedule_jobs.py\nindex f7d8d805..06839463 100644\n--- a/monkey/infection_monkey/post_breach/actions/schedule_jobs.py\n+++ b/monkey/infection_monkey/post_breach/actions/schedule_jobs.py",
"hunks": [
{
"swimmHunkMetadata": {
@ -41,6 +42,8 @@
]
}
},
"app_version": "0.3.5-1",
"file_version": "1.0.4",
"hunksOrder": [
"monkey/infection_monkey/post_breach/actions/schedule_jobs.py_0"
],

View File

@ -4,23 +4,34 @@
"dod": "You should add a new PBA to the Monkey which creates a new user on the machine.",
"description": "Read [our documentation about adding a new PBA](https://www.guardicore.com/infectionmonkey/docs/development/adding-post-breach-actions/).\n\nAfter that we want you to add the BackdoorUser PBA. The commands that add users for Win and Linux can be retrieved from `get_commands_to_add_user` - make sure you see how to use this function correctly. \n\nNote that the PBA should impact the T1136 MITRE technique as well! \n\n# Manual test to confirm\n\n1. Run the Monkey Island\n2. Make sure your new PBA is enabled by default in the config - for this test, disable network scanning, exploiting, and all other PBAs\n3. Run Monkey\n4. See the PBA in the security report\n5, See the PBA in the MITRE report in the relevant technique\n",
"summary": "Take a look at the configuration of the island again - see the \"command to run after breach\" option we offer the user? It's implemented exactly like you did right now but each user can do it for themselves. \n\nHowever, what if the PBA needs to do stuff which is more complex than just running a few commands? In that case... ",
"hunksOrder": [
"monkey/common/data/post_breach_consts.py_0",
"monkey/infection_monkey/post_breach/actions/add_user.py_0",
"monkey/monkey_island/cc/services/attack/technique_reports/T1136.py_0",
"monkey/monkey_island/cc/services/attack/technique_reports/T1136.py_1",
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py_0"
],
"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 T1136 articulates that adversaries may create an account to maintain access to victim systems, therefore, the BackdoorUser PBA is relevant to it. Make sure to map this PBA to the MITRE ATT&CK configuration and report."
],
"play_mode": "all",
"swimmPatch": {
"monkey/common/common_consts/post_breach_consts.py": {
"monkey/common/data/post_breach_consts.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/common/common_consts/post_breach_consts.py b/monkey/common/common_consts/post_breach_consts.py\nindex 25e6679c..5198f006 100644\n--- a/monkey/common/common_consts/post_breach_consts.py\n+++ b/monkey/common/common_consts/post_breach_consts.py",
"fileDiffHeader": "diff --git a/monkey/common/data/post_breach_consts.py b/monkey/common/data/post_breach_consts.py\nindex 25e6679c..05980288 100644\n--- a/monkey/common/data/post_breach_consts.py\n+++ b/monkey/common/data/post_breach_consts.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -1,5 +1,4 @@",
"@@ -1,5 +1,5 @@",
" POST_BREACH_COMMUNICATE_AS_NEW_USER = \"Communicate as new user\"",
"-POST_BREACH_BACKDOOR_USER = \"Backdoor user\"",
"+# Swimmer: PUT THE NEW CONST HERE!",
" POST_BREACH_FILE_EXECUTION = \"File execution\"",
" POST_BREACH_SHELL_STARTUP_FILE_MODIFICATION = \"Modify shell startup file\"",
" POST_BREACH_HIDDEN_FILES = \"Hide files and directories\""
@ -30,13 +41,15 @@
},
"monkey/infection_monkey/post_breach/actions/add_user.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py\nindex a8584584..d8476a97 100644\n--- a/monkey/infection_monkey/post_breach/actions/add_user.py\n+++ b/monkey/infection_monkey/post_breach/actions/add_user.py",
"fileDiffHeader": "diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py\nindex 58be89a1..d8476a97 100644\n--- a/monkey/infection_monkey/post_breach/actions/add_user.py\n+++ b/monkey/infection_monkey/post_breach/actions/add_user.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -1,15 +1,7 @@",
"-from common.common_consts.post_breach_consts import POST_BREACH_BACKDOOR_USER",
"-from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER",
"-from infection_monkey.config import WormConfiguration",
" from infection_monkey.post_breach.pba import PBA",
" from infection_monkey.utils.users import get_commands_to_add_user",
@ -58,23 +71,28 @@
},
"monkey/monkey_island/cc/services/attack/technique_reports/T1136.py": {
"diffType": "MODIFIED",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py\nindex d9d86e08..0abe6a02 100644\n--- a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py\n+++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py",
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py\nindex 086a1c13..9f23bb8d 100644\n--- a/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py\n+++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1136.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -1,4 +1,4 @@",
"-from common.common_consts.post_breach_consts import POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER",
"+from common.common_consts.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER",
" from monkey_island.cc.services.attack.technique_reports.pba_technique import PostBreachTechnique",
" ",
" __author__ = \"shreyamalviya\""
"@@ -1,5 +1,5 @@",
" from common.data.post_breach_consts import (",
"- POST_BREACH_BACKDOOR_USER, POST_BREACH_COMMUNICATE_AS_NEW_USER)",
"+ POST_BREACH_COMMUNICATE_AS_NEW_USER)",
" from monkey_island.cc.services.attack.technique_reports.pba_technique import \\",
" PostBreachTechnique",
" "
]
},
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -9,4 +9,4 @@",
"@@ -11,4 +11,4 @@",
" unscanned_msg = \"Monkey didn't try creating a new user on the network's systems.\"",
" scanned_msg = \"Monkey tried creating a new user on the network's systems, but failed.\"",
" used_msg = \"Monkey created a new user on the network's systems.\"",
@ -89,9 +107,11 @@
"fileDiffHeader": "diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py\nindex f1fe0f6f..39ebd33a 100644\n--- a/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py\n+++ b/monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py",
"hunks": [
{
"swimmHunkMetadata": {},
"swimmHunkMetadata": {
"hunkComments": []
},
"hunkDiffLines": [
"@@ -4,15 +4,7 @@",
"@@ -4,16 +4,7 @@",
" \"might do after breaching a new machine. Used in ATT&CK and Zero trust reports.\",",
" \"type\": \"string\",",
" \"anyOf\": [",
@ -102,6 +122,7 @@
"- \"BackdoorUser\"",
"- ],",
"- \"title\": \"Back door user\",",
"- \"safe\": True,",
"- \"info\": \"Attempts to create a new user on the system and delete it afterwards.\",",
"- \"attack_techniques\": [\"T1136\"]",
"- },",
@ -113,6 +134,14 @@
]
}
},
"app_version": "0.2.1",
"file_version": "1.0.3"
}
"app_version": "0.3.5-1",
"file_version": "1.0.4",
"hunksOrder": [
"monkey/common/data/post_breach_consts.py_0",
"monkey/infection_monkey/post_breach/actions/add_user.py_0",
"monkey/monkey_island/cc/services/attack/technique_reports/T1136.py_0",
"monkey/monkey_island/cc/services/attack/technique_reports/T1136.py_1",
"monkey/monkey_island/cc/services/config_schema/definitions/post_breach_actions.py_0"
],
"last_commit_sha_for_swimm_patch": "9d9e8168fb2c23367b9947273aa1a041687b3e2e"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -7,6 +7,7 @@ class VictimHost(object):
self.domain_name = str(domain_name)
self.os = {}
self.services = {}
self.icmp = False
self.monkey_exe = None
self.default_tunnel = None
self.default_server = None
@ -40,7 +41,7 @@ class VictimHost(object):
victim += "] Services - ["
for k, v in list(self.services.items()):
victim += "%s-%s " % (k, v)
victim += '] '
victim += '] ICMP: %s ' % (self.icmp)
victim += "target monkey: %s" % self.monkey_exe
return victim

View File

@ -62,6 +62,9 @@ class PingScanner(HostScanner, HostFinger):
host.os['type'] = 'linux'
else: # as far we we know, could also be OSX/BSD but lets handle that when it comes up.
host.os['type'] = 'windows'
host.icmp = True
return True
except Exception as exc:
LOG.debug("Error parsing ping fingerprint: %s", exc)

View File

@ -12,6 +12,7 @@ EXPLOITER_CLASSES = {
"SmbExploiter"
],
"title": "SMB Exploiter",
"safe": True,
"attack_techniques": ["T1110", "T1075", "T1035"],
"info": "Brute forces using credentials provided by user and"
" hashes gathered by mimikatz.",
@ -23,6 +24,7 @@ EXPLOITER_CLASSES = {
"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.",
@ -34,6 +36,7 @@ EXPLOITER_CLASSES = {
"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.",
@ -44,7 +47,8 @@ EXPLOITER_CLASSES = {
"enum": [
"Ms08_067_Exploiter"
],
"title": "MS08-067 Exploiter (UNSAFE)",
"title": "MS08-067 Exploiter",
"safe": False,
"info": "Unsafe exploiter, that might cause system crash due to the use of buffer overflow. "
"Uses MS08-067 vulnerability.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/ms08-067/"
@ -55,6 +59,7 @@ EXPLOITER_CLASSES = {
"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/"
@ -65,6 +70,7 @@ EXPLOITER_CLASSES = {
"ShellShockExploiter"
],
"title": "ShellShock Exploiter",
"safe": True,
"info": "CVE-2014-6271, based on logic from "
"https://github.com/nccgroup/shocker/blob/master/shocker.py .",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/shellshock/"
@ -75,6 +81,7 @@ EXPLOITER_CLASSES = {
"SambaCryExploiter"
],
"title": "SambaCry Exploiter",
"safe": True,
"info": "Bruteforces and searches for anonymous shares. Uses Impacket.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/sambacry/"
},
@ -84,6 +91,7 @@ EXPLOITER_CLASSES = {
"ElasticGroovyExploiter"
],
"title": "ElasticGroovy Exploiter",
"safe": True,
"info": "CVE-2015-1427. Logic is based on Metasploit module.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/elasticgroovy/"
},
@ -93,6 +101,7 @@ EXPLOITER_CLASSES = {
"Struts2Exploiter"
],
"title": "Struts2 Exploiter",
"safe": True,
"info": "Exploits struts2 java web framework. CVE-2017-5638. Logic based on "
"https://www.exploit-db.com/exploits/41570 .",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/struts2/"
@ -103,6 +112,7 @@ EXPLOITER_CLASSES = {
"WebLogicExploiter"
],
"title": "WebLogic Exploiter",
"safe": True,
"info": "Exploits CVE-2017-10271 and CVE-2019-2725 vulnerabilities on WebLogic server.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/weblogic/"
},
@ -112,6 +122,7 @@ EXPLOITER_CLASSES = {
"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/"
@ -122,6 +133,7 @@ EXPLOITER_CLASSES = {
"VSFTPDExploiter"
],
"title": "VSFTPD Exploiter",
"safe": True,
"info": "Exploits a malicious backdoor that was added to the VSFTPD download archive. "
"Logic based on Metasploit module.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/vsftpd/"
@ -132,6 +144,7 @@ EXPLOITER_CLASSES = {
"DrupalExploiter"
],
"title": "Drupal Exploiter",
"safe": True,
"info": "Exploits a remote command execution vulnerability in a Drupal server,"
"for which certain modules (such as RESTful Web Services) are enabled.",
"link": "https://www.guardicore.com/infectionmonkey/docs/reference/exploiters/drupal/"

View File

@ -10,6 +10,7 @@ FINGER_CLASSES = {
"SMBFinger"
],
"title": "SMBFinger",
"safe": True,
"info": "Figures out if SMB is running and what's the version of it.",
"attack_techniques": ["T1210"]
},
@ -19,6 +20,7 @@ FINGER_CLASSES = {
"SSHFinger"
],
"title": "SSHFinger",
"safe": True,
"info": "Figures out if SSH is running.",
"attack_techniques": ["T1210"]
},
@ -28,6 +30,7 @@ FINGER_CLASSES = {
"PingScanner"
],
"title": "PingScanner",
"safe": True,
"info": "Tries to identify if host is alive and which OS it's running by ping scan."
},
{
@ -36,6 +39,7 @@ FINGER_CLASSES = {
"HTTPFinger"
],
"title": "HTTPFinger",
"safe": True,
"info": "Checks if host has HTTP/HTTPS ports open."
},
{
@ -44,6 +48,7 @@ FINGER_CLASSES = {
"MySQLFinger"
],
"title": "MySQLFinger",
"safe": True,
"info": "Checks if MySQL server is running and tries to get it's version.",
"attack_techniques": ["T1210"]
},
@ -53,6 +58,7 @@ FINGER_CLASSES = {
"MSSQLFinger"
],
"title": "MSSQLFinger",
"safe": True,
"info": "Checks if Microsoft SQL service is running and tries to gather information about it.",
"attack_techniques": ["T1210"]
},
@ -62,6 +68,7 @@ FINGER_CLASSES = {
"ElasticFinger"
],
"title": "ElasticFinger",
"safe": True,
"info": "Checks if ElasticSearch is running and attempts to find it's version.",
"attack_techniques": ["T1210"]
},
@ -71,6 +78,7 @@ FINGER_CLASSES = {
"WindowsServerFinger"
],
"title": "WindowsServerFinger",
"safe": True,
"info": "Checks if server is a Windows Server and tests if it is vulnerable to Zerologon.",
"attack_techniques": ["T1210"]
}

View File

@ -10,6 +10,7 @@ POST_BREACH_ACTIONS = {
"BackdoorUser"
],
"title": "Back door user",
"safe": True,
"info": "Attempts to create a new user on the system and delete it afterwards.",
"attack_techniques": ["T1136"]
},
@ -19,6 +20,7 @@ POST_BREACH_ACTIONS = {
"CommunicateAsNewUser"
],
"title": "Communicate as new user",
"safe": True,
"info": "Attempts to create a new user, create HTTPS requests as that user and delete the user "
"afterwards.",
"attack_techniques": ["T1136"]
@ -29,6 +31,7 @@ POST_BREACH_ACTIONS = {
"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"]
@ -39,6 +42,7 @@ POST_BREACH_ACTIONS = {
"HiddenFiles"
],
"title": "Hidden files and directories",
"safe": True,
"info": "Attempts to create a hidden file and remove it afterward.",
"attack_techniques": ["T1158"]
},
@ -48,6 +52,7 @@ POST_BREACH_ACTIONS = {
"TrapCommand"
],
"title": "Trap",
"safe": True,
"info": "On Linux systems, attempts to trap an interrupt signal in order to execute a command "
"upon receiving that signal. Removes the trap afterwards.",
"attack_techniques": ["T1154"]
@ -58,6 +63,7 @@ POST_BREACH_ACTIONS = {
"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"]
@ -68,6 +74,7 @@ POST_BREACH_ACTIONS = {
"ScheduleJobs"
],
"title": "Job scheduling",
"safe": True,
"info": "Attempts to create a scheduled job on the system and remove it.",
"attack_techniques": ["T1168", "T1053"]
},
@ -77,6 +84,7 @@ POST_BREACH_ACTIONS = {
"Timestomping"
],
"title": "Timestomping",
"safe": True,
"info": "Creates a temporary file and attempts to modify its time attributes. Removes the file afterwards.",
"attack_techniques": ["T1099"]
},
@ -86,7 +94,8 @@ POST_BREACH_ACTIONS = {
"SignedScriptProxyExecution"
],
"title": "Signed script proxy execution",
"info": "On Windows systems, attemps to execute an arbitrary file "
"safe": False,
"info": "On Windows systems, attempts to execute an arbitrary file "
"with the help of a pre-existing signed script.",
"attack_techniques": ["T1216"]
},
@ -96,6 +105,7 @@ POST_BREACH_ACTIONS = {
"AccountDiscovery"
],
"title": "Account Discovery",
"safe": True,
"info": "Attempts to get a listing of user accounts on the system.",
"attack_techniques": ["T1087"]
},
@ -105,6 +115,7 @@ POST_BREACH_ACTIONS = {
"ClearCommandHistory"
],
"title": "Clear command history",
"safe": False,
"info": "Attempts to clear the command history.",
"attack_techniques": ["T1146"]
}

View File

@ -13,6 +13,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = {
ENVIRONMENT_COLLECTOR
],
"title": "Environment collector",
"safe": True,
"info": "Collects information about machine's environment (on premise/GCP/AWS).",
"attack_techniques": ["T1082"]
},
@ -22,6 +23,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = {
MIMIKATZ_COLLECTOR
],
"title": "Mimikatz collector",
"safe": True,
"info": "Collects credentials from Windows credential manager.",
"attack_techniques": ["T1003", "T1005"]
},
@ -31,8 +33,8 @@ SYSTEM_INFO_COLLECTOR_CLASSES = {
AWS_COLLECTOR
],
"title": "AWS collector",
"info": "If on AWS, collects more information about the AWS instance currently running on. "
"Also responsible for running ScoutSuite cloud security check.",
"safe": True,
"info": "If on AWS, collects more information about the AWS instance currently running on.",
"attack_techniques": ["T1082"]
},
{
@ -41,6 +43,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = {
HOSTNAME_COLLECTOR
],
"title": "Hostname collector",
"safe": True,
"info": "Collects machine's hostname.",
"attack_techniques": ["T1082", "T1016"]
},
@ -50,6 +53,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = {
PROCESS_LIST_COLLECTOR
],
"title": "Process list collector",
"safe": True,
"info": "Collects a list of running processes on the machine.",
"attack_techniques": ["T1082"]
},
@ -59,6 +63,7 @@ SYSTEM_INFO_COLLECTOR_CLASSES = {
AZURE_CRED_COLLECTOR
],
"title": "Azure credential collector",
"safe": True,
"info": "Collects password credentials from Azure VMs",
"attack_techniques": ["T1003", "T1005"]
}

View File

@ -510,6 +510,7 @@ class ReportService:
'hostname': monkey['hostname'],
'target': target_ip,
'services': scan['data']['machine']['services'],
'icmp': scan['data']['machine']['icmp'],
'is_self': False
})
@ -544,7 +545,7 @@ class ReportService:
@staticmethod
def get_cross_segment_issues():
scans = mongo.db.telemetry.find({'telem_category': 'scan'},
{'monkey_guid': 1, 'data.machine.ip_addr': 1, 'data.machine.services': 1})
{'monkey_guid': 1, 'data.machine.ip_addr': 1, 'data.machine.services': 1, 'data.machine.icmp': 1})
cross_segment_issues = []

View File

@ -14,7 +14,7 @@ class T1003 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<StolenPasswordsComponent

View File

@ -28,7 +28,7 @@ class T1005 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -28,7 +28,7 @@ class T1016 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -42,7 +42,7 @@ class T1018 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -41,7 +41,7 @@ class T1021 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -13,7 +13,7 @@ class T1035 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.services.length !== 0 ?
<ReactTable

View File

@ -22,7 +22,7 @@ class T1041 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1053 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -30,7 +30,7 @@ class T1059 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -13,7 +13,7 @@ class T1064 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.scripts.length !== 0 ?
<ReactTable

View File

@ -7,7 +7,7 @@ class T1065 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<MitigationsComponent mitigations={this.props.data.mitigations}/>
</div>
);

View File

@ -33,7 +33,7 @@ class T1075 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -32,7 +32,7 @@ class T1082 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -35,7 +35,7 @@ class T1086 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1087 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -25,7 +25,7 @@ class T1090 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1099 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -24,7 +24,7 @@ class T1105 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status !== ScanStatus.UNSCANNED ?
<ReactTable

View File

@ -13,7 +13,7 @@ class T1106 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.api_uses.length !== 0 ?
<ReactTable

View File

@ -38,7 +38,7 @@ class T1107 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.deleted_files.length !== 0 ?
<ReactTable

View File

@ -38,7 +38,7 @@ class T1110 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status !== ScanStatus.UNSCANNED ?
<ReactTable

View File

@ -12,7 +12,7 @@ class T1129 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.dlls.length !== 0 ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1136 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -41,7 +41,7 @@ class T1145 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1146 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1154 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1156 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1158 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1166 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1168 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -39,7 +39,7 @@ class T1188 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -45,7 +45,7 @@ class T1210 extends React.Component {
return (
<div className="data-table-container">
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
{this.props.data.bits_jobs.length > 0 ? <div>BITS jobs were used in these machines: </div> : ''}
</div>
<br/>

View File

@ -95,7 +95,7 @@ class T1210 extends React.Component {
let scanned_services = this.props.data.scanned_services.map(T1210.formatScanned).flat();
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
{scanned_services.length > 0 ?
this.renderScannedServices(scanned_services) : ''}
{this.props.data.exploited_services.length > 0 ?

View File

@ -27,7 +27,7 @@ class T1216 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -23,7 +23,7 @@ class T1222 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -27,7 +27,7 @@ class T1504 extends React.Component {
render() {
return (
<div>
<div>{this.props.data.message}</div>
<div>{this.props.data.message_html}</div>
<br/>
{this.props.data.status === ScanStatus.USED ?
<ReactTable

View File

@ -15,7 +15,7 @@ const renderTime = (val) => val.split('.')[0];
const columns = [
{title: 'Time', prop: 'timestamp', render: renderTime},
{title: 'Monkey', prop: 'monkey'},
{title: 'Type', prop: 'telem_catagory'},
{title: 'Type', prop: 'telem_category'},
{title: 'Details', prop: 'data', render: renderJson, width: '40%'}
];

View File

@ -157,9 +157,9 @@ class AttackReport extends React.Component {
}
}
}
// modify techniques' messages
// compiles techniques' message string from markdown to HTML
for (const tech_id in techniques){
techniques[tech_id]['message'] = <div dangerouslySetInnerHTML={{__html: marked(techniques[tech_id]['message'])}} />;
techniques[tech_id]['message_html'] = <div dangerouslySetInnerHTML={{__html: marked(techniques[tech_id]['message'])}} />;
}
return techniques

View File

@ -451,25 +451,64 @@ class ReportPageComponent extends AuthComponent {
}
generateCrossSegmentIssue(crossSegmentIssue) {
let crossSegmentIssueOverview = 'Communication possible from ' + crossSegmentIssue['source_subnet'] + ' to ' + crossSegmentIssue['target_subnet']
return <li key={crossSegmentIssueOverview}>
{crossSegmentIssueOverview}
<CollapsibleWellComponent>
let crossSegmentIssueOverview = 'Communication possible from '
+ `${crossSegmentIssue['source_subnet']} to ${crossSegmentIssue['target_subnet']}`;
return (
<li key={crossSegmentIssueOverview}>
{crossSegmentIssueOverview}
<CollapsibleWellComponent>
<ul className='cross-segment-issues'>
{crossSegmentIssue['issues'].map(
issue => this.generateCrossSegmentIssueListItem(issue)
)}
</ul>
</CollapsibleWellComponent>
</li>
);
}
generateCrossSegmentIssueListItem(issue) {
if (issue['is_self']) {
return this.generateCrossSegmentSingleHostMessage(issue);
}
return this.generateCrossSegmentMultiHostMessage(issue);
}
generateCrossSegmentSingleHostMessage(issue) {
return (
<li key={issue['hostname']}>
{`Machine ${issue['hostname']} has both ips: ${issue['source']} and ${issue['target']}`}
</li>
);
}
generateCrossSegmentMultiHostMessage(issue) {
return (
<li key={issue['source'] + issue['target']}>
IP {issue['source']} ({issue['hostname']}) was able to communicate with
IP {issue['target']} using:
<ul>
{crossSegmentIssue['issues'].map(x =>
x['is_self'] ?
<li key={x['hostname']}>
{'Machine ' + x['hostname'] + ' has both ips: ' + x['source'] + ' and ' + x['target']}
</li>
:
<li key={x['source'] + x['target']}>
{'IP ' + x['source'] + ' (' + x['hostname'] + ') connected to IP ' + x['target']
+ ' using the services: ' + Object.keys(x['services']).join(', ')}
</li>
)}
{issue['icmp'] && <li key='icmp'>ICMP</li>}
{this.generateCrossSegmentServiceListItems(issue)}
</ul>
</CollapsibleWellComponent>
</li>;
</li>
);
}
generateCrossSegmentServiceListItems(issue) {
let service_list_items = [];
for (const [service, info] of Object.entries(issue['services'])) {
service_list_items.push(
<li key={service}>
<span className='cross-segment-service'>{service}</span> ({info['display_name']})
</li>
);
}
return service_list_items;
}
generateShellshockPathListBadges(paths) {

View File

@ -1,121 +1,192 @@
import React, {useState} from 'react';
import React from 'react';
import {Button, Card} from 'react-bootstrap';
import {Card, Button, Form} from 'react-bootstrap';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheckSquare} from '@fortawesome/free-solid-svg-icons';
import {faSquare} from '@fortawesome/free-regular-svg-icons';
import {cloneDeep} from 'lodash';
import {getComponentHeight} from './utils/HeightCalculator';
import {resolveObjectPath} from './utils/ObjectPathResolver';
import InfoPane from './InfoPane';
import {getDefaultPaneParams, InfoPane, WarningType} from './InfoPane';
import {MasterCheckbox, MasterCheckboxState} from './MasterCheckbox';
import ChildCheckboxContainer from './ChildCheckbox';
import {getFullDefinitionByKey} from './JsonSchemaHelpers';
function getSelectValuesAfterClick(valueArray, clickedValue) {
if (valueArray.includes(clickedValue)) {
return valueArray.filter((e) => {
return e !== clickedValue;
});
} else {
valueArray.push(clickedValue);
return valueArray;
}
}
function onMasterCheckboxClick(checkboxValue, defaultArray, onChangeFnc) {
if (checkboxValue) {
onChangeFnc([]);
} else {
onChangeFnc(defaultArray);
}
}
// Definitions passed to components only contains value and label,
// custom fields like "info" or "links" must be pulled from registry object using this function
function getFullDefinitionsFromRegistry(refString, registry) {
return getObjectFromRegistryByRef(refString, registry).anyOf;
}
function getObjectFromRegistryByRef(refString, registry) {
let refArray = refString.replace('#', '').split('/');
return resolveObjectPath(refArray, registry);
}
function getFullDefinitionByKey(refString, registry, itemKey) {
let fullArray = getFullDefinitionsFromRegistry(refString, registry);
return fullArray.filter(e => (e.enum[0] === itemKey))[0];
}
function setPaneInfo(refString, registry, itemKey, setPaneInfoFnc) {
let definitionObj = getFullDefinitionByKey(refString, registry, itemKey);
setPaneInfoFnc({title: definitionObj.title, content: definitionObj.info, link: definitionObj.link});
}
function getDefaultPaneParams(refString, registry) {
let configSection = getObjectFromRegistryByRef(refString, registry);
return ({title: configSection.title, content: configSection.description});
}
function AdvancedMultiSelect(props) {
const [masterCheckbox, setMasterCheckbox] = useState(true);
function AdvancedMultiSelectHeader(props) {
const {
schema,
id,
options,
value,
required,
disabled,
readonly,
multiple,
autofocus,
onChange,
registry
title,
onCheckboxClick,
checkboxState,
hideReset,
onResetClick
} = props;
const {enumOptions} = options;
const [infoPaneParams, setInfoPaneParams] = useState(getDefaultPaneParams(schema.items.$ref, registry));
getDefaultPaneParams(schema.items.$ref, registry);
const selectValue = cloneDeep(value);
return (
<div className={'advanced-multi-select'}>
<Card.Header>
<Button key={`${props.schema.title}-button`} value={value}
variant={'link'} disabled={disabled}
onClick={() => {
onMasterCheckboxClick(masterCheckbox, schema.default, onChange);
setMasterCheckbox(!masterCheckbox);
}}
>
<FontAwesomeIcon icon={masterCheckbox ? faCheckSquare : faSquare}/>
</Button>
<span className={'header-title'}>{props.schema.title}</span>
</Card.Header>
<Form.Group
style={{height: `${getComponentHeight(enumOptions.length)}px`}}
id={id}
multiple={multiple}
className='choice-block form-control'
required={required}
disabled={disabled || readonly}
autoFocus={autofocus}>
{enumOptions.map(({value, label}, i) => {
return (
<Form.Group
key={i}
onClick={() => setPaneInfo(schema.items.$ref, registry, value, setInfoPaneParams)}>
<Button value={value} variant={'link'} disabled={disabled}
onClick={() => onChange(getSelectValuesAfterClick(selectValue, value))}>
<FontAwesomeIcon icon={selectValue.includes(value) ? faCheckSquare : faSquare}/>
</Button>
<span className={'option-text'}>
{label}
</span>
</Form.Group>
);
})}
</Form.Group>
<InfoPane title={infoPaneParams.title} body={infoPaneParams.content} link={infoPaneParams.link}/>
</div>
<Card.Header className="d-flex justify-content-between">
<MasterCheckbox title={title} onClick={onCheckboxClick} checkboxState={checkboxState}/>
<Button className={'reset-safe-defaults'} type={'reset'} variant={'warning'}
hidden={hideReset} onClick={onResetClick}>
Reset to safe defaults
</Button>
</Card.Header>
);
}
class AdvancedMultiSelect extends React.Component {
constructor(props) {
super(props);
this.defaultValues = props.schema.default;
this.infoPaneRefString = props.schema.items.$ref;
this.registry = props.registry;
this.enumOptions = props.options.enumOptions.sort(this.compareOptions);
this.state = {
masterCheckboxState: this.getMasterCheckboxState(props.value),
hideReset: this.getHideResetState(props.value),
infoPaneParams: getDefaultPaneParams(
this.infoPaneRefString,
this.registry,
this.isUnsafeOptionSelected(this.props.value)
)
};
}
// Sort options alphabetically. "Unsafe" options float to the bottom"
compareOptions = (a, b) => {
// Apparently, you can use additive operators with boolean types. Ultimately,
// the ToNumber() abstraction operation is called to convert the booleans to
// numbers: https://tc39.es/ecma262/#sec-tonumeric
if (this.isSafe(b.value) - this.isSafe(a.value) !== 0) {
return this.isSafe(b.value) - this.isSafe(a.value);
}
return a.value.localeCompare(b.value);
}
onMasterCheckboxClick = () => {
if (this.state.masterCheckboxState === MasterCheckboxState.ALL) {
var newValues = [];
} else {
newValues = this.enumOptions.map(({value}) => value);
}
this.props.onChange(newValues);
this.setMasterCheckboxState(newValues);
this.setHideResetState(newValues);
this.setPaneInfoToDefault(this.isUnsafeOptionSelected(newValues));
}
onChildCheckboxClick = (value) => {
let selectValues = this.getSelectValuesAfterClick(value);
this.props.onChange(selectValues);
this.setMasterCheckboxState(selectValues);
this.setHideResetState(selectValues);
}
getSelectValuesAfterClick(clickedValue) {
const valueArray = cloneDeep(this.props.value);
if (valueArray.includes(clickedValue)) {
return valueArray.filter(e => e !== clickedValue);
} else {
valueArray.push(clickedValue);
return valueArray;
}
}
setMasterCheckboxState(selectValues) {
this.setState(() => ({
masterCheckboxState: this.getMasterCheckboxState(selectValues)
}));
}
getMasterCheckboxState(selectValues) {
if (selectValues.length === 0) {
return MasterCheckboxState.NONE;
}
if (selectValues.length !== this.enumOptions.length) {
return MasterCheckboxState.MIXED;
}
return MasterCheckboxState.ALL;
}
onResetClick = () => {
this.props.onChange(this.defaultValues);
this.setHideResetState(this.defaultValues);
this.setMasterCheckboxState(this.defaultValues);
this.setPaneInfoToDefault(this.isUnsafeOptionSelected(this.defaultValues));
}
setHideResetState(selectValues) {
this.setState(() => ({
hideReset: this.getHideResetState(selectValues)
}));
}
getHideResetState(selectValues) {
return !(this.isUnsafeOptionSelected(selectValues))
}
isUnsafeOptionSelected(selectValues) {
return !(selectValues.every((value) => this.isSafe(value)));
}
isSafe = (itemKey) => {
return getFullDefinitionByKey(this.infoPaneRefString, this.registry, itemKey).safe;
}
setPaneInfo = (itemKey) => {
let definitionObj = getFullDefinitionByKey(this.infoPaneRefString, this.registry, itemKey);
this.setState(
{
infoPaneParams: {
title: definitionObj.title,
content: definitionObj.info,
link: definitionObj.link,
warningType: this.isSafe(itemKey) ? WarningType.NONE : WarningType.SINGLE
}
}
);
}
setPaneInfoToDefault(isUnsafeOptionSelected) {
this.setState(() => ({
infoPaneParams: getDefaultPaneParams(
this.props.schema.items.$ref,
this.props.registry,
isUnsafeOptionSelected
)
}));
}
render() {
const {
schema,
id,
required,
multiple,
autofocus
} = this.props;
return (
<div className={'advanced-multi-select'}>
<AdvancedMultiSelectHeader title={schema.title}
onCheckboxClick={this.onMasterCheckboxClick}
checkboxState={this.state.masterCheckboxState}
hideReset={this.state.hideReset} onResetClick={this.onResetClick}/>
<ChildCheckboxContainer id={id} multiple={multiple} required={required}
autoFocus={autofocus} isSafe={this.isSafe}
onPaneClick={this.setPaneInfo} onCheckboxClick={this.onChildCheckboxClick}
selectedValues={this.props.value} enumOptions={this.enumOptions}/>
<InfoPane title={this.state.infoPaneParams.title}
body={this.state.infoPaneParams.content}
link={this.state.infoPaneParams.link}
warningType={this.state.infoPaneParams.warningType}/>
</div>
);
}
}
export default AdvancedMultiSelect;

View File

@ -0,0 +1,64 @@
import React from 'react';
import {Button, Form} from 'react-bootstrap';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheckSquare} from '@fortawesome/free-solid-svg-icons';
import {faSquare} from '@fortawesome/free-regular-svg-icons';
import {getComponentHeight} from './utils/HeightCalculator';
import WarningIcon from './WarningIcon';
function ChildCheckboxContainer(props) {
const {
enumOptions,
id,
multiple,
required,
autofocus,
onPaneClick,
onCheckboxClick,
selectedValues,
isSafe
} = props;
return(
<Form.Group
style={{height: `${getComponentHeight(enumOptions.length)}px`}}
id={id} multiple={multiple} className='choice-block form-control'
required={required} autoFocus={autofocus}>
{
enumOptions.map(({value, label}, i) => {
return (
<ChildCheckbox key={i} onPaneClick={onPaneClick}
onClick={onCheckboxClick} value={value}
label={label} checkboxState={selectedValues.includes(value)}
safe={isSafe(value)}/>
);
}
)}
</Form.Group>
);
}
function ChildCheckbox(props) {
const {
onPaneClick,
onClick,
value,
label,
checkboxState,
safe
} = props;
return (
<Form.Group onClick={() => onPaneClick(value)}>
<Button value={value} variant={'link'} onClick={() => onClick(value)}>
<FontAwesomeIcon icon={checkboxState ? faCheckSquare : faSquare}/>
</Button>
<span key={'label'} className={'option-text'}>{label}</span>
{!safe && <WarningIcon key="warning-icon"/>}
</Form.Group>
);
}
export default ChildCheckboxContainer;

View File

@ -3,6 +3,24 @@ import React from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons';
import {getObjectFromRegistryByRef} from './JsonSchemaHelpers';
import WarningIcon from './WarningIcon';
const WarningType = {
NONE: 0,
SINGLE: 1,
MULTIPLE: 2
}
function getDefaultPaneParams(refString, registry, isUnsafeOptionSelected) {
let configSection = getObjectFromRegistryByRef(refString, registry);
return (
{
title: configSection.title,
content: configSection.description,
warningType: isUnsafeOptionSelected ? WarningType.Multiple : WarningType.NONE
});
}
function InfoPane(props) {
return (
@ -44,9 +62,36 @@ function getSubtitle(props) {
function getBody(props) {
return (
<Card.Body className={'pane-body'}>
{props.body}
<span key={'body'}>{props.body}</span>
{props.warningType !== WarningType.NONE && getWarning(props.warningType)}
</Card.Body>
)
}
export default InfoPane
function getWarning(warningType) {
return (
<div className={'info-pane-warning'} key={'warning'}>
<WarningIcon/>{warningType === WarningType.SINGLE ? getSingleOptionWarning() : getMultipleOptionsWarning()}
</div>
);
}
function getSingleOptionWarning() {
return (
<span>This option may cause a system to become unstable or
may change a system's state in undesirable ways. Therefore, this option
is not recommended for use in production or other sensitive
environments.</span>
);
}
function getMultipleOptionsWarning() {
return (
<span>Some options have been selected that may cause a system
to become unstable or may change a system's state in undesirable ways.
Running Infection Monkey in a production or other sensitive environment
with this configuration is not recommended.</span>
);
}
export {getDefaultPaneParams, InfoPane, WarningType}

View File

@ -0,0 +1,19 @@
import {resolveObjectPath} from './utils/ObjectPathResolver';
function getFullDefinitionByKey(refString, registry, itemKey) {
let fullArray = getFullDefinitionsFromRegistry(refString, registry);
return fullArray.filter(e => (e.enum[0] === itemKey))[0];
}
// Definitions passed to components only contains value and label,
// custom fields like "info" or "links" must be pulled from registry object using this function
function getFullDefinitionsFromRegistry(refString, registry) {
return getObjectFromRegistryByRef(refString, registry).anyOf;
}
function getObjectFromRegistryByRef(refString, registry) {
let refArray = refString.replace('#', '').split('/');
return resolveObjectPath(refArray, registry);
}
export {getFullDefinitionByKey, getObjectFromRegistryByRef};

View File

@ -0,0 +1,40 @@
import React from 'react';
import {Button} from 'react-bootstrap';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheckSquare} from '@fortawesome/free-solid-svg-icons';
import {faMinusSquare} from '@fortawesome/free-solid-svg-icons';
import {faSquare} from '@fortawesome/free-regular-svg-icons';
const MasterCheckboxState = {
NONE: 0,
MIXED: 1,
ALL: 2
}
function MasterCheckbox(props) {
const {
title,
onClick,
checkboxState
} = props;
let newCheckboxIcon = faCheckSquare;
if (checkboxState === MasterCheckboxState.NONE) {
newCheckboxIcon = faSquare;
} else if (checkboxState === MasterCheckboxState.MIXED) {
newCheckboxIcon = faMinusSquare;
}
return (
<div className={'master-checkbox'}>
<Button key={`${title}-button`} variant={'link'} onClick={onClick}>
<FontAwesomeIcon icon={newCheckboxIcon}/>
</Button>
<span className={'header-title'}>{title}</span>
</div>
);
}
export {MasterCheckboxState, MasterCheckbox};

View File

@ -0,0 +1,11 @@
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons';
import React from 'react';
function WarningIcon() {
return (
<FontAwesomeIcon className="warning-icon" icon={faExclamationTriangle}/>
);
}
export default WarningIcon;

View File

@ -18,12 +18,14 @@
padding-bottom: 5px;
}
.advanced-multi-select .card-header button {
padding-top: 0;
.advanced-multi-select .card-header .master-checkbox span {
padding-bottom: 0.188rem;
}
.advanced-multi-select .card-header .header-title {
font-size: 1.2em;
display: inline-block;
vertical-align: middle;
}
.advanced-multi-select .choice-block .form-group {

View File

@ -27,3 +27,14 @@
margin: 10px 15px;
padding: 0;
}
.info-pane-warning {
margin-top: 1em;
display: flex;
}
.info-pane-warning .warning-icon {
margin-top: .188em;
margin-left: 0em;
margin-right: .75em;
}

View File

@ -57,3 +57,8 @@
white-space: pre-wrap;
}
.warning-icon {
text-transform: uppercase;
color: #FFC107;
margin-left: .75em;
}

View File

@ -76,3 +76,12 @@ div.report-wrapper .nav-tabs > .nav-item > a:hover:not(.active), .nav-tabs > .n
text-decoration: none;
background-color: $light-gray;
}
ul.cross-segment-issues {
list-style-type: none;
padding: 0px;
margin: 0px;
}
span.cross-segment-service {
text-transform: uppercase;
}