From 8d5c90faa46d5c698ee4f5c1f7cc0be90beba22f Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 29 Jul 2019 09:18:45 +0300 Subject: [PATCH 1/4] Filtering sensitive info when logging running config --- monkey/infection_monkey/config.py | 6 ++++++ monkey/infection_monkey/control.py | 3 ++- monkey/infection_monkey/main.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index f7e4cfae4..72fc8adf6 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -53,6 +53,12 @@ class Configuration(object): result = self.from_kv(formatted_data) return result + @staticmethod + def filter_sensitive_info(config_dict): + config_dict["exploit_password_list"] = ["~REDACTED~"] + config_dict["exploit_user_list"] = ["~REDACTED~"] + return config_dict + def as_dict(self): result = {} for key in dir(Configuration): diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index f34784041..874616f00 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -168,7 +168,8 @@ class ControlClient(object): try: unknown_variables = WormConfiguration.from_kv(reply.json().get('config')) - LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),)) + LOG.info("New configuration was loaded from server: %r" % + (WormConfiguration.filter_sensitive_info(WormConfiguration.as_dict()),)) except Exception as exc: # we don't continue with default conf here because it might be dangerous LOG.error("Error parsing JSON reply from control server %s (%s): %s", diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py index b8e292243..c880dc7f0 100644 --- a/monkey/infection_monkey/main.py +++ b/monkey/infection_monkey/main.py @@ -68,7 +68,7 @@ def main(): else: print("Config file wasn't supplied and default path: %s wasn't found, using internal default" % (config_file,)) - print("Loaded Configuration: %r" % WormConfiguration.as_dict()) + print("Loaded Configuration: %r" % WormConfiguration.filter_sensitive_info(WormConfiguration.as_dict())) # Make sure we're not in a machine that has the kill file kill_path = os.path.expandvars( From bb8e9f519282f9e07981d413af1db8a1bd9fbf4b Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 29 Jul 2019 10:15:27 +0300 Subject: [PATCH 2/4] Fixed CR Comment - exported sensitive fields --- monkey/infection_monkey/config.py | 12 +++++++----- monkey/infection_monkey/control.py | 2 +- monkey/infection_monkey/main.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 72fc8adf6..077aa705b 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -13,9 +13,11 @@ GUID = str(uuid.getnode()) EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin') +SENSITIVE_FIELDS = ["exploit_password_list", "exploit_user_list"] +HIDDEN_FIELD_REPLACEMENT_CONTENT = "hidden" + class Configuration(object): - def from_kv(self, formatted_data): # now we won't work at <2.7 for sure network_import = importlib.import_module('infection_monkey.network') @@ -54,9 +56,9 @@ class Configuration(object): return result @staticmethod - def filter_sensitive_info(config_dict): - config_dict["exploit_password_list"] = ["~REDACTED~"] - config_dict["exploit_user_list"] = ["~REDACTED~"] + def hide_sensitive_info(config_dict): + for field in SENSITIVE_FIELDS: + config_dict[field] = HIDDEN_FIELD_REPLACEMENT_CONTENT return config_dict def as_dict(self): @@ -180,7 +182,7 @@ class Configuration(object): # TCP Scanner HTTP_PORTS = [80, 8080, 443, - 8008, # HTTP alternate + 8008, # HTTP alternate 7001 # Oracle Weblogic default server port ] tcp_target_ports = [22, diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 874616f00..f6c52be55 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -169,7 +169,7 @@ class ControlClient(object): try: unknown_variables = WormConfiguration.from_kv(reply.json().get('config')) LOG.info("New configuration was loaded from server: %r" % - (WormConfiguration.filter_sensitive_info(WormConfiguration.as_dict()),)) + (WormConfiguration.hide_sensitive_info(WormConfiguration.as_dict()),)) except Exception as exc: # we don't continue with default conf here because it might be dangerous LOG.error("Error parsing JSON reply from control server %s (%s): %s", diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py index c880dc7f0..2ddf9127e 100644 --- a/monkey/infection_monkey/main.py +++ b/monkey/infection_monkey/main.py @@ -68,7 +68,7 @@ def main(): else: print("Config file wasn't supplied and default path: %s wasn't found, using internal default" % (config_file,)) - print("Loaded Configuration: %r" % WormConfiguration.filter_sensitive_info(WormConfiguration.as_dict())) + print("Loaded Configuration: %r" % WormConfiguration.hide_sensitive_info(WormConfiguration.as_dict())) # Make sure we're not in a machine that has the kill file kill_path = os.path.expandvars( From 209aacd96d0f79aef8ca6497f4ba4f92b581859e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 29 Jul 2019 17:11:01 +0300 Subject: [PATCH 3/4] Hashing all places in the log that log passwords Now passwords are no longer plaintext --- monkey/infection_monkey/config.py | 13 ++++++++++ monkey/infection_monkey/exploit/mssqlexec.py | 5 ++-- monkey/infection_monkey/exploit/rdpgrinder.py | 14 +++++------ monkey/infection_monkey/exploit/smbexec.py | 8 +++---- monkey/infection_monkey/exploit/sshexec.py | 24 +++++++++---------- monkey/infection_monkey/exploit/wmiexec.py | 18 ++++++++------ 6 files changed, 50 insertions(+), 32 deletions(-) diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 077aa705b..cb5bf881b 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -1,3 +1,4 @@ +import hashlib import os import json import sys @@ -280,5 +281,17 @@ class Configuration(object): PBA_linux_filename = None PBA_windows_filename = None + @staticmethod + def hash_sensitive_data(sensitive_data): + """ + Hash sensitive data (e.g. passwords). Used so the log won't contain sensitive data plain-text, as the log is + saved on client machines plain-text. + + :param sensitive_data: the data to hash. + :return: the hashed data. + """ + password_hashed = hashlib.sha512(sensitive_data).hexdigest() + return password_hashed + WormConfiguration = Configuration() diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 9d1dcb2d6..f1e561644 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -123,8 +123,9 @@ class MSSQLExploiter(HostExploiter): # Core steps # Trying to connect conn = pymssql.connect(host, user, password, port=port, login_timeout=self.LOGIN_TIMEOUT) - LOG.info('Successfully connected to host: {0}, ' - 'using user: {1}, password: {2}'.format(host, user, password)) + LOG.info( + 'Successfully connected to host: {0}, using user: {1}, password (SHA-512): {2}'.format( + host, user, self._config.hash_sensitive_data(password))) self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) self.report_login_attempt(True, user, password) cursor = conn.cursor() diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index 0db63e86d..c63affcdc 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -9,16 +9,16 @@ from rdpy.core.error import RDPSecurityNegoFail from rdpy.protocol.rdp import rdp from twisted.internet import reactor +from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING +from common.utils.exploit_enum import ExploitType from infection_monkey.exploit import HostExploiter from infection_monkey.exploit.tools import HTTPTools, get_monkey_depth +from infection_monkey.exploit.tools import build_monkey_commandline from infection_monkey.exploit.tools import get_target_monkey from infection_monkey.model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS from infection_monkey.network.tools import check_tcp_port -from infection_monkey.exploit.tools import build_monkey_commandline from infection_monkey.telemetry.attack.t1197_telem import T1197Telem from infection_monkey.utils import utf_to_ascii -from common.utils.exploit_enum import ExploitType -from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING __author__ = 'hoffer' @@ -299,8 +299,8 @@ class RdpExploiter(HostExploiter): for user, password in user_password_pairs: try: # run command using rdp. - LOG.info("Trying RDP logging into victim %r with user %s and password '%s'", - self.host, user, password) + LOG.info("Trying RDP logging into victim %r with user %s and password (SHA-512) '%s'", + self.host, user, self._config.hash_sensitive_data(password)) LOG.info("RDP connected to %r", self.host) @@ -327,8 +327,8 @@ class RdpExploiter(HostExploiter): except Exception as exc: LOG.debug("Error logging into victim %r with user" - " %s and password '%s': (%s)", self.host, - user, password, exc) + " %s and password (SHA-512) '%s': (%s)", self.host, + user, self._config.hash_sensitive_data(password), exc) continue http_thread.join(DOWNLOAD_TIMEOUT) diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index d49e66ae8..0bc8fc783 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -66,8 +66,8 @@ class SmbExploiter(HostExploiter): self._config.smb_download_timeout) if remote_full_path is not None: - LOG.debug("Successfully logged in %r using SMB (%s : %s : %s : %s)", - self.host, user, password, lm_hash, ntlm_hash) + LOG.debug("Successfully logged in %r using SMB (%s : (SHA-512) %s : %s : %s)", + self.host, user, self._config.hash_sensitive_data(password), lm_hash, ntlm_hash) self.report_login_attempt(True, user, password, lm_hash, ntlm_hash) self.add_vuln_port("%s or %s" % (SmbExploiter.KNOWN_PROTOCOLS['139/SMB'][1], SmbExploiter.KNOWN_PROTOCOLS['445/SMB'][1])) @@ -79,8 +79,8 @@ class SmbExploiter(HostExploiter): 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)", self.host, - user, password, lm_hash, ntlm_hash, exc) + " %s, password (SHA-512): '%s', LM hash: %s, NTLM hash: %s: (%s)", self.host, + user, self._config.hash_sensitive_data(password), lm_hash, ntlm_hash, exc) continue if not exploited: diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index c7cf030c1..1e002cd26 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -1,16 +1,16 @@ +import StringIO import logging import time import paramiko -import StringIO import infection_monkey.monkeyfs as monkeyfs +from common.utils.exploit_enum import ExploitType from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.tools import build_monkey_commandline from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth from infection_monkey.model import MONKEY_ARG from infection_monkey.network.tools import check_tcp_port -from infection_monkey.exploit.tools import build_monkey_commandline -from common.utils.exploit_enum import ExploitType __author__ = 'hoffer' @@ -71,26 +71,26 @@ class SSHExploiter(HostExploiter): exploited = False - for user, curpass in user_password_pairs: + for user, current_password in user_password_pairs: try: ssh.connect(self.host.ip_addr, username=user, - password=curpass, + password=current_password, port=port, timeout=None) - LOG.debug("Successfully logged in %r using SSH (%s : %s)", - self.host, user, curpass) + LOG.debug("Successfully logged in %r using SSH. User: %s, pass (SHA-512): %s)", + self.host, user, self._config.hash_sensitive_data(current_password)) exploited = True self.add_vuln_port(port) - self.report_login_attempt(True, user, curpass) + self.report_login_attempt(True, user, current_password) break except Exception as exc: LOG.debug("Error logging into victim %r with user" - " %s and password '%s': (%s)", self.host, - user, curpass, exc) - self.report_login_attempt(False, user, curpass) + " %s and password (SHA-512) '%s': (%s)", self.host, + user, self._config.hash_sensitive_data(current_password), exc) + self.report_login_attempt(False, user, current_password) continue return exploited @@ -109,7 +109,7 @@ class SSHExploiter(HostExploiter): LOG.info("SSH port is closed on %r, skipping", self.host) return False - #Check for possible ssh exploits + # Check for possible ssh exploits exploited = self.exploit_with_ssh_keys(port, ssh) if not exploited: exploited = self.exploit_with_login_creds(port, ssh) diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 9439d7414..b27dccbb8 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -33,8 +33,10 @@ class WmiExploiter(HostExploiter): creds = self._config.get_exploit_user_password_or_hash_product() for user, password, lm_hash, ntlm_hash in creds: - LOG.debug("Attempting to connect %r using WMI with user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')", - self.host, user, password, lm_hash, ntlm_hash) + password_hashed = self._config.hash_sensitive_data(password) + LOG.debug("Attempting to connect %r using WMI with " + "user,password (SHA-512),lm hash,ntlm hash: ('%s','%s','%s','%s')", + self.host, user, password_hashed, lm_hash, ntlm_hash) wmi_connection = WmiTools.WmiConnection() @@ -44,23 +46,23 @@ class WmiExploiter(HostExploiter): self.report_login_attempt(False, user, password, lm_hash, ntlm_hash) LOG.debug("Failed connecting to %r using WMI with " "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')", - self.host, user, password, lm_hash, ntlm_hash) + self.host, user, password_hashed, lm_hash, ntlm_hash) continue except DCERPCException: self.report_login_attempt(False, user, password, lm_hash, ntlm_hash) LOG.debug("Failed connecting to %r using WMI with " "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')", - self.host, user, password, lm_hash, ntlm_hash) + self.host, user, password_hashed, lm_hash, ntlm_hash) continue except socket.error: LOG.debug("Network error in WMI connection to %r with " "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s')", - self.host, user, password, lm_hash, ntlm_hash) + self.host, user, password_hashed, lm_hash, ntlm_hash) return False except Exception as exc: LOG.debug("Unknown WMI connection error to %r with " "user,password,lm hash,ntlm hash: ('%s','%s','%s','%s') (%s):\n%s", - self.host, user, password, lm_hash, ntlm_hash, exc, traceback.format_exc()) + self.host, user, password_hashed, lm_hash, ntlm_hash, exc, traceback.format_exc()) return False self.report_login_attempt(True, user, password, lm_hash, ntlm_hash) @@ -91,7 +93,8 @@ class WmiExploiter(HostExploiter): # execute the remote dropper in case the path isn't final elif remote_full_path.lower() != self._config.dropper_target_path_win_32.lower(): cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32) + build_monkey_commandline( + self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32) else: cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) @@ -118,3 +121,4 @@ class WmiExploiter(HostExploiter): return success return False + From 111080d5cd17481f8b4ffafd995401f9a19ca528 Mon Sep 17 00:00:00 2001 From: itay Date: Thu, 1 Aug 2019 10:52:38 +0300 Subject: [PATCH 4/4] remove python 3 build from travis --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 963c37fc6..b14482939 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,6 @@ language: python cache: pip python: - 2.7 - - 3.6 -matrix: - include: - - python: 3.7 - dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) - sudo: required # required for Python 3.7 (travis-ci/travis-ci#9069) install: #- pip install -r requirements.txt - pip install flake8 # pytest # add another testing frameworks later