forked from p15670423/monkey
Merge remote-tracking branch 'upstream/develop' into custom_pba_bugfix
This commit is contained in:
commit
8aae611396
|
@ -1,31 +1,48 @@
|
|||
{
|
||||
"id": "VW4rf3AxRslfT7lwaug7",
|
||||
"name": "Implement a new PBA — `ScheduleJobs`",
|
||||
"dod": "WW91JTIwc2hvdWxkJTIwaW1wbGVtZW50JTIwYSUyMG5ldyUyMFBCQSUyMGluJTIwTW9ua2V5JTIwd2hpY2glMjBzY2hlZHVsZXMlMjBqb2JzJTIwb24lMjB0aGUlMjBtYWNoaW5lLg==",
|
||||
"description": "WW91JTIwbmVlZCUyMHRvJTIwaW1wbGVtZW50JTIwdGhlJTIwJTYwU2NoZWR1bGVKb2JzJTYwJTIwUEJBJTIwd2hpY2glMjBjcmVhdGVzJTIwc2NoZWR1bGVkJTIwam9icyUyMG9uJTIwdGhlJTIwbWFjaGluZS4lMjAlM0NiciUzRSUzQ2JyJTNFJTBBJTNDaW1nJTIwc3JjJTNEJTIyaHR0cHMlM0ElMkYlMkZtZWRpYS5naXBoeS5jb20lMkZtZWRpYSUyRmwwSzRtVkU1YjVXWjFzY3RXJTJGZ2lwaHkuZ2lmJTIyJTIwaGVpZ2h0JTNEMTc1JTNFJTNDYnIlM0UlM0NiciUzRSUwQVRoZSUyMGNvbW1hbmRzJTIwdGhhdCUyMGFkZCUyMHNjaGVkdWxlZCUyMGpvYnMlMjBmb3IlMjBXaW5kb3dzJTIwYW5kJTIwTGludXglMjBjYW4lMjBiZSUyMHJldHJpZXZlZCUyMGZyb20lMjAlNjBnZXRfY29tbWFuZHNfdG9fc2NoZWR1bGVfam9icyU2MCUyMCVFMiU4MCU5NCUyMG1ha2UlMjBzdXJlJTIweW91JTIwdW5kZXJzdGFuZCUyMGhvdyUyMHRvJTIwdXNlJTIwdGhpcyUyMGZ1bmN0aW9uJTIwY29ycmVjdGx5LiUwQSUwQSUyMyUyMyUyME1hbnVhbCUyMHRlc3QlMjAlMjAlMEFPbmNlJTIweW91JTIwdGhpbmslMjB5b3UncmUlMjBkb25lLi4uJTBBLSUyMFJ1biUyMHRoZSUyME1vbmtleSUyMElzbGFuZCUwQS0lMjBNYWtlJTIwc3VyZSUyMHRoZSUyMCUyMkpvYiUyMHNjaGVkdWxpbmclMjIlMjBQQkElMjBpcyUyMGVuYWJsZWQlMjBpbiUyMHRoZSUyMCUyMk1vbmtleSUyMiUyMHRhYiUyMGluJTIwdGhlJTIwY29uZmlndXJhdGlvbiUyMCVFMiU4MCU5NCUyMGZvciUyMHRoaXMlMjB0ZXN0JTJDJTIwZGlzYWJsZSUyMG5ldHdvcmslMjBzY2FubmluZyUyQyUyMGV4cGxvaXRpbmclMkMlMjBhbmQlMjBhbGwlMjBvdGhlciUyMFBCQXMlMEEtJTIwUnVuJTIwdGhlJTIwTW9ua2V5JTBBLSUyME1ha2UlMjBzdXJlJTIweW91JTIwc2VlJTIwdGhlJTIwUEJBJTIwd2l0aCUyMGl0cyUyMHJlc3VsdHMlMjBpbiUyMHRoZSUyMFNlY3VyaXR5JTIwcmVwb3J0JTIwYXMlMjB3ZWxsJTIwYXMlMjBpbiUyMHRoZSUyMEFUVCUyNkNLJTIwcmVwb3J0JTIwdW5kZXIlMjB0aGUlMjByZWxldmFudCUyME1JVFJFJTIwdGVjaG5pcXVlJTBBJTBBJTNDaW1nJTIwc3JjJTNEJTIyaHR0cHMlM0ElMkYlMkZmaXJlYmFzZXN0b3JhZ2UuZ29vZ2xlYXBpcy5jb20lMkZ2MCUyRmIlMkZzd2ltbWlvLWNvbnRlbnQlMkZvJTJGcmVwb3NpdG9yaWVzJTI1MkY2TmxiOTlOdFk1RmMzYlNkOHN1SCUyNTJGaW1nJTI1MkZmMGU1M2U2Yy05ZGJlLTQxZDgtOTQ1NC0yYjU3NjFjM2Y1M2EucG5nJTNGYWx0JTNEbWVkaWElMjZ0b2tlbiUzRDIxYWE0YmI4LTdlYmUtNGRhYi1hNzM5LWM3N2UwNTkxNDRkZCUyMiUyMGhlaWdodCUzRDQwMCUzRSUwQSUzQ2JyJTNFJTNDYnIlM0UlMEElM0NpbWclMjBzcmMlM0QlMjJodHRwcyUzQSUyRiUyRmZpcmViYXNlc3RvcmFnZS5nb29nbGVhcGlzLmNvbSUyRnYwJTJGYiUyRnN3aW1taW8tY29udGVudCUyRm8lMkZyZXBvc2l0b3JpZXMlMjUyRjZObGI5OU50WTVGYzNiU2Q4c3VIJTI1MkZpbWclMjUyRjUyODM4OWEwLTM1YzgtNDM4MC1iNmUyLTM1MzA2OGVkMDFlNC5wbmclM0ZhbHQlM0RtZWRpYSUyNnRva2VuJTNEMDg3NjdmNTUtODZlMi00ZjUxLThlY2YtMTNmZDZjYzI1YWQ1JTIyJTIwaGVpZ2h0JTNENDAwJTNF",
|
||||
"summary": "TWFueSUyMG90aGVyJTIwUEJBcyUyMGFyZSUyMGFzJTIwc2ltcGxlJTIwYXMlMjB0aGlzJTIwb25lJTJDJTIwdXNpbmclMjBzaGVsbCUyMGNvbW1hbmRzJTIwb3IlMjBzY3JpcHRzJTIwJUUyJTgwJTk0JTIwc2VlJTIwJTYwVGltZXN0b21waW5nJTYwJTIwYW5kJTIwJTYwQWNjb3VudERpc2NvdmVyeSU2MC4lMjAlM0NiciUzRSUzQ2JyJTNFJTBBJTBBSG93ZXZlciUyQyUyMGZvciUyMGxlc3MlMjBzdHJhaWdodGZvcndhcmQlMjBvbmVzJTJDJTIweW91JTIwY2FuJTIwb3ZlcnJpZGUlMjBmdW5jdGlvbnMlMjBhbmQlMjBpbXBsZW1lbnQlMjBuZXclMjBjbGFzc2VzJTIwZGVwZW5kaW5nJTIwb24lMjB3aGF0JTIwaXMlMjByZXF1aXJlZCUyMCVFMiU4MCU5NCUyMHNlZSUyMCU2MFNpZ25lZFNjcmlwdFByb3h5RXhlY3V0aW9uJTYwJTIwYW5kJTIwJTYwTW9kaWZ5U2hlbGxTdGFydHVwRmlsZXMlNjAuJTNDYnIlM0UlM0NiciUzRSUwQSUwQVRoaXMlMjBQQkElMkMlMjBhbG9uZyUyMHdpdGglMjBhbGwlMjB0aGUlMjBvdGhlciUyMFBCQXMlMkMlMjB3aWxsJTIwcnVuJTIwb24lMjBhJTIwc3lzdGVtJTIwYWZ0ZXIlMjBpdCUyMGhhcyUyMGJlZW4lMjBicmVhY2hlZC4lMjBUaGUlMjBwdXJwb3NlJTIwb2YlMjB0aGlzJTIwY29kZSUyMGlzJTIwdG8lMjB0ZXN0JTIwd2hldGhlciUyMHRhcmdldCUyMHN5c3RlbXMlMjBhbGxvdyUyMGF0dGFja2VycyUyMHRvJTIwc2NoZWR1bGUlMjBqb2JzJTJDJTIwd2hpY2glMjB0aGV5JTIwY291bGQlMjB1c2UlMjB0byUyMHJ1biUyMG1hbGljaW91cyUyMGNvZGUlMjBhdCUyMHNvbWUlMjBzcGVjaWZpZWQlMjBkYXRlJTIwYW5kJTIwdGltZS4=",
|
||||
"diff": "ZGlmZiUyMC0tZ2l0JTIwYSUyRm1vbmtleSUyRmluZmVjdGlvbl9tb25rZXklMkZwb3N0X2JyZWFjaCUyRmFjdGlvbnMlMkZzY2hlZHVsZV9qb2JzLnB5JTIwYiUyRm1vbmtleSUyRmluZmVjdGlvbl9tb25rZXklMkZwb3N0X2JyZWFjaCUyRmFjdGlvbnMlMkZzY2hlZHVsZV9qb2JzLnB5JTBBaW5kZXglMjBkNmNkZDI3Ni4uNzlhNzcyNGQlMjAxMDA2NDQlMEEtLS0lMjBhJTJGbW9ua2V5JTJGaW5mZWN0aW9uX21vbmtleSUyRnBvc3RfYnJlYWNoJTJGYWN0aW9ucyUyRnNjaGVkdWxlX2pvYnMucHklMEElMkIlMkIlMkIlMjBiJTJGbW9ua2V5JTJGaW5mZWN0aW9uX21vbmtleSUyRnBvc3RfYnJlYWNoJTJGYWN0aW9ucyUyRnNjaGVkdWxlX2pvYnMucHklMEElNDAlNDAlMjAtMTAlMkMxMCUyMCUyQjEwJTJDNSUyMCU0MCU0MCUyMGNsYXNzJTIwU2NoZWR1bGVKb2JzKFBCQSklM0ElMEElMjAlMjAlMjAlMjAlMjAlMjIlMjIlMjIlMEElMjAlMEElMjAlMjAlMjAlMjAlMjBkZWYlMjBfX2luaXRfXyhzZWxmKSUzQSUwQS0lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjBsaW51eF9jbWRzJTJDJTIwd2luZG93c19jbWRzJTIwJTNEJTIwZ2V0X2NvbW1hbmRzX3RvX3NjaGVkdWxlX2pvYnMoKSUwQS0lMEEtJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwc3VwZXIoU2NoZWR1bGVKb2JzJTJDJTIwc2VsZikuX19pbml0X18obmFtZSUzRFBPU1RfQlJFQUNIX0pPQl9TQ0hFRFVMSU5HJTJDJTBBLSUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGxpbnV4X2NtZCUzRCclMjAnLmpvaW4obGludXhfY21kcyklMkMlMEEtJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwd2luZG93c19jbWQlM0R3aW5kb3dzX2NtZHMpJTBBLSUwQS0lMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjByZW1vdmVfc2NoZWR1bGVkX2pvYnMoKSUwQSUyQiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMHBhc3MlMEElMkIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjMlMjBTd2ltbWVyJTNBJTIwSU1QTEVNRU5UJTIwSEVSRSElMEE=",
|
||||
"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",
|
||||
"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!"
|
||||
],
|
||||
"files": {
|
||||
"app_version": "0.2.8",
|
||||
"file_version": "1.0.4",
|
||||
"swimmPatch": {
|
||||
"monkey/infection_monkey/post_breach/actions/schedule_jobs.py": {
|
||||
"index": [
|
||||
"d6cdd276..79a7724d",
|
||||
"100644"
|
||||
],
|
||||
"fileA": "monkey/infection_monkey/post_breach/actions/schedule_jobs.py",
|
||||
"fileB": "monkey/infection_monkey/post_breach/actions/schedule_jobs.py",
|
||||
"status": "MODIFIED",
|
||||
"numLineDeletions": 7,
|
||||
"numLineAdditions": 2,
|
||||
"hunkContainers": [
|
||||
"JTdCJTIyaHVuayUyMiUzQSU3QiUyMmhlYWRlciUyMiUzQSUyMiU0MCU0MCUyMC0xMCUyQzEwJTIwJTJCMTAlMkM1JTIwJTQwJTQwJTIwY2xhc3MlMjBTY2hlZHVsZUpvYnMoUEJBKSUzQSUyMiUyQyUyMmNoYW5nZXMlMjIlM0ElNUIlN0IlMjJ0eXBlJTIyJTNBJTIyY29udGV4dCUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlNUMlMjIlNUMlMjIlNUMlMjIlMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0ExMCUyQyUyMmIlMjIlM0ExMCU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJjb250ZXh0JTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTExJTJDJTIyYiUyMiUzQTExJTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmNvbnRleHQlMjIlMkMlMjJkYXRhJTIyJTNBJTIyJTIwJTIwJTIwJTIwJTIwZGVmJTIwX19pbml0X18oc2VsZiklM0ElMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0ExMiUyQyUyMmIlMjIlM0ExMiU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJkZWwlMjIlMkMlMjJtYXJrJTIyJTNBJTIyLSUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjBsaW51eF9jbWRzJTJDJTIwd2luZG93c19jbWRzJTIwJTNEJTIwZ2V0X2NvbW1hbmRzX3RvX3NjaGVkdWxlX2pvYnMoKSUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTEzJTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmRlbCUyMiUyQyUyMm1hcmslMjIlM0ElMjItJTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTE0JTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmRlbCUyMiUyQyUyMm1hcmslMjIlM0ElMjItJTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMHN1cGVyKFNjaGVkdWxlSm9icyUyQyUyMHNlbGYpLl9faW5pdF9fKG5hbWUlM0RQT1NUX0JSRUFDSF9KT0JfU0NIRURVTElORyUyQyUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTE1JTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmRlbCUyMiUyQyUyMm1hcmslMjIlM0ElMjItJTIyJTJDJTIyZGF0YSUyMiUzQSUyMiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGxpbnV4X2NtZCUzRCclMjAnLmpvaW4obGludXhfY21kcyklMkMlMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0ExNiU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJkZWwlMjIlMkMlMjJtYXJrJTIyJTNBJTIyLSUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjB3aW5kb3dzX2NtZCUzRHdpbmRvd3NfY21kcyklMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0ExNyU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJkZWwlMjIlMkMlMjJtYXJrJTIyJTNBJTIyLSUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjIlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0ExOCU3RCU3RCUyQyU3QiUyMnR5cGUlMjIlM0ElMjJkZWwlMjIlMkMlMjJtYXJrJTIyJTNBJTIyLSUyMiUyQyUyMmRhdGElMjIlM0ElMjIlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjByZW1vdmVfc2NoZWR1bGVkX2pvYnMoKSUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYSUyMiUzQTE5JTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmFkZCUyMiUyQyUyMm1hcmslMjIlM0ElMjIlMkIlMjIlMkMlMjJkYXRhJTIyJTNBJTIyJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwcGFzcyUyMiUyQyUyMmxpbmVOdW1iZXJzJTIyJTNBJTdCJTIyYiUyMiUzQTEzJTdEJTdEJTJDJTdCJTIydHlwZSUyMiUzQSUyMmFkZCUyMiUyQyUyMm1hcmslMjIlM0ElMjIlMkIlMjIlMkMlMjJkYXRhJTIyJTNBJTIyJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIzJTIwU3dpbW1lciUzQSUyMElNUExFTUVOVCUyMEhFUkUhJTIyJTJDJTIybGluZU51bWJlcnMlMjIlM0ElN0IlMjJiJTIyJTNBMTQlN0QlN0QlNUQlMkMlMjJsaW5lTnVtYmVycyUyMiUzQSU3QiUyMmElMjIlM0ElN0IlMjJzdGFydExpbmUlMjIlM0ExMCUyQyUyMmxpbmVzQ291bnQlMjIlM0ExMCU3RCUyQyUyMmIlMjIlM0ElN0IlMjJzdGFydExpbmUlMjIlM0ExMCUyQyUyMmxpbmVzQ291bnQlMjIlM0E1JTdEJTdEJTdEJTdE"
|
||||
"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",
|
||||
"hunks": [
|
||||
{
|
||||
"swimmHunkMetadata": {
|
||||
"hunkComments": []
|
||||
},
|
||||
"hunkDiffLines": [
|
||||
"@@ -10,11 +10,5 @@",
|
||||
" \"\"\"",
|
||||
" ",
|
||||
" def __init__(self):",
|
||||
"- linux_cmds, windows_cmds = get_commands_to_schedule_jobs()",
|
||||
"+ pass",
|
||||
"-",
|
||||
"+ # Swimmer: IMPLEMENT HERE!",
|
||||
"- super(ScheduleJobs, self).__init__(name=POST_BREACH_JOB_SCHEDULING,",
|
||||
"- linux_cmd=' '.join(linux_cmds),",
|
||||
"- windows_cmd=windows_cmds)",
|
||||
"- ",
|
||||
"- def run(self):",
|
||||
"- super(ScheduleJobs, self).run()"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"app_version": "0.1.90",
|
||||
"file_version": "1.0.2"
|
||||
"hunksOrder": [
|
||||
"monkey/infection_monkey/post_breach/actions/schedule_jobs.py_0"
|
||||
],
|
||||
"last_commit_sha_for_swimm_patch": "44fd1ab69cfbab33cec638dcbbaa8831992a9a9f"
|
||||
}
|
|
@ -98,10 +98,11 @@ script:
|
|||
|
||||
# verify swimm
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
- wget https://firebasestorage.googleapis.com/v0/b/swimmio.appspot.com/o/Release%2Fv018%2Fswimm-0.1.8-linux-executable\?alt\=media\&token\=e59c0a18-577f-4b77-bb3b-91b22c3d8b2a -O swimm
|
||||
- wget "https://firebasestorage.googleapis.com/v0/b/swimmio.appspot.com/o/Release%2Fv029%2FSwimm_0.2.9_Setup.deb?alt=media&token=774ebd98-cb4e-4615-900c-aada224c1608" -O swimm
|
||||
- sudo dpkg -i swimm || (sudo apt-get update && sudo apt-get -f install)
|
||||
- chmod +x ./swimm
|
||||
- ./swimm --version
|
||||
- ./swimm verify
|
||||
- swimm --version
|
||||
- swimm verify
|
||||
|
||||
after_success:
|
||||
# Upload code coverage results to codecov.io, see https://github.com/codecov/codecov-bash for more information
|
||||
|
|
|
@ -39,6 +39,7 @@ Your user must have root permissions; however, don't run the script as root!
|
|||
|
||||
```sh
|
||||
wget https://raw.githubusercontent.com/guardicore/monkey/develop/deployment_scripts/deploy_linux.sh
|
||||
chmod u+x ./deploy_linux.sh
|
||||
```
|
||||
|
||||
This will download our deploy script. It's a good idea to read it quickly before executing it!
|
||||
|
@ -52,4 +53,13 @@ After downloading that script, execute it in a shell. The first argument should
|
|||
- `./deploy_linux.sh "" "master"` (deploys master branch in script directory)
|
||||
- `./deploy_linux.sh "/home/user/new" "master"` (if directory "new" is not found creates it and clones master branch into it)
|
||||
|
||||
You may also pass in an optional third `false` parameter to disable downloading the latest agent binaries.
|
||||
You may also pass in an optional third `false` parameter to disable downloading the latest agent binaries.
|
||||
|
||||
### Run on Linux
|
||||
|
||||
After the `deploy_linux.sh` script completes, you can start the monkey island.
|
||||
|
||||
```sh
|
||||
cd infection_monkey/monkey
|
||||
./monkey_island/linux/run.sh
|
||||
```
|
||||
|
|
|
@ -4,41 +4,42 @@ export MONKEY_FOLDER_NAME="infection_monkey"
|
|||
# Url of public git repository that contains monkey's source code
|
||||
export MONKEY_GIT_URL="https://github.com/guardicore/monkey"
|
||||
|
||||
get_latest_release() {
|
||||
curl --silent "https://api.github.com/repos/$1/releases/latest" | # Get latest release from GitHub API
|
||||
grep '"tag_name":' | # Get tag line
|
||||
sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value
|
||||
exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
MONKEY_LATEST_RELEASE=$(get_latest_release "monkey/guardicore")
|
||||
get_latest_release() {
|
||||
RELEASE_URL="https://api.github.com/repos/$1/releases/latest"
|
||||
|
||||
if exists wget; then
|
||||
RELEASE_INFO=$(wget --quiet -O - "$RELEASE_URL") # Get latest release from GitHub API
|
||||
else
|
||||
RELEASE_INFO=$(curl --silent "$RELEASE_URL") # Get latest release from GitHub API
|
||||
fi
|
||||
|
||||
echo "$RELEASE_INFO" |
|
||||
grep '"tag_name":' | # Get tag line
|
||||
sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value
|
||||
}
|
||||
|
||||
MONKEY_LATEST_RELEASE=$(get_latest_release "guardicore/monkey")
|
||||
|
||||
# Monkey binaries
|
||||
LINUX_32_BINARY_NAME="monkey-linux-32"
|
||||
LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/monkey-linux-32"
|
||||
export LINUX_32_BINARY_URL
|
||||
export LINUX_32_BINARY_NAME
|
||||
export LINUX_32_BINARY_NAME="monkey-linux-32"
|
||||
export LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-linux-32"
|
||||
|
||||
LINUX_64_BINARY_NAME="monkey-linux-64"
|
||||
LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/monkey-linux-64"
|
||||
export LINUX_64_BINARY_URL
|
||||
export LINUX_64_BINARY_NAME
|
||||
export LINUX_64_BINARY_NAME="monkey-linux-64"
|
||||
export LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-linux-64"
|
||||
|
||||
WINDOWS_32_BINARY_NAME="monkey-windows-32.exe"
|
||||
WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/monkey-windows-32.exe"
|
||||
export WINDOWS_32_BINARY_URL
|
||||
export WINDOWS_32_BINARY_NAME
|
||||
export WINDOWS_32_BINARY_NAME="monkey-windows-32.exe"
|
||||
export WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-windows-32.exe"
|
||||
|
||||
WINDOWS_64_BINARY_NAME="monkey-windows-64.exe"
|
||||
WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/monkey-windows-64.exe"
|
||||
export WINDOWS_64_BINARY_URL
|
||||
export WINDOWS_64_BINARY_NAME
|
||||
export WINDOWS_64_BINARY_NAME="monkey-windows-64.exe"
|
||||
export WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/monkey-windows-64.exe"
|
||||
|
||||
# Other binaries for monkey
|
||||
TRACEROUTE_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/traceroute64"
|
||||
export TRACEROUTE_64_BINARY_URL
|
||||
TRACEROUTE_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/traceroute32"
|
||||
export TRACEROUTE_32_BINARY_URL
|
||||
SAMBACRY_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/sc_monkey_runner64.so"
|
||||
export SAMBACRY_64_BINARY_URL
|
||||
SAMBACRY_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$($MONKEY_LATEST_RELEASE)/sc_monkey_runner32.so"
|
||||
export SAMBACRY_32_BINARY_URL
|
||||
export TRACEROUTE_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/traceroute64"
|
||||
export TRACEROUTE_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/traceroute32"
|
||||
|
||||
export SAMBACRY_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/sc_monkey_runner64.so"
|
||||
export SAMBACRY_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/$MONKEY_LATEST_RELEASE/sc_monkey_runner32.so"
|
||||
|
|
|
@ -10,7 +10,7 @@ is_root() {
|
|||
|
||||
has_sudo() {
|
||||
# 0 true, 1 false
|
||||
timeout 1 sudo id && return 0 || return 1
|
||||
return $(sudo -nv > /dev/null 2>&1)
|
||||
}
|
||||
|
||||
handle_error() {
|
||||
|
@ -23,6 +23,11 @@ log_message() {
|
|||
echo -e "DEPLOYMENT SCRIPT: $1"
|
||||
}
|
||||
|
||||
if is_root; then
|
||||
log_message "Please don't run this script as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
config_branch=${2:-"develop"}
|
||||
config_url="https://raw.githubusercontent.com/guardicore/monkey/${config_branch}/deployment_scripts/config"
|
||||
|
||||
|
@ -62,14 +67,9 @@ ISLAND_BINARIES_PATH="$ISLAND_PATH/cc/binaries"
|
|||
INFECTION_MONKEY_DIR="$monkey_home/monkey/infection_monkey"
|
||||
MONKEY_BIN_DIR="$INFECTION_MONKEY_DIR/bin"
|
||||
|
||||
if is_root; then
|
||||
log_message "Please don't run this script as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HAS_SUDO=$(has_sudo)
|
||||
if [[ ! $HAS_SUDO ]]; then
|
||||
log_message "You need root permissions for some of this script operations. Quiting."
|
||||
if ! has_sudo; then
|
||||
log_message "You need root permissions for some of this script operations. \
|
||||
Run \`sudo -v\`, enter your password, and then re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -110,13 +110,16 @@ if [[ ${python_cmd} == "" ]]; then
|
|||
log_message "Python 3.7 command not found. Installing python 3.7."
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||
sudo apt-get update
|
||||
sudo apt install python3.7 python3.7-dev
|
||||
sudo apt-get install -y python3.7 python3.7-dev
|
||||
log_message "Python 3.7 is now available with command 'python3.7'."
|
||||
python_cmd="python3.7"
|
||||
fi
|
||||
|
||||
log_message "Installing build-essential"
|
||||
sudo apt install build-essential
|
||||
sudo apt-get install -y build-essential
|
||||
|
||||
log_message "Installing python3-distutils"
|
||||
sudo apt-get install -y python3-distutils
|
||||
|
||||
log_message "Installing or updating pip"
|
||||
# shellcheck disable=SC2086
|
||||
|
@ -134,7 +137,7 @@ requirements_island="$ISLAND_PATH/requirements.txt"
|
|||
${python_cmd} -m pip install -r "${requirements_island}" --user --upgrade || handle_error
|
||||
|
||||
log_message "Installing monkey requirements"
|
||||
sudo apt-get install libffi-dev upx libssl-dev libc++1
|
||||
sudo apt-get install -y libffi-dev upx libssl-dev libc++1
|
||||
requirements_monkey="$INFECTION_MONKEY_DIR/requirements.txt"
|
||||
${python_cmd} -m pip install -r "${requirements_monkey}" --user --upgrade || handle_error
|
||||
|
||||
|
@ -162,15 +165,19 @@ chmod a+x "$ISLAND_BINARIES_PATH/$LINUX_64_BINARY_NAME"
|
|||
|
||||
# If a user haven't installed mongo manually check if we can install it with our script
|
||||
if ! exists mongod; then
|
||||
log_message "Installing libcurl4"
|
||||
sudo apt-get install -y libcurl4
|
||||
|
||||
log_message "Installing MongoDB"
|
||||
"${ISLAND_PATH}"/linux/install_mongo.sh ${MONGO_PATH} || handle_error
|
||||
fi
|
||||
log_message "Installing openssl"
|
||||
sudo apt-get install openssl
|
||||
sudo apt-get install -y openssl
|
||||
|
||||
# Generate SSL certificate
|
||||
log_message "Generating certificate"
|
||||
|
||||
chmod u+x "${ISLAND_PATH}"/linux/create_certificate.sh
|
||||
"${ISLAND_PATH}"/linux/create_certificate.sh ${ISLAND_PATH}/cc
|
||||
|
||||
# Update node
|
||||
|
|
|
@ -15,7 +15,7 @@ Want to help secure networks? That's great!
|
|||
|
||||
Here's a few short links to help you get started.
|
||||
|
||||
* [Getting up and running](../setup-development-environment) - To help you get a working development setup.
|
||||
* [Getting up and running](./setup-development-environment) - To help you get a working development setup.
|
||||
* [Contributing guidelines](https://github.com/guardicore/monkey/blob/master/CONTRIBUTING.md) - Some guidelines to help you submit.
|
||||
|
||||
## What are we looking for?
|
||||
|
|
|
@ -17,4 +17,4 @@ This section of the documentation is incomplete and under active construction.
|
|||
|
||||
See these documentation pages for information on each configuration value:
|
||||
|
||||
{{% children description=true %}}
|
||||
{{% children description=true style="p"%}}
|
||||
|
|
|
@ -15,7 +15,7 @@ After deploying the Monkey Island in your environment, navigate to `https://<ser
|
|||
|
||||
### First-time login
|
||||
|
||||
On your first login, you'll be asked to set up a username and password for the Monkey Island server. [See this page for more details](../accounts-and-security).
|
||||
On your first login, you'll be asked to set up a username and password for the Monkey Island server. [See this page for more details](../../setup/accounts-and-security).
|
||||
|
||||
### Run the Monkey
|
||||
|
||||
|
|
|
@ -11,4 +11,4 @@ pre: "<i class='fas fa-directions'></i> "
|
|||
|
||||
The Monkey likes working together. See these documentation pages for information on each integration the Monkey currently offers:
|
||||
|
||||
{{% children description=true %}}
|
||||
{{% children description=true style="p"%}}
|
||||
|
|
|
@ -54,16 +54,15 @@ See [Amazon's documentation about working with SSM agents](https://docs.aws.amaz
|
|||
|
||||
### Running the monkey
|
||||
|
||||
When you run the monkey island on an AWS instance, the island detects it's running on AWS and present the following option in the _"Run Monkey"_ page, like so:
|
||||
When you run the Monkey Island on an AWS instance, the island detects it's running on AWS and present the following option in the _"Run Monkey"_ page, like so:
|
||||
|
||||
![Running a Monkey on EC2 Instance](/images/usage/integrations/monkey-island-aws-screenshot-1.png "Running a Monkey on EC2 Instance")
|
||||
|
||||
And then you can choose one of the available instances as "patient zero" like so:
|
||||
After you click on "AWS run" you can choose one of the available instances as "patient zero" like so:
|
||||
|
||||
1. Click on "Run on AWS"
|
||||
2. Choose the relevant Network Interface
|
||||
3. Select the machines you'd like to run the Monkey on
|
||||
4. Click "Run on Selected Machines", and watch the monkey go! 🐒
|
||||
1. Choose the relevant Network Interface
|
||||
2. Select the machines you'd like to run the Monkey on
|
||||
3. Click "Run on Selected Machines", and watch the monkey go! 🐒
|
||||
|
||||
![Running a Monkey on EC2 Instance](/images/usage/integrations/monkey-island-aws-screenshot-2.png "Running a Monkey on EC2 Instance")
|
||||
|
||||
|
|
|
@ -23,6 +23,13 @@ If the correct permissions have been set on the AWS IAM role of the Monkey Islan
|
|||
|
||||
Note that the integration is specifically between your Monkey Island and the security hub. The Infection Monkey is an free project and there is no centralised infrastructure.
|
||||
|
||||
### Enabling finding reception
|
||||
|
||||
Before starting the scan, make sure that AWS Security Hub is accepting findings by enabling Infection Monkey
|
||||
integration. Find **GuardiCore: AWS Infection Monkey** integration on the list and click on **Accept findings**.
|
||||
|
||||
![Enabled integration](/images/usage/integrations/security-hub-enable-accepting-findings.png "Enabled integration")
|
||||
|
||||
## Integration details
|
||||
|
||||
The Infection Monkey reports the following types of issues to the AWS security hub: `Software and Configuration Checks/Vulnerabilities/CVE`.
|
||||
|
|
|
@ -18,4 +18,4 @@ No worries! The Monkey uses safe exploiters and does not cause any permanent sys
|
|||
|
||||
## Section contents
|
||||
|
||||
{{% children description=True %}}
|
||||
{{% children description=True style="p"%}}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
title: "MITRE ATT&CK assessment"
|
||||
date: 2020-10-22T16:58:22+03:00
|
||||
draft: false
|
||||
description: "Assess your network security detection and prevention capabilities."
|
||||
weight: 2
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Infection Monkey can simulate various [ATT&CK](https://attack.mitre.org/matrices/enterprise/) techniques on the network.
|
||||
Use it to assess your security solutions’ detection and prevention capabilities. Infection Monkey will help you find
|
||||
which ATT&CK techniques go unnoticed and will provide recommendations about preventing them.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
- **ATT&CK matrix** You can use ATT&CK configuration section to select which techniques you want the Monkey to simulate.
|
||||
Leave default settings for the full simulation.
|
||||
- **Exploits -> Credentials** This configuration value will be used for brute-forcing. We use most popular passwords
|
||||
and usernames, but feel free to adjust it according to the default passwords used in your network. Keep in mind that
|
||||
long lists means longer scanning times.
|
||||
- **Network -> Scope** Disable “Local network scan” and instead provide specific network ranges in
|
||||
the “Scan target list”.
|
||||
|
||||
![ATT&CK matrix](/images/usage/scenarios/attack-matrix.png "ATT&CK matrix")
|
||||
|
||||
## Suggested run mode
|
||||
|
||||
Run the Infection Monkey on as many machines in your environment as you can to get a better assessment. This can be easily
|
||||
achieved by selecting the “Manual” run option and executing the command shown on different machines in your environment
|
||||
manually or with your deployment tool.
|
||||
|
||||
## Assessing results
|
||||
|
||||
The **ATT&CK Report** shows the status of ATT&CK techniques simulations. Click on any technique to see more details
|
||||
about it and potential mitigations. Keep in mind that each technique display contains a question mark symbol that
|
||||
will take you to the official documentation of ATT&CK technique, where you can learn more about it.
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
title: "Credential Leak"
|
||||
title: "Credentials Leak"
|
||||
date: 2020-08-12T13:04:25+03:00
|
||||
draft: false
|
||||
description: "Assess the impact of successful phishing attack, insider threat, or other form of credentials leak."
|
||||
weight: 4
|
||||
description: "Assess the impact of a successful phishing attack, insider threat, or other form of credentials leak."
|
||||
weight: 5
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
@ -16,8 +16,6 @@ where these credentials can be reused.
|
|||
|
||||
## Configuration
|
||||
|
||||
#### Important configuration values:
|
||||
|
||||
- **Exploits -> Credentials** After setting up the Island add the users’ **real** credentials
|
||||
(usernames and passwords) to the Monkey’s configuration (Don’t worry, this sensitive data is not accessible and is not
|
||||
distributed or used in any way other than being sent to the monkeys, and can be easily eliminated by resetting the Monkey Island’s configuration).
|
||||
|
@ -26,15 +24,14 @@ For this to work, Monkey Island or initial Monkey needs to have access to SSH ke
|
|||
To make sure SSH keys were gathered successfully, refresh the page and check this configuration value after you run the Monkey
|
||||
(content of keys will not be displayed, it will appear as `<Object>`).
|
||||
|
||||
To simulate the damage from a successful phishing attack using the Infection Monkey, choose machines in your network
|
||||
from potentially problematic group of machines, such as the laptop of one of your heavy email users or
|
||||
one of your strong IT users (think of people who are more likely to correspond with people outside of
|
||||
your organization). Execute the Monkey on chosen machines by clicking on “**1. Run Monkey**” from the left sidebar menu
|
||||
and choosing “**Run on machine of your choice**”.
|
||||
## Suggested run mode
|
||||
|
||||
Execute the Monkey on a chosen machine in your network using the “Manual” run option.
|
||||
Run the Monkey as a privileged user to make sure it gathers as many credentials from the system as possible.
|
||||
|
||||
![Exploit password and user lists](/images/usage/scenarios/user-password-lists.png "Exploit password and user lists")
|
||||
|
||||
## Assessing results
|
||||
|
||||
To assess the impact of leaked credentials see Security report. It's possible, that credential leak resulted in even
|
||||
To assess the impact of leaked credentials see Security report. It's possible that credential leak resulted in even
|
||||
more leaked credentials, for that look into **Security report -> Stolen credentials**.
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
---
|
||||
title: "IDS/IPS Test"
|
||||
date: 2020-08-12T13:07:47+03:00
|
||||
draft: false
|
||||
description: "Test your network defence solutions."
|
||||
weight: 5
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Infection Monkey can help you verify that your security solutions are working the way you expected them to.
|
||||
These may include your IR and SOC teams, your SIEM, your firewall, your endpoint security solution, and more.
|
||||
|
||||
## Configuration
|
||||
|
||||
#### Important configuration values:
|
||||
|
||||
- **Monkey -> Post breach** Post breach actions simulate the actions an attacker would make on infected system.
|
||||
To test something not present on the tool, you can provide your own file or command to be ran.
|
||||
|
||||
The default configuration is good enough for many cases, but configuring testing scope and adding brute-force
|
||||
credentials is a good bet in any scenario.
|
||||
|
||||
Running the Monkey on both the Island and on a few other machines in the network manually is also recommended,
|
||||
as it increases coverage and propagation rates.
|
||||
|
||||
|
||||
![Post breach configuration](/images/usage/use-cases/ids-test.PNG "Post breach configuration")
|
||||
|
||||
## Assessing results
|
||||
|
||||
After running the Monkey, follow the Monkeys’ actions on the Monkey Island’s infection map.
|
||||
|
||||
Now you can match this activity from the Monkey timeline display to your internal SIEM and make sure your security
|
||||
solutions are identifying and correctly alerting on different attacks.
|
||||
|
||||
- The red arrows indicate successful exploitations. If you see red arrows, those incidents ought to be reported as
|
||||
exploitation attempts, so check whether you are receiving alerts from your security systems as expected.
|
||||
- The orange arrows indicate scanning activity, usually used by attackers to locate potential vulnerabilities.
|
||||
If you see orange arrows, those incidents ought to be reported as scanning attempts (and possibly as segmentation violations).
|
||||
- The blue arrows indicate tunneling activity, usually used by attackers to infiltrate “protected” networks from
|
||||
the Internet. Perhaps someone is trying to bypass your firewall to gain access to a protected service in your network?
|
||||
Check if your micro-segmentation / firewall solution identify or report anything.
|
||||
|
||||
While running this scenario, be on the lookout for the action that should arise:
|
||||
Did you get a phone call telling you about suspicious activity inside your network? Are events flowing
|
||||
into your security events aggregators? Are you getting emails from your IR teams?
|
||||
Is the endpoint protection software you installed on machines in the network reporting on anything? Are your
|
||||
compliance scanners detecting anything wrong?
|
||||
|
||||
Lastly, check Zero Trust and Mitre ATT&CK reports, to see which attacks can be executed on the network and how to
|
||||
fix it.
|
||||
|
||||
![Map](/images/usage/use-cases/map-full-cropped.png "Map")
|
||||
|
|
@ -3,7 +3,7 @@ title: "Network Breach"
|
|||
date: 2020-08-12T13:04:55+03:00
|
||||
draft: false
|
||||
description: "Simulate an internal network breach and assess the potential impact."
|
||||
weight: 1
|
||||
weight: 3
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
@ -17,7 +17,6 @@ Infection Monkey will help you assess the impact of internal network breach, by
|
|||
|
||||
## Configuration
|
||||
|
||||
#### Important configuration values:
|
||||
- **Exploits -> Exploits** You can review the exploits Infection Monkey will be using. By default all
|
||||
safe exploiters are selected.
|
||||
- **Exploits -> Credentials** This configuration value will be used for brute-forcing. We use most popular passwords
|
||||
|
@ -34,6 +33,15 @@ all post breach actions. These actions simulate attacker's behaviour after getti
|
|||
|
||||
![Exploiter selector](/images/usage/use-cases/network-breach.PNG "Exploiter selector")
|
||||
|
||||
## Suggested run mode
|
||||
|
||||
Decide which machines you want to simulate a breach on and use the “Manual” run option to start Monkeys there.
|
||||
Use high privileges to run the Monkey to simulate an attacker that was able to elevate its privileges.
|
||||
You could also simulate an attack initiated from an unidentified machine connected to the network (a technician
|
||||
laptop, 3rd party vendor machine, etc) by running the Monkey on a dedicated machine with an IP in the network you
|
||||
wish to test.
|
||||
|
||||
|
||||
## Assessing results
|
||||
|
||||
Check infection map and security report to see how far monkey managed to propagate in the network and which
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
title: "Network Segmentation"
|
||||
date: 2020-08-12T13:05:05+03:00
|
||||
draft: false
|
||||
description: "Test network segmentation policies for apps that need ringfencing or tiers that require microsegmentation."
|
||||
weight: 3
|
||||
description: "Verify your network is properly segmented."
|
||||
weight: 4
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Segmentation is a method of creating secure zones in data centers and cloud deployments that allows companies to
|
||||
isolate workloads from one another and secure them individually, typically using policies.
|
||||
A useful way to test the effectiveness of your segmentation is to ensure that your network segments are
|
||||
properly separated, e,g, your Development is separated from your Production, your applications are separated from one
|
||||
another etc. To security test is to verify that your network segmentation is configured properly. This way you make
|
||||
sure that even if a certain attacker has breached your defenses, it can’t move laterally from point A to point B.
|
||||
Segmentation is a method of creating secure zones in data centers and cloud deployments that allows companies to
|
||||
isolate workloads from one another and secure them individually, typically using policies. A useful way to test
|
||||
the effectiveness of your segmentation is to ensure that your network segments are properly separated, e,g, your
|
||||
Development is separated from your Production, your applications are separated from one another etc. Use the
|
||||
Infection Monkey to verify that your network segmentation is configured properly. This way you make sure that
|
||||
even if a certain attacker has breached your defenses, it can’t move laterally between segments.
|
||||
|
||||
[Segmentation is key](https://www.guardicore.com/use-cases/micro-segmentation/) to protecting your network, reducing
|
||||
the attack surface and minimizing the damage of a breach. The Monkey can help you test your segmentation settings with
|
||||
|
@ -21,8 +21,6 @@ its cross-segment traffic testing feature.
|
|||
|
||||
## Configuration
|
||||
|
||||
#### Important configuration values:
|
||||
|
||||
- **Network -> Network analysis -> Network segmentation testing** This configuration setting allows you to define
|
||||
subnets that should be segregated from each other. If any of provided networks can reach each other, you'll see it
|
||||
in security report.
|
||||
|
@ -32,12 +30,12 @@ its cross-segment traffic testing feature.
|
|||
all post breach actions. These actions simulate attacker's behaviour after getting access to a new system, so they
|
||||
might trigger your defence solutions which will interrupt segmentation test.
|
||||
|
||||
Execute Monkeys on machines in different subnetworks manually, by choosing “**1. Run Monkey**” from the left sidebar menu
|
||||
and clicking on “**Run on machine of your choice**”.
|
||||
Alternatively, you could provide valid credentials and allow Monkey to propagate to relevant subnetworks by itself.
|
||||
## Suggested run mode
|
||||
|
||||
Execute Monkeys on machines in different subnetworks using the “Manual” run option.
|
||||
|
||||
Note that if Monkey can't communicate to the Island, it will
|
||||
not be able to send scan results, so make sure all machines can reach the island.
|
||||
not be able to send scan results, so make sure all machines can reach the island.
|
||||
|
||||
![How to configure network segmentation testing](/images/usage/scenarios/segmentation-config.png "How to configure network segmentation testing")
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title: "Other"
|
||||
date: 2020-08-12T13:07:55+03:00
|
||||
draft: false
|
||||
description: "Tips and tricks about configuring monkey for your needs."
|
||||
description: "Tips and tricks about configuring Monkeys for your needs."
|
||||
weight: 100
|
||||
---
|
||||
|
||||
|
@ -10,33 +10,52 @@ weight: 100
|
|||
|
||||
This page provides additional information about configuring monkeys, tips and tricks and creative usage scenarios.
|
||||
|
||||
## ATT&CK & Zero Trust scanning
|
||||
## Custom behaviour
|
||||
|
||||
You can use **ATT&CK** configuration section to select which techniques you want to scan. Keep in mind that ATT&CK
|
||||
matrix configuration just changes the overall configuration by modifying related fields, thus you should start by
|
||||
modifying and saving the matrix. After that you can change credentials and scope of the scan, but exploiters,
|
||||
post breach actions and other configuration values will be already chosen based on ATT&CK matrix and shouldn't be
|
||||
modified.
|
||||
|
||||
There's currently no way to configure monkey using Zero Trust framework, but regardless of configuration options,
|
||||
you'll always be able to see ATT&CK and Zero Trust reports.
|
||||
If you want Monkey to run some kind of script or a tool after it breaches a machine, you can configure it in
|
||||
**Configuration -> Monkey -> Post breach**. Just input commands you want executed in the corresponding fields.
|
||||
You can also upload files and call them through commands you entered in command fields.
|
||||
|
||||
## Tips and tricks
|
||||
## Accelerate the test
|
||||
|
||||
- Use **Monkey -> Persistent scanning** configuration section to either have periodic scans or to increase
|
||||
reliability of exploitations.
|
||||
|
||||
- To increase propagation run monkey as root/administrator. This will ensure that monkey will gather credentials
|
||||
on current system and use them to move laterally.
|
||||
To improve scanning speed you could **specify a subnet instead of scanning all of the local network**.
|
||||
|
||||
- Every network has its old “skeleton keys” that should have long been discarded. Configure the Monkey with old and stale passwords, but make sure that they were really discarded using the Monkey. To add the old passwords, in the island’s configuration, go to the “Exploit password list” under “Basic - Credentials” and use the “+” button to add the old passwords to the configuration. For example, here we added a few extra passwords (and a username as well) to the configuration:
|
||||
The following configuration values also have an impact on scanning speed:
|
||||
- **Credentials** - the more usernames and passwords you input, the longer it will take the Monkey to scan machines having
|
||||
remote access services. Monkeys try to stay elusive and leave a low impact, thus brute forcing takes longer than with
|
||||
loud conventional tools.
|
||||
- **Network scope** - scanning large networks with a lot of propagations can become unwieldy. Instead, try to scan your
|
||||
networks bit by bit with multiple runs.
|
||||
- **Post breach actions** - you can disable most of these if you only care about propagation.
|
||||
- **Internal -> TCP scanner** - you can trim the list of ports monkey tries to scan increasing performance even further.
|
||||
|
||||
## Combining different scenarios
|
||||
|
||||
Infection Monkey is not limited to the scenarios mentioned in this section, once you get the hang of configuring it,
|
||||
you might come up with your own use case or test all of suggested scenarios at the same time! Whatever you do,
|
||||
Security, ATT&CK and Zero Trust reports will be waiting for you!
|
||||
|
||||
## Persistent scanning
|
||||
|
||||
Use **Monkey -> Persistent** scanning configuration section to either have periodic scans or to increase reliability of
|
||||
exploitations by running consecutive Infection Monkey scans.
|
||||
|
||||
## Credentials
|
||||
|
||||
Every network has its old “skeleton keys” that should have long been discarded. Configure the Monkey with old and stale
|
||||
passwords, but make sure that they were really discarded using the Monkey. To add the old passwords, in the island’s
|
||||
configuration, go to the “Exploit password list” under “Basic - Credentials” and use the “+” button to add the old
|
||||
passwords to the configuration. For example, here we added a few extra passwords (and a username as well) to the
|
||||
configuration:
|
||||
|
||||
![Exploit password and user lists](/images/usage/scenarios/user-password-lists.png "Exploit password and user lists")
|
||||
|
||||
- To see the Monkey executing in real-time on your servers, add the **post-breach action** command: `wall “Infection Monkey was here”`. This post breach command will broadcast a message across all open terminals on the servers the Monkey breached, to achieve the following: Let you know the Monkey ran successfully on the server. let you follow the breach “live” alongside the infection map, and check which terminals are logged and monitored inside your network. See below:
|
||||
## Check logged and monitored terminals
|
||||
|
||||
To see the Monkey executing in real-time on your servers, add the **post-breach action** command:
|
||||
`wall “Infection Monkey was here”`. This post breach command will broadcast a message across all open terminals on
|
||||
the servers the Monkey breached, to achieve the following: Let you know the Monkey ran successfully on the server.
|
||||
Let you follow the breach “live” alongside the infection map, and check which terminals are logged and monitored
|
||||
inside your network. See below:
|
||||
|
||||
![How to configure post breach commands](/images/usage/scenarios/pba-example.png "How to configure post breach commands.")
|
||||
|
||||
- If you're scanning a large network, consider narrowing the scope and scanning it bit by bit if scan times become too
|
||||
long. Lowering the amount of credentials, exploiters or post breach actions can also help to lower scanning times.
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
title: "Zero Trust assessment"
|
||||
date: 2020-10-22T16:58:09+03:00
|
||||
draft: false
|
||||
description: "See where you stand in your Zero Trust journey."
|
||||
weight: 1
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Infection Monkey will help you assess your progress on your journey to achieve Zero Trust network.
|
||||
The Infection Monkey will automatically assess your readiness across the different
|
||||
[Zero Trust Extended Framework](https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210) principles.
|
||||
|
||||
## Configuration
|
||||
|
||||
- **Exploits -> Credentials** This configuration value will be used for brute-forcing. We use most popular passwords
|
||||
and usernames, but feel free to adjust it according to the default passwords used in your network.
|
||||
Keep in mind that long lists means longer scanning times.
|
||||
- **Network -> Scope** Disable “Local network scan” and instead provide specific network ranges in the “Scan target list”.
|
||||
- **Network -> Network analysis -> Network segmentation testing** This configuration setting allows you to define
|
||||
subnets that should be segregated from each other.
|
||||
|
||||
In general, other configuration value defaults should be good enough, but feel free to see the “Other” section
|
||||
for tips and tricks about other features and in-depth configuration parameters you can use.
|
||||
|
||||
![Exploit password and user lists](/images/usage/scenarios/user-password-lists.png "Exploit password and user lists")
|
||||
|
||||
## Suggested run mode
|
||||
|
||||
Run the Monkey on as many machines as you can. This can be easily achieved by selecting the “Manual” run option and
|
||||
executing the command shown on different machines in your environment manually or with your deployment tool.
|
||||
In addition, you can use any other run options you see fit.
|
||||
|
||||
## Assessing results
|
||||
|
||||
See the results in the Zero Trust report section. “The Summary” section will give you an idea about which Zero Trust
|
||||
pillars were tested, how many tests were done and test statuses. Specific tests are described in the “Test Results”
|
||||
section. The “Findings” section shows details about the Monkey actions. Click on “Events” of different findings to
|
||||
observe what exactly Infection Monkey did and when it was done. This should make it easy to cross reference events
|
||||
with your security solutions and alerts/logs.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 72 KiB |
Binary file not shown.
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 97 KiB |
BIN
docs/static/images/usage/integrations/security-hub-enable-accepting-findings.png
vendored
Normal file
BIN
docs/static/images/usage/integrations/security-hub-enable-accepting-findings.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 145 KiB |
Binary file not shown.
After Width: | Height: | Size: 158 KiB |
|
@ -1 +1 @@
|
|||
Subproject commit 4fdb70e3639143076ce2cd7d5a69cc1df8e78caf
|
||||
Subproject commit 045d78bc98540c9b96518df73c05fdb9d16507ba
|
|
@ -20,3 +20,7 @@ class CredentialsNotRequiredError(RegistrationNotNeededError):
|
|||
|
||||
class AlreadyRegisteredError(RegistrationNotNeededError):
|
||||
""" Raise to indicate the reason why registration is not required """
|
||||
|
||||
|
||||
class VersionServerConnectionError(Exception):
|
||||
""" Raise to indicate that connection to version update server failed """
|
||||
|
|
|
@ -84,7 +84,7 @@ class ControlClient(object):
|
|||
if ControlClient.proxies:
|
||||
debug_message += " through proxies: %s" % ControlClient.proxies
|
||||
LOG.debug(debug_message)
|
||||
requests.get("https://%s/api?action=is-up" % (server,), # noqa: DUO123
|
||||
requests.get(f"https://{server}/api?action=is-up", # noqa: DUO123
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies,
|
||||
timeout=TIMEOUT_IN_SECONDS)
|
||||
|
|
|
@ -228,7 +228,6 @@ class ShellShockExploiter(HostExploiter):
|
|||
Checks if which urls exist
|
||||
:return: Sequence of URLs to try and attack
|
||||
"""
|
||||
import requests
|
||||
attack_path = 'http://'
|
||||
if is_https:
|
||||
attack_path = 'https://'
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from logging import getLogger
|
||||
|
||||
from impacket.dcerpc.v5 import scmr, transport
|
||||
from impacket.smbconnection import SMB_DIALECT
|
||||
|
||||
from common.utils.attack_utils import ScanStatus, UsageEnum
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
|
@ -104,7 +103,7 @@ class SmbExploiter(HostExploiter):
|
|||
LOG.debug("Exploiter SmbExec is giving up...")
|
||||
return False
|
||||
|
||||
self.set_vulnerable_port(self.host)
|
||||
self.set_vulnerable_port()
|
||||
# execute the remote dropper in case the path isn't final
|
||||
if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower():
|
||||
cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} + \
|
||||
|
@ -121,8 +120,7 @@ class SmbExploiter(HostExploiter):
|
|||
for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values():
|
||||
rpctransport = transport.DCERPCTransportFactory(str_bind_format % (self.host.ip_addr,))
|
||||
rpctransport.set_dport(port)
|
||||
if hasattr(rpctransport, 'preferred_dialect'):
|
||||
rpctransport.preferred_dialect(SMB_DIALECT)
|
||||
rpctransport.setRemoteHost(self.host.ip_addr)
|
||||
if hasattr(rpctransport, 'set_credentials'):
|
||||
# This method exists only for selected protocol sequences.
|
||||
rpctransport.set_credentials(user, password, '', lm_hash, ntlm_hash, None)
|
||||
|
@ -168,7 +166,7 @@ class SmbExploiter(HostExploiter):
|
|||
SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1]))
|
||||
return True
|
||||
|
||||
def set_vulnerable_port(self, host: VictimHost):
|
||||
def set_vulnerable_port(self):
|
||||
if 'tcp-445' in self.host.services:
|
||||
self.vulnerable_port = "445"
|
||||
elif 'tcp-139' in self.host.services:
|
||||
|
|
|
@ -56,7 +56,7 @@ class WebRCE(HostExploiter):
|
|||
Method that creates a dictionary of configuration values for exploit
|
||||
:return: configuration dict
|
||||
"""
|
||||
exploit_config = dict()
|
||||
exploit_config = {}
|
||||
|
||||
# dropper: If true monkey will use dropper parameter that will detach monkey's process and try to copy
|
||||
# it's file to the default destination path.
|
||||
|
|
|
@ -170,7 +170,11 @@ class InfectionMonkey(object):
|
|||
for finger in self._fingerprint:
|
||||
LOG.info("Trying to get OS fingerprint from %r with module %s",
|
||||
machine, finger.__class__.__name__)
|
||||
finger.get_host_fingerprint(machine)
|
||||
try:
|
||||
finger.get_host_fingerprint(machine)
|
||||
except BaseException as exc:
|
||||
LOG.error("Failed to run fingerprinter %s, exception %s" % finger.__class__.__name__,
|
||||
str(exc))
|
||||
|
||||
ScanTelem(machine).send()
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ class NetworkScanner(object):
|
|||
:return: Victim or None if victim isn't alive
|
||||
"""
|
||||
LOG.debug("Scanning target address: %r", victim)
|
||||
if any([scanner.is_host_alive(victim) for scanner in self.scanners]):
|
||||
if any(scanner.is_host_alive(victim) for scanner in self.scanners):
|
||||
LOG.debug("Found potential target_ip: %r", victim)
|
||||
return victim
|
||||
else:
|
||||
|
|
|
@ -139,7 +139,7 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False):
|
|||
timeout = int(round(timeout)) # clamp to integer, to avoid checking input
|
||||
sockets_to_try = possible_ports[:]
|
||||
connected_ports_sockets = []
|
||||
while (timeout >= 0) and len(sockets_to_try):
|
||||
while (timeout >= 0) and sockets_to_try:
|
||||
sock_objects = [s[1] for s in sockets_to_try]
|
||||
|
||||
_, writeable_sockets, _ = select.select(sock_objects, sock_objects, sock_objects, 0)
|
||||
|
|
|
@ -15,5 +15,7 @@ class ScheduleJobs(PBA):
|
|||
super(ScheduleJobs, self).__init__(name=POST_BREACH_JOB_SCHEDULING,
|
||||
linux_cmd=' '.join(linux_cmds),
|
||||
windows_cmd=windows_cmds)
|
||||
|
||||
|
||||
def run(self):
|
||||
super(ScheduleJobs, self).run()
|
||||
remove_scheduled_jobs()
|
||||
|
|
|
@ -5,7 +5,7 @@ SCHEDULED_TASK_COMMAND = r'C:\windows\system32\cmd.exe'
|
|||
|
||||
|
||||
def get_windows_commands_to_schedule_jobs():
|
||||
return f'schtasks /Create /SC monthly /TN {SCHEDULED_TASK_NAME} /TR {SCHEDULED_TASK_COMMAND}'
|
||||
return f'schtasks /Create /SC monthly /F /TN {SCHEDULED_TASK_NAME} /TR {SCHEDULED_TASK_COMMAND}'
|
||||
|
||||
|
||||
def get_windows_commands_to_remove_scheduled_jobs():
|
||||
|
|
|
@ -25,9 +25,9 @@ class PostBreach(object):
|
|||
"""
|
||||
Executes all post breach actions.
|
||||
"""
|
||||
pool = Pool(5)
|
||||
pool.map(self.run_pba, self.pba_list)
|
||||
LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list)))
|
||||
with Pool(5) as pool:
|
||||
pool.map(self.run_pba, self.pba_list)
|
||||
LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list)))
|
||||
|
||||
@staticmethod
|
||||
def config_to_pba_list() -> Sequence[PBA]:
|
||||
|
@ -40,5 +40,6 @@ class PostBreach(object):
|
|||
try:
|
||||
LOG.debug("Executing PBA: '{}'".format(pba.name))
|
||||
pba.run()
|
||||
LOG.debug(f"Execution of {pba.name} finished")
|
||||
except Exception as e:
|
||||
LOG.error("PBA {} failed. Error info: {}".format(pba.name, e))
|
||||
|
|
|
@ -67,8 +67,8 @@ def _get_windows_cred(pypykatz_cred: PypykatzCredential):
|
|||
|
||||
|
||||
def _hash_to_string(hash_: Any):
|
||||
if type(hash_) == str:
|
||||
if type(hash_) is str:
|
||||
return hash_
|
||||
if type(hash_) == bytes:
|
||||
if type(hash_) is bytes:
|
||||
return binascii.hexlify(bytearray(hash_)).decode()
|
||||
raise Exception(f"Can't convert hash_ to string, unsupported hash_ type {type(hash_)}")
|
||||
|
|
|
@ -6,13 +6,14 @@ import boto3
|
|||
from botocore.exceptions import UnknownServiceError
|
||||
|
||||
from common.cloud.aws.aws_instance import AwsInstance
|
||||
from monkey_island.cc.environment import EnvironmentConfig
|
||||
from monkey_island.cc.services.reporting.exporter import Exporter
|
||||
|
||||
__authors__ = ['maor.rayzin', 'shay.nehmad']
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
INFECTION_MONKEY_ARN = "324264561773:product/guardicore/aws-infection-monkey"
|
||||
|
||||
|
||||
class AWSExporter(Exporter):
|
||||
@staticmethod
|
||||
|
@ -68,7 +69,7 @@ class AWSExporter(Exporter):
|
|||
# azure and conficker are not relevant issues for an AWS env
|
||||
}
|
||||
|
||||
configured_product_arn = EnvironmentConfig.get_from_file().aws.get('sec_hub_product_arn', '')
|
||||
configured_product_arn = INFECTION_MONKEY_ARN
|
||||
product_arn = 'arn:aws:securityhub:{region}:{arn}'.format(region=region, arn=configured_product_arn)
|
||||
instance_arn = 'arn:aws:ec2:' + str(region) + ':instance:{instance_id}'
|
||||
# Not suppressing error here on purpose.
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import logging
|
||||
|
||||
import monkey_island.cc.environment.environment_singleton as env_singleton
|
||||
from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
|
||||
from monkey_island.cc.services.reporting.aws_exporter import AWSExporter
|
||||
from monkey_island.cc.services.reporting.report_exporter_manager import \
|
||||
|
@ -22,7 +21,7 @@ def try_add_aws_exporter_to_manager(manager):
|
|||
# noinspection PyBroadException
|
||||
try:
|
||||
RemoteRunAwsService.init()
|
||||
if RemoteRunAwsService.is_running_on_aws() and ('aws' == env_singleton.env.get_deployment()):
|
||||
if RemoteRunAwsService.is_running_on_aws():
|
||||
manager.add_exporter_to_list(AWSExporter)
|
||||
except Exception:
|
||||
logger.error("Failed adding aws exporter to manager. Exception info:", exc_info=True)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.models import Monkey
|
||||
from monkey_island.cc.services.edge.edge import EdgeService
|
||||
from monkey_island.cc.services.node import NodeService
|
||||
from monkey_island.cc.services.telemetry.processing.utils import \
|
||||
get_edge_by_scan_or_exploit_telemetry
|
||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
|||
import requests
|
||||
|
||||
import monkey_island.cc.environment.environment_singleton as env_singleton
|
||||
from common.utils.exceptions import VersionServerConnectionError
|
||||
from common.version import get_version
|
||||
|
||||
__author__ = "itay.mizeretz"
|
||||
|
@ -29,8 +30,8 @@ class VersionUpdateService:
|
|||
if VersionUpdateService.newer_version is None:
|
||||
try:
|
||||
VersionUpdateService.newer_version = VersionUpdateService._check_new_version()
|
||||
except Exception:
|
||||
logger.exception('Failed updating version number')
|
||||
except VersionServerConnectionError:
|
||||
logger.info('Failed updating version number')
|
||||
|
||||
return VersionUpdateService.newer_version
|
||||
|
||||
|
@ -42,7 +43,11 @@ class VersionUpdateService:
|
|||
"""
|
||||
url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % (env_singleton.env.get_deployment(), get_version())
|
||||
|
||||
reply = requests.get(url, timeout=15)
|
||||
try:
|
||||
reply = requests.get(url, timeout=7)
|
||||
except requests.exceptions.RequestException:
|
||||
logger.info("Can't get latest monkey version, probably no connection to the internet.")
|
||||
raise VersionServerConnectionError
|
||||
|
||||
res = reply.json().get('newer_version', None)
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
"comma-dangle": 1,
|
||||
"quotes": [
|
||||
1,
|
||||
"single"
|
||||
"single",
|
||||
{"allowTemplateLiterals": true}
|
||||
],
|
||||
"no-undef": 1,
|
||||
"global-strict": 0,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
|
||||
import DropdownSelect from '../../ui-components/DropdownSelect';
|
||||
import {OS_TYPES} from './OsTypes';
|
||||
import GenerateLocalWindowsCmd from './commands/local_windows_cmd';
|
||||
import GenerateLocalWindowsPowershell from './commands/local_windows_powershell';
|
||||
import GenerateLocalLinuxWget from './commands/local_linux_wget';
|
||||
import GenerateLocalLinuxCurl from './commands/local_linux_curl';
|
||||
import CommandDisplay from './CommandDisplay';
|
||||
import InlineSelection from '../../../ui-components/inline-selection/InlineSelection';
|
||||
import DropdownSelect from '../../../ui-components/DropdownSelect';
|
||||
import {OS_TYPES} from '../utils/OsTypes';
|
||||
import GenerateLocalWindowsCmd from '../commands/local_windows_cmd';
|
||||
import GenerateLocalWindowsPowershell from '../commands/local_windows_powershell';
|
||||
import GenerateLocalLinuxWget from '../commands/local_linux_wget';
|
||||
import GenerateLocalLinuxCurl from '../commands/local_linux_curl';
|
||||
import CommandDisplay from '../utils/CommandDisplay';
|
||||
|
||||
|
||||
const LocalManualRunOptions = (props) => {
|
|
@ -0,0 +1,119 @@
|
|||
import React, {useState} from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
import checkboxHOC from 'react-table/lib/hoc/selectTable';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
|
||||
const CheckboxTable = checkboxHOC(ReactTable);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
Header: 'Machines',
|
||||
columns: [
|
||||
{Header: 'Machine', accessor: 'name'},
|
||||
{Header: 'Instance ID', accessor: 'instance_id'},
|
||||
{Header: 'IP Address', accessor: 'ip_address'},
|
||||
{Header: 'OS', accessor: 'os'}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const pageSize = 10;
|
||||
|
||||
function AWSInstanceTable(props) {
|
||||
|
||||
const [allToggled, setAllToggled] = useState(false);
|
||||
let checkboxTable = null;
|
||||
|
||||
function toggleSelection(key) {
|
||||
key = key.replace('select-', '');
|
||||
// start off with the existing state
|
||||
let modifiedSelection = [...props.selection];
|
||||
const keyIndex = modifiedSelection.indexOf(key);
|
||||
// check to see if the key exists
|
||||
if (keyIndex >= 0) {
|
||||
// it does exist so we will remove it using destructing
|
||||
modifiedSelection = [
|
||||
...modifiedSelection.slice(0, keyIndex),
|
||||
...modifiedSelection.slice(keyIndex + 1)
|
||||
];
|
||||
} else {
|
||||
// it does not exist so add it
|
||||
modifiedSelection.push(key);
|
||||
}
|
||||
// update the state
|
||||
props.setSelection(modifiedSelection);
|
||||
}
|
||||
|
||||
function isSelected(key) {
|
||||
return props.selection.includes(key);
|
||||
}
|
||||
|
||||
function toggleAll() {
|
||||
const selectAll = !allToggled;
|
||||
const selection = [];
|
||||
if (selectAll) {
|
||||
// we need to get at the internals of ReactTable
|
||||
const wrappedInstance = checkboxTable.getWrappedInstance();
|
||||
// the 'sortedData' property contains the currently accessible records based on the filter and sort
|
||||
const currentRecords = wrappedInstance.getResolvedState().sortedData;
|
||||
// we just push all the IDs onto the selection array
|
||||
currentRecords.forEach(item => {
|
||||
selection.push(item._original.instance_id);
|
||||
});
|
||||
}
|
||||
setAllToggled(selectAll);
|
||||
props.setSelection(selection);
|
||||
}
|
||||
|
||||
function getTrProps(_, r) {
|
||||
let color = 'inherit';
|
||||
if (r) {
|
||||
let instId = r.original.instance_id;
|
||||
if (isSelected(instId)) {
|
||||
color = '#ffed9f';
|
||||
} else if (Object.prototype.hasOwnProperty.call(props.results, instId)) {
|
||||
color = props.results[instId] ? '#00f01b' : '#f00000'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
style: {backgroundColor: color}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="data-table-container">
|
||||
<CheckboxTable
|
||||
ref={r => (checkboxTable = r)}
|
||||
keyField="instance_id"
|
||||
columns={columns}
|
||||
data={props.data}
|
||||
showPagination={true}
|
||||
defaultPageSize={pageSize}
|
||||
className="-highlight"
|
||||
selectType="checkbox"
|
||||
toggleSelection={toggleSelection}
|
||||
isSelected={isSelected}
|
||||
toggleAll={toggleAll}
|
||||
selectAll={allToggled}
|
||||
getTrProps={getTrProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
AWSInstanceTable.propTypes = {
|
||||
data: PropTypes.arrayOf(PropTypes.exact({
|
||||
instance_id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
os: PropTypes.string,
|
||||
ip_address: PropTypes.string
|
||||
})),
|
||||
results: PropTypes.arrayOf(PropTypes.string),
|
||||
selection: PropTypes.arrayOf(PropTypes.string),
|
||||
setSelection: PropTypes.func
|
||||
}
|
||||
|
||||
export default AWSInstanceTable;
|
|
@ -0,0 +1,80 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import AuthComponent from '../../../AuthComponent';
|
||||
import '../../../../styles/components/RunOnIslandButton.scss';
|
||||
import {faCloud} from '@fortawesome/free-solid-svg-icons';
|
||||
import AWSRunOptions from './AWSRunOptions';
|
||||
import NextSelectionButton from '../../../ui-components/inline-selection/NextSelectionButton';
|
||||
import {Alert, Button} from 'react-bootstrap';
|
||||
import LoadingIcon from '../../../ui-components/LoadingIcon';
|
||||
|
||||
|
||||
function AWSRunButton(props) {
|
||||
|
||||
const authComponent = new AuthComponent({});
|
||||
|
||||
const [isOnAWS, setIsOnAWS] = useState(false);
|
||||
const [AWSInstances, setAWSInstances] = useState([]);
|
||||
const [awsMachineCollectionError, setAwsMachineCollectionError] = useState('');
|
||||
const [componentLoading, setComponentLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
checkIsOnAWS();
|
||||
}, []);
|
||||
|
||||
function checkIsOnAWS() {
|
||||
authComponent.authFetch('/api/remote-monkey?action=list_aws')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
let isAws = res['is_aws'];
|
||||
setComponentLoading(false);
|
||||
if (isAws) {
|
||||
// On AWS!
|
||||
// Checks if there was an error while collecting the aws machines.
|
||||
let isErrorWhileCollectingAwsMachines = (res['error'] != null);
|
||||
if (isErrorWhileCollectingAwsMachines) {
|
||||
// There was an error. Finish loading, and display error message.
|
||||
setIsOnAWS(true);
|
||||
setAwsMachineCollectionError(res['error']);
|
||||
} else {
|
||||
// No error! Finish loading and display machines for user
|
||||
setIsOnAWS(true);
|
||||
setAWSInstances(res['instances']);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getAWSButton() {
|
||||
return <NextSelectionButton title={'AWS run'}
|
||||
description={'Run on a chosen AWS instance in the cloud.'}
|
||||
icon={faCloud}
|
||||
onButtonClick={() => {
|
||||
props.setComponent(AWSRunOptions,
|
||||
{AWSInstances: AWSInstances, setComponent: props.setComponent})
|
||||
}}/>
|
||||
}
|
||||
|
||||
function getErrorDisplay() {
|
||||
return (
|
||||
<Alert variant={'info'}>Detected ability to run on different AWS instances.
|
||||
To enable this feature, follow the
|
||||
<Button variant={'link'} className={'inline-link'}
|
||||
href={'https://www.guardicore.com/infectionmonkey/docs/usage/integrations/aws-run-on-ec2-machine/'}>
|
||||
Tutorial
|
||||
</Button> and refresh the page. Error received while trying to list AWS instances: {awsMachineCollectionError}
|
||||
</Alert> );
|
||||
}
|
||||
|
||||
let displayed = '';
|
||||
if (componentLoading) {
|
||||
displayed = LoadingIcon();
|
||||
}
|
||||
if (awsMachineCollectionError !== '') {
|
||||
displayed = getErrorDisplay();
|
||||
} else if (isOnAWS) {
|
||||
displayed = getAWSButton();
|
||||
}
|
||||
return displayed;
|
||||
}
|
||||
|
||||
export default AWSRunButton;
|
|
@ -0,0 +1,117 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import {Button, Nav} from 'react-bootstrap';
|
||||
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faSync} from '@fortawesome/free-solid-svg-icons/faSync';
|
||||
import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
|
||||
import AwsRunTable from './AWSInstanceTable';
|
||||
import AuthComponent from '../../../AuthComponent';
|
||||
import InlineSelection from '../../../ui-components/inline-selection/InlineSelection';
|
||||
|
||||
|
||||
const AWSRunOptions = (props) => {
|
||||
return InlineSelection(getContents, {
|
||||
...props,
|
||||
onBackButtonClick: () => {props.setComponent()}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const getContents = (props) => {
|
||||
|
||||
const authComponent = new AuthComponent({});
|
||||
|
||||
let [allIPs, setAllIPs] = useState([]);
|
||||
let [selectedIp, setSelectedIp] = useState(null);
|
||||
let [AWSClicked, setAWSClicked] = useState(false);
|
||||
let [runResults, setRunResults] = useState([]);
|
||||
let [selectedInstances, setSelectedInstances] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
getIps();
|
||||
}, []);
|
||||
|
||||
function getIps() {
|
||||
authComponent.authFetch('/api')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
setAllIPs(res['ip_addresses']);
|
||||
setSelectedIp(res['ip_addresses'][0]);
|
||||
});
|
||||
}
|
||||
|
||||
function runOnAws() {
|
||||
setAWSClicked(true);
|
||||
let instances = selectedInstances.map(x => instanceIdToInstance(x));
|
||||
|
||||
authComponent.authFetch('/api/remote-monkey',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({type: 'aws', instances: instances, island_ip: selectedIp})
|
||||
}).then(res => res.json())
|
||||
.then(res => {
|
||||
let result = res['result'];
|
||||
|
||||
// update existing state, not run-over
|
||||
let prevRes = result;
|
||||
for (let key in result) {
|
||||
if (result.hasOwnProperty(key)) {
|
||||
prevRes[key] = result[key];
|
||||
}
|
||||
}
|
||||
setRunResults(prevRes);
|
||||
setSelectedInstances([]);
|
||||
setAWSClicked(false);
|
||||
});
|
||||
}
|
||||
|
||||
function instanceIdToInstance(instance_id) {
|
||||
let instance = props.AWSInstances.find(
|
||||
function (inst) {
|
||||
return inst['instance_id'] === instance_id;
|
||||
});
|
||||
|
||||
return {'instance_id': instance_id, 'os': instance['os']}
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{'marginBottom': '2em'}}>
|
||||
<div style={{'marginTop': '1em', 'marginBottom': '1em'}}>
|
||||
<p className="alert alert-info">
|
||||
<FontAwesomeIcon icon={faInfoCircle} style={{'marginRight': '5px'}}/>
|
||||
Not sure what this is? Not seeing your AWS EC2 instances? <a
|
||||
href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances"
|
||||
rel="noopener noreferrer" target="_blank">Read the documentation</a>!
|
||||
</p>
|
||||
</div>
|
||||
{
|
||||
allIPs.length > 1 ?
|
||||
<Nav variant="pills" activeKey={selectedIp} onSelect={setSelectedIp}
|
||||
style={{'marginBottom': '2em'}}>
|
||||
{allIPs.map(ip => <Nav.Item key={ip}><Nav.Link eventKey={ip}>{ip}</Nav.Link></Nav.Item>)}
|
||||
</Nav>
|
||||
: <div style={{'marginBottom': '2em'}}/>
|
||||
}
|
||||
<AwsRunTable
|
||||
data={props.AWSInstances}
|
||||
results={runResults}
|
||||
selection={selectedInstances}
|
||||
setSelection={setSelectedInstances}
|
||||
/>
|
||||
<div className={'aws-run-button-container'}>
|
||||
<Button
|
||||
size={'lg'}
|
||||
onClick={runOnAws}
|
||||
className={'btn btn-default btn-md center-block'}
|
||||
disabled={AWSClicked}>
|
||||
Run on selected machines
|
||||
{AWSClicked ?
|
||||
<FontAwesomeIcon icon={faSync} className={`text-success spinning-icon`} style={{'marginLeft': '5px'}}/> : null}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AWSRunOptions;
|
|
@ -1,12 +1,13 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import NextSelectionButton from '../../ui-components/inline-selection/NextSelectionButton';
|
||||
import LocalManualRunOptions from './LocalManualRunOptions';
|
||||
import LocalManualRunOptions from './RunManually/LocalManualRunOptions';
|
||||
import AuthComponent from '../../AuthComponent';
|
||||
import {faLaptopCode} from '@fortawesome/free-solid-svg-icons/faLaptopCode';
|
||||
import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
|
||||
import {cloneDeep} from 'lodash';
|
||||
import {faExpandArrowsAlt} from '@fortawesome/free-solid-svg-icons';
|
||||
import RunOnIslandButton from './RunOnIslandButton';
|
||||
import AWSRunButton from './RunOnAWS/AWSRunButton';
|
||||
|
||||
function RunOptions(props) {
|
||||
|
||||
|
@ -61,6 +62,7 @@ function RunOptions(props) {
|
|||
setComponent(LocalManualRunOptions,
|
||||
{ips: ips, setComponent: setComponent})
|
||||
}}/>
|
||||
<AWSRunButton setComponent={setComponent}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {OS_TYPES} from '../OsTypes';
|
||||
import {OS_TYPES} from '../utils/OsTypes';
|
||||
|
||||
|
||||
export default function generateLocalLinuxCurl(ip, osType) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {OS_TYPES} from '../OsTypes';
|
||||
import {OS_TYPES} from '../utils/OsTypes';
|
||||
|
||||
|
||||
export default function generateLocalLinuxWget(ip, osType) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {OS_TYPES} from '../OsTypes';
|
||||
import {OS_TYPES} from '../utils/OsTypes';
|
||||
|
||||
|
||||
export default function generateLocalWindowsCmd(ip, osType) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {OS_TYPES} from '../OsTypes';
|
||||
import {OS_TYPES} from '../utils/OsTypes';
|
||||
|
||||
|
||||
export default function generateLocalWindowsPowershell(ip, osType) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
|
||||
import LocalManualRunOptions from './LocalManualRunOptions';
|
||||
import InlineSelection from '../../../ui-components/inline-selection/InlineSelection';
|
||||
import LocalManualRunOptions from '../RunManually/LocalManualRunOptions';
|
||||
|
||||
function InterfaceSelection(props) {
|
||||
return InlineSelection(getContents, props, LocalManualRunOptions)
|
|
@ -1,109 +0,0 @@
|
|||
import React from 'react';
|
||||
import ReactTable from 'react-table'
|
||||
import checkboxHOC from 'react-table/lib/hoc/selectTable';
|
||||
|
||||
const CheckboxTable = checkboxHOC(ReactTable);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
Header: 'Machines',
|
||||
columns: [
|
||||
{Header: 'Machine', accessor: 'name'},
|
||||
{Header: 'Instance ID', accessor: 'instance_id'},
|
||||
{Header: 'IP Address', accessor: 'ip_address'},
|
||||
{Header: 'OS', accessor: 'os'}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const pageSize = 10;
|
||||
|
||||
class AwsRunTableComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selection: [],
|
||||
selectAll: false,
|
||||
result: {}
|
||||
}
|
||||
}
|
||||
|
||||
toggleSelection = (key) => {
|
||||
// start off with the existing state
|
||||
let selection = [...this.state.selection];
|
||||
const keyIndex = selection.indexOf(key);
|
||||
// check to see if the key exists
|
||||
if (keyIndex >= 0) {
|
||||
// it does exist so we will remove it using destructing
|
||||
selection = [
|
||||
...selection.slice(0, keyIndex),
|
||||
...selection.slice(keyIndex + 1)
|
||||
];
|
||||
} else {
|
||||
// it does not exist so add it
|
||||
selection.push(key);
|
||||
}
|
||||
// update the state
|
||||
this.setState({selection});
|
||||
};
|
||||
|
||||
isSelected = key => {
|
||||
return this.state.selection.includes(key);
|
||||
};
|
||||
|
||||
toggleAll = () => {
|
||||
const selectAll = !this.state.selectAll;
|
||||
const selection = [];
|
||||
if (selectAll) {
|
||||
// we need to get at the internals of ReactTable
|
||||
const wrappedInstance = this.checkboxTable.getWrappedInstance();
|
||||
// the 'sortedData' property contains the currently accessible records based on the filter and sort
|
||||
const currentRecords = wrappedInstance.getResolvedState().sortedData;
|
||||
// we just push all the IDs onto the selection array
|
||||
currentRecords.forEach(item => {
|
||||
selection.push(item._original.instance_id);
|
||||
});
|
||||
}
|
||||
this.setState({selectAll, selection});
|
||||
};
|
||||
|
||||
getTrProps = (_, r) => {
|
||||
let color = 'inherit';
|
||||
if (r) {
|
||||
let instId = r.original.instance_id;
|
||||
if (this.isSelected(instId)) {
|
||||
color = '#ffed9f';
|
||||
} else if (Object.prototype.hasOwnProperty.call(this.state.result, instId)) {
|
||||
color = this.state.result[instId] ? '#00f01b' : '#f00000'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
style: {backgroundColor: color}
|
||||
};
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="data-table-container">
|
||||
<CheckboxTable
|
||||
ref={r => (this.checkboxTable = r)}
|
||||
keyField="instance_id"
|
||||
columns={columns}
|
||||
data={this.props.data}
|
||||
showPagination={true}
|
||||
defaultPageSize={pageSize}
|
||||
className="-highlight"
|
||||
selectType="checkbox"
|
||||
toggleSelection={this.toggleSelection}
|
||||
isSelected={this.isSelected}
|
||||
toggleAll={this.toggleAll}
|
||||
selectAll={this.state.selectAll}
|
||||
getTrProps={this.getTrProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AwsRunTableComponent;
|
|
@ -0,0 +1,9 @@
|
|||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faSync} from '@fortawesome/free-solid-svg-icons';
|
||||
import React from 'react';
|
||||
|
||||
function LoadingIcon() {
|
||||
return <FontAwesomeIcon icon={faSync} className={`spinning-icon loading-icon`} />
|
||||
}
|
||||
|
||||
export default LoadingIcon;
|
|
@ -17,6 +17,7 @@
|
|||
@import 'components/inline-selection/BackButton';
|
||||
@import 'components/inline-selection/CommandDisplay';
|
||||
@import 'components/Icons';
|
||||
@import 'components/Buttons';
|
||||
|
||||
|
||||
// Define custom elements after bootstrap import
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
a.inline-link {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
|
@ -1,3 +1,10 @@
|
|||
.loading-icon {
|
||||
color: $monkey-yellow;
|
||||
font-size: 20px;
|
||||
margin-left: 50%;
|
||||
margin-right: 50%;
|
||||
}
|
||||
|
||||
.spinning-icon {
|
||||
animation: spin-animation 0.5s infinite;
|
||||
display: inline-block;
|
||||
|
|
|
@ -14,3 +14,8 @@ div.run-on-os-buttons > .nav-item > .nav-link:hover{
|
|||
color: $monkey-white;
|
||||
background-color: $monkey-alt;
|
||||
}
|
||||
|
||||
.aws-run-button-container {
|
||||
margin-top: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ module.exports = {
|
|||
publicPath: '/'
|
||||
},
|
||||
devServer: {
|
||||
host: '0.0.0.0',
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://localhost:5000',
|
||||
|
|
|
@ -10,7 +10,7 @@ dpath>=2.0
|
|||
flask>=1.1
|
||||
ipaddress>=1.0.23
|
||||
jsonschema==3.2.0
|
||||
mongoengine>=0.20
|
||||
mongoengine==0.20
|
||||
mongomock==3.19.0
|
||||
netifaces>=0.10.9
|
||||
pycryptodome==3.9.8
|
||||
|
|
Loading…
Reference in New Issue