rapid7/metasploit-framework

View on GitHub
lib/msf/core/exploit/remote/http/wordpress/helpers.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# -*- coding: binary -*-
module Msf::Exploit::Remote::HTTP::Wordpress::Helpers

  # Helper methods are private and should not be called by modules
  private

  # Returns the POST data for a Wordpress login request
  #
  # @param user [String] Username
  # @param pass [String] Password
  # @param redirect URL [String] to redirect after successful login
  # @return [Hash] The post data for vars_post Parameter
  def wordpress_helper_login_post_data(user, pass, redirect = nil)
    post_data = {
      'log' => user.to_s,
      'pwd' => pass.to_s,
      'redirect_to' => redirect.to_s,
      'wp-submit' => 'Login'
    }
    post_data
  end


  # Returns the POST data for a Wordpress register request
  #
  # @param user [String] Username
  # @param email [String] Email Address
  # @param redirect URL [String] to redirect after successful registration
  # @return [Hash] The post data for vars_post Parameter
  def wordpress_helper_register_post_data(user, email, redirect = nil)
    {
      'user_login' => user.to_s,
      'user_email' => email.to_s,
      'redirect_to' => redirect.to_s,
      'wp-submit' => 'Register'
    }
  end

  # Helper method to post a comment to Wordpress
  #
  # @param comment [String] The comment
  # @param comment_post_id [Integer] The Post ID to post the comment to
  # @param login_cookie [String] The valid login_cookie
  # @param author [String] The author name
  # @param email [String] The author email
  # @param url [String] The author url
  # @return [String,nil] The location of the new comment/post, nil on error
  def wordpress_helper_post_comment(comment, comment_post_id, login_cookie, author, email, url)
    vars_post = {
      'comment' => comment,
      'submit' => 'Post+Comment',
      'comment_post_ID' => comment_post_id.to_s,
      'comment_parent' => '0'
    }
    vars_post.merge!({
      'author' => author,
      'email' => email,
      'url' => url
    }) unless login_cookie

    options = {
      'uri' => normalize_uri(target_uri.path, 'wp-comments-post.php'),
      'method' => 'POST'
    }
    options.merge!({ 'vars_post' => vars_post })
    options.merge!({ 'cookie' => login_cookie }) if login_cookie
    res = send_request_cgi(options)
    if res && res.redirect? && res.redirection
      return wordpress_helper_parse_location_header(res)
    else
      message = "Post comment failed."
      message << " Status Code: #{res.code}" if res
      print_error(message)
      return nil
    end
  end

  # Helper method for bruteforcing a valid post id
  #
  # @param range [Range] The Range of post_ids to bruteforce
  # @param comments_enabled [Boolean] If true try to find a post id with comments enabled, otherwise return the first found
  # @param login_cookie [String] A valid login cookie to perform the bruteforce as an authenticated user
  # @return [Integer,nil] The post id, nil when nothing found
  def wordpress_helper_bruteforce_valid_post_id(range, comments_enabled = false, login_cookie = nil)
    range.each { |id|
      vprint_status("Checking POST ID #{id}...") if (id % 100) == 0
      body = wordpress_helper_check_post_id(wordpress_url_post(id), comments_enabled, login_cookie)
      return id if body
    }
    # no post found
    return nil
  end

  # Helper method to check if a post is valid an has comments enabled
  #
  # @param uri [String] the Post URI Path
  # @param comments_enabled [Boolean] Check if comments are enabled on this post
  # @param login_cookie [String] A valid login cookie to perform the check as an authenticated user
  # @return [String,nil] the HTTP response body of the post, nil otherwise
  def wordpress_helper_check_post_id(uri, comments_enabled = false, login_cookie = nil)
    options = {
      'method' => 'GET',
      'uri' => uri
    }
    options.merge!({ 'cookie' => login_cookie }) if login_cookie
    res = send_request_cgi(options)
    # post exists
    if res && res.code == 200
      # also check if comments are enabled
      if comments_enabled
        if res.body =~ /form.*action.*wp-comments-post\.php/
          return res.body
        else
          return nil
        end
        # valid post found, not checking for comments
      else
        return res.body
      end
    elsif res && res.redirect? && res.redirection
      path = wordpress_helper_parse_location_header(res)
      return wordpress_helper_check_post_id(path, comments_enabled, login_cookie)
    end
    return nil
  end

  # Helper method parse a Location header and returns only the path and query. Returns nil on error
  #
  # @param res [Rex::Proto::Http::Response] The HTTP response
  # @return [String,nil] the path and query, nil on error
  def wordpress_helper_parse_location_header(res)
    return nil unless res && res.redirect? && res.redirection

    location = res.redirection
    path_from_uri(location)
  end

  # Helper method to retrieve a valid plugin upload nonce.
  #
  # @param cookie [String] A valid admin session cookie
  # @return [String,nil] The nonce, nil on error
  def wordpress_helper_get_plugin_upload_nonce(cookie, path = nil, vars_get = nil)
    uri = path || normalize_uri(wordpress_url_backend, 'plugin-install.php')
    options = {
      'method'    => 'GET',
      'uri'       => uri,
      'cookie'    => cookie,
      'vars_get'  => vars_get || { 'tab' => 'upload' }
    }
    res = send_request_cgi(options)
    if res && res.code == 200
      return res.body.to_s[/id="_wpnonce" name="_wpnonce" value="([a-z0-9]+)"/i, 1]
    elsif res && res.redirect? && res.redirection
      path = wordpress_helper_parse_location_header(res)
      return wordpress_helper_get_plugin_upload_nonce(cookie, path)
    end
  end

  # Helper method to retrieve a valid plugin edit nonce.
  #
  # @param cookie [String] A valid admin session cookie
  # @param file [String] The plugin file to edit (relative to plugins dir)
  # @return [String,nil] The nonce, nil on error
  def wordpress_helper_get_plugin_edit_nonce(cookie, file)
    wordpress_helper_get_plugin_upload_nonce(
      cookie,
      normalize_uri(wordpress_url_backend, 'plugin-editor.php'),
      'file' => file
    )
  end

  # Helper method to retrieve plugin file contents.
  #
  # @param cookie [String] A valid admin session cookie
  # @param file [String] The plugin file to retrieve (relative to plugins dir)
  # @return [String,nil] The contents, nil on error
  def wordpress_helper_get_plugin_file_contents(cookie, file)
    res = send_request_cgi(
      'method'   => 'GET',
      'uri'      => normalize_uri(wordpress_url_backend, 'plugin-editor.php'),
      'cookie'   => cookie,
      'vars_get' => {'file' => file}
    )

    return unless res && res.code == 200

    contents = res.get_html_document.at('//textarea[@name = "newcontent"]')

    return unless contents

    contents.text
  end

end