From 2bbd5d48241f26c791b37a0a46dc49624470e215 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz <itay.mizeretz@guardicore.com>
Date: Tue, 3 Oct 2017 15:47:07 +0300
Subject: [PATCH 1/5] Fix SambaCry .close() bug

---
 chaos_monkey/exploit/sambacry.py | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py
index 4bebbd6a1..ab27728ff 100644
--- a/chaos_monkey/exploit/sambacry.py
+++ b/chaos_monkey/exploit/sambacry.py
@@ -100,7 +100,6 @@ class SambaCryExploiter(HostExploiter):
             smb_client = self.connect_to_server(host.ip_addr, creds)
             self.upload_module(smb_client, host, share, depth)
             self.trigger_module(smb_client, share)
-            smb_client.close()
         except (impacket.smbconnection.SessionError, SessionError):
             LOG.debug(
                 "Exception trying to exploit host: %s, share: %s, with creds: %s." % (host.ip_addr, share, str(creds)))
@@ -125,7 +124,6 @@ class SambaCryExploiter(HostExploiter):
                 # Ignore exception to try and delete as much as possible
                 pass
         smb_client.disconnectTree(tree_id)
-        smb_client.close()
 
     def get_trigger_result(self, ip, share, creds):
         """
@@ -147,7 +145,6 @@ class SambaCryExploiter(HostExploiter):
             pass
 
         smb_client.disconnectTree(tree_id)
-        smb_client.close()
         return file_content
 
     def get_writable_shares_creds_dict(self, ip):
@@ -159,6 +156,8 @@ class SambaCryExploiter(HostExploiter):
         writable_shares_creds_dict = {}
         credentials_list = self.get_credentials_list()
 
+        LOG.debug("SambaCry credential list: %s" % str(credentials_list))
+
         for credentials in credentials_list:
             try:
                 smb_client = self.connect_to_server(ip, credentials)
@@ -169,7 +168,6 @@ class SambaCryExploiter(HostExploiter):
                     if self.is_share_writable(smb_client, share):
                         writable_shares_creds_dict[share] = credentials
 
-                smb_client.close()
             except (impacket.smbconnection.SessionError, SessionError):
                 # If failed using some credentials, try others.
                 pass

From 65f5dbeaaf9d6528e91406dc41ea0b1879fe83e9 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz <itay.mizeretz@guardicore.com>
Date: Tue, 3 Oct 2017 15:47:50 +0300
Subject: [PATCH 2/5] Sleep only *between* life cycles

---
 chaos_monkey/config.py              |  2 ++
 chaos_monkey/example.conf           |  1 +
 chaos_monkey/monkey.py              | 36 ++++++++++++++++-------------
 monkey_island/cc/services/config.py |  6 +++++
 4 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py
index 6b49d3bb3..c1cd618ef 100644
--- a/chaos_monkey/config.py
+++ b/chaos_monkey/config.py
@@ -173,6 +173,8 @@ class Configuration(object):
     # addresses of internet servers to ping and check if the monkey has internet acccess.
     internet_services = ["monkey.guardicore.com", "www.google.com"]
 
+    keep_tunnel_open_time = 60
+
     ###########################
     # scanners config
     ###########################
diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf
index 285bffd11..b738cff75 100644
--- a/chaos_monkey/example.conf
+++ b/chaos_monkey/example.conf
@@ -6,6 +6,7 @@
      "monkey.guardicore.com",
      "www.google.com"
      ],
+    "keep_tunnel_open_time": 60,
     "range_class": "RelativeRange",
     "range_fixed": [
         ""
diff --git a/chaos_monkey/monkey.py b/chaos_monkey/monkey.py
index daabad0ee..426d121eb 100644
--- a/chaos_monkey/monkey.py
+++ b/chaos_monkey/monkey.py
@@ -1,17 +1,18 @@
-import sys
-import os
-import time
-import logging
-import tunnel
 import argparse
+import logging
+import os
 import subprocess
-from system_singleton import SystemSingleton
-from network.firewall import app as firewall
-from control import ControlClient
+import sys
+import time
+
+import tunnel
 from config import WormConfiguration
-from network.network_scanner import NetworkScanner
+from control import ControlClient
 from model import DELAY_DELETE_CMD
+from network.firewall import app as firewall
+from network.network_scanner import NetworkScanner
 from system_info import SystemInfoCollector
+from system_singleton import SystemSingleton
 
 __author__ = 'itamar'
 
@@ -101,7 +102,7 @@ class ChaosMonkey(object):
         else:
             LOG.debug("Running with depth: %d" % WormConfiguration.depth)
 
-        for _ in xrange(WormConfiguration.max_iterations):
+        for iteration_index in xrange(WormConfiguration.max_iterations):
             ControlClient.keepalive()
             ControlClient.load_control_config()
 
@@ -146,7 +147,6 @@ class ChaosMonkey(object):
                         LOG.debug("Skipping %r - exploitation failed before", machine)
                         continue
 
-
                 if monkey_tunnel:
                     monkey_tunnel.set_tunnel_for_host(machine)
                 if self._default_server:
@@ -196,8 +196,10 @@ class ChaosMonkey(object):
                 else:
                     self._fail_exploitation_machines.add(machine)
 
-            if not is_empty:
-                time.sleep(WormConfiguration.timeout_between_iterations)
+            if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1):
+                time_to_sleep = WormConfiguration.timeout_between_iterations
+                LOG.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep)
+                time.sleep(time_to_sleep)
 
         if self._keep_running and WormConfiguration.alive:
             LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations)
@@ -206,8 +208,10 @@ class ChaosMonkey(object):
 
         # if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to
         # connect to the tunnel
-        if last_exploit_time and (time.time() - last_exploit_time < 60):
-            time.sleep(time.time() - last_exploit_time)
+        if last_exploit_time and (time.time() - last_exploit_time < WormConfiguration.keep_tunnel_open_time):
+            time_to_sleep = WormConfiguration.keep_tunnel_open_time - (time.time() - last_exploit_time)
+            LOG.info("Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep)
+            time.sleep(time_to_sleep)
 
         if monkey_tunnel:
             monkey_tunnel.stop()
@@ -242,7 +246,7 @@ class ChaosMonkey(object):
                                      close_fds=True, startupinfo=startupinfo)
                 else:
                     os.remove(sys.executable)
-            except Exception, exc:
+            except Exception as exc:
                 LOG.error("Exception in self delete: %s", exc)
 
         LOG.info("Monkey is shutting down")
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 5e4d5abe0..6807d5d86 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -277,6 +277,12 @@ SCHEMA = {
                             "type": "string",
                             "default": "{2384ec59-0df8-4ab9-918c-843740924a28}",
                             "description": "The name of the mutex used to determine whether the monkey is already running"
+                        },
+                        "keep_tunnel_open_time": {
+                            "title": "Keep tunnel open time",
+                            "type": "integer",
+                            "default": 60,
+                            "description": "Time to keep tunnel open before going down since last exploit (in seconds)"
                         }
                     }
                 },

From 14eec1ba99fd7f6b9ffbb6389695349b4620a256 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz <itay.mizeretz@guardicore.com>
Date: Tue, 3 Oct 2017 16:18:34 +0300
Subject: [PATCH 3/5] Log stack trace of exceptions thrown from exploit

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

diff --git a/chaos_monkey/monkey.py b/chaos_monkey/monkey.py
index 426d121eb..a71993f7c 100644
--- a/chaos_monkey/monkey.py
+++ b/chaos_monkey/monkey.py
@@ -172,8 +172,8 @@ class ChaosMonkey(object):
                                                                      'exploiter': exploiter.__class__.__name__})
 
                     except Exception as exc:
-                        LOG.error("Exception while attacking %s using %s: %s",
-                                  machine, exploiter.__class__.__name__, exc)
+                        LOG.exception("Exception while attacking %s using %s: %s",
+                                      machine, exploiter.__class__.__name__, exc)
                         ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
                                                                  'exploiter': exploiter.__class__.__name__})
                         continue

From 0c971da15c40b56ea642a71b6e343c6e5f1b8fb9 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz <itay.mizeretz@guardicore.com>
Date: Tue, 3 Oct 2017 17:08:23 +0300
Subject: [PATCH 4/5] linux's implementation of local_ips returns array of strs
 instead of unicodes This fixes SambaCry Linux->Linux exploit among other
 things

---
 chaos_monkey/network/info.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py
index 605799ce3..e438f37b3 100644
--- a/chaos_monkey/network/info.py
+++ b/chaos_monkey/network/info.py
@@ -48,7 +48,7 @@ else:
 
     def local_ips():
         ipv4_nets = get_host_subnets()
-        valid_ips = [network['addr'] for network in ipv4_nets]
+        valid_ips = [network['addr'].encode('utf-8').strip() for network in ipv4_nets]
         return valid_ips
 
 

From bf5fb10838fb44fef3e76e876fb04db543df8b59 Mon Sep 17 00:00:00 2001
From: Itay Mizeretz <itay.mizeretz@guardicore.com>
Date: Sun, 8 Oct 2017 19:23:34 +0300
Subject: [PATCH 5/5] Fix CR

---
 chaos_monkey/monkey.py              | 7 ++-----
 chaos_monkey/network/info.py        | 5 +++--
 monkey_island/cc/services/config.py | 2 +-
 3 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/chaos_monkey/monkey.py b/chaos_monkey/monkey.py
index a71993f7c..17fc17bdd 100644
--- a/chaos_monkey/monkey.py
+++ b/chaos_monkey/monkey.py
@@ -81,8 +81,6 @@ class ChaosMonkey(object):
         if monkey_tunnel:
             monkey_tunnel.start()
 
-        last_exploit_time = None
-
         ControlClient.send_telemetry("state", {'done': False})
 
         self._default_server = WormConfiguration.current_server
@@ -180,7 +178,6 @@ class ChaosMonkey(object):
 
                 if successful_exploiter:
                     self._exploited_machines.add(machine)
-                    last_exploit_time = time.time()
                     ControlClient.send_telemetry('exploit', {'result': True, 'machine': machine.__dict__,
                                                              'exploiter': successful_exploiter.__class__.__name__})
 
@@ -208,8 +205,8 @@ class ChaosMonkey(object):
 
         # if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to
         # connect to the tunnel
-        if last_exploit_time and (time.time() - last_exploit_time < WormConfiguration.keep_tunnel_open_time):
-            time_to_sleep = WormConfiguration.keep_tunnel_open_time - (time.time() - last_exploit_time)
+        if len(self._exploited_machines) > 0:
+            time_to_sleep = WormConfiguration.keep_tunnel_open_time
             LOG.info("Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep)
             time.sleep(time_to_sleep)
 
diff --git a/chaos_monkey/network/info.py b/chaos_monkey/network/info.py
index e438f37b3..0c841dc9f 100644
--- a/chaos_monkey/network/info.py
+++ b/chaos_monkey/network/info.py
@@ -29,6 +29,8 @@ def get_host_subnets():
     for network in ipv4_nets:
         if 'broadcast' in network:
             network.pop('broadcast')
+        for attr in network:
+            network[attr] = network[attr].encode('utf-8').strip()
     return ipv4_nets
 
 
@@ -47,8 +49,7 @@ else:
 
 
     def local_ips():
-        ipv4_nets = get_host_subnets()
-        valid_ips = [network['addr'].encode('utf-8').strip() for network in ipv4_nets]
+        valid_ips = [network['addr'] for network in get_host_subnets()]
         return valid_ips
 
 
diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py
index 6807d5d86..dc9ce6a9e 100644
--- a/monkey_island/cc/services/config.py
+++ b/monkey_island/cc/services/config.py
@@ -282,7 +282,7 @@ SCHEMA = {
                             "title": "Keep tunnel open time",
                             "type": "integer",
                             "default": 60,
-                            "description": "Time to keep tunnel open before going down since last exploit (in seconds)"
+                            "description": "Time to keep tunnel open before going down after last exploit (in seconds)"
                         }
                     }
                 },