lib/msf/core/exploit/remote/http/wordpress/helpers.rb
# -*- 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