From 2bbd5d48241f26c791b37a0a46dc49624470e215 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz 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 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 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 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 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)" } } },