Vulnerabilities Patched in WP Cost Estimation Plugin

At the end of January, Wordfence security analysts identified attackers exploiting vulnerabilities in outdated versions of the commercial plugin WP Cost Estimation & Payment Forms Builder, or WP Cost Estimation for short. These flaws were found and patched by the developer a few months ago, but no official public disclosure was made at the time. Following this discovery, our threat intelligence team reviewed updated versions of the plugin for additional security issues. We reported an unpatched directory traversal vulnerability to the developer, Loopus Plugins, who has since released an update addressing the issue. This flaw is present in plugin versions before 9.660.

Any sites using the plugin should update it to the latest available version. Wordfence WAF users, both free and paid, are already protected from each of these vulnerabilities thanks to broad rules built into the firewall. It is still recommended that Wordfence users perform these updates to ensure their sites are as secure as possible.

In today’s post, we’ll look at the original activity that drew our analysts’ attention to the plugin, then discuss the issues our team identified and disclosed to the developer.

File Upload and Delete Vulnerabilities Exploited In The Wild (Versions < 9.644)

During a forensic review of a compromised site, a Wordfence security analyst identified logs indicating the exploit used to take over the site:

POST /wp-admin/admin-ajax.php?action=lfb_upload_form
POST /wp-admin/admin-ajax.php?action=lfb_upload_form
POST /wp-content/uploads/CostEstimationPayment/_/ngfndfgsdcas.tss

The action lfb_upload_form was traced to the installed WP Cost Estimation plugin, which allowed us to piece together what had taken place. The installed version of the plugin was outdated, and the AJAX action allowing file uploads through form submissions was exploitable.

if (strlen($value["name"]) > 4 &&
$value['size'] < 10485760 &&
strpos(strtolower($value["name"]), '.php') === false &&
strpos(strtolower($value["name"]), '.js') === false &&
strpos(strtolower($value["name"]), '.html') === false &&
strpos(strtolower($value["name"]), '.phtml') === false &&
strpos(strtolower($value["name"]), '.pl') === false &&
strpos(strtolower($value["name"]), '.py') === false &&
strpos(strtolower($value["name"]), '.jsp') === false &&
strpos(strtolower($value["name"]), '.asp') === false &&
strpos(strtolower($value["name"]), '.htm') === false &&
strpos(strtolower($value["name"]), '.shtml') === false &&
strpos(strtolower($value["name"]), '.sh') === false &&
strpos(strtolower($value["name"]), '.cgi') === false
) {

The if statement above served as the only security check on uploaded files in older versions (9.526 in this case), performing some basic filename checks in an attempt to prevent files with executable extensions from being uploaded. Generally speaking, blacklisting uploads based on their filename is not an effective means of implementing upload security, as there tend to be techniques to bypass such methods. One such bypass was used here.

In the log entries above, you may have noticed the lfb_upload_form action being fired twice. This attack involved first uploading a web shell script with a meaningless extension, “ngfndfgsdcas.tss”. While the script was a malicious PHP file, the filename did not contain the string “.php”, and bypassed the blacklist. Then, to allow the uploaded file to execute as a PHP script, the following .htaccess file was uploaded:

AddHandler application/x-httpd-php .tss
AddType application/x-httpd-php .tss

These .htaccess rules associated the “.tss” extension with the PHP handler, allowing the shell to run as a PHP script. As of version 9.644 of WP Cost Estimation, released in October 2018, it is no longer possible to upload a valid .htaccess file to perform this bypass.

Interestingly, despite the attackers’ successful uploads, they quickly followed up with a different exploit against the same plugin. It’s unknown why this second exploit was performed, though it’s our assumption that either the uploaded shell or .htaccess file failed to behave as intended, leaving the attackers to go to Plan B.

In this second attack we see a new AJAX action from WP Cost Estimation, followed by a familiar attack pattern:

POST /wp-admin/admin-ajax.php?action=lfb_removeFile
POST /wp-admin/setup-config.php?step=2
POST /wp-admin/install.php?step=2
GET /wp-login.php
POST /wp-login.php
GET /wp-admin/
GET /wp-admin/theme-install.php?upload
POST /wp-admin/update.php?action=upload-theme
GET /wp-content/themes/AdvanceImage5/config.php

The exploited AJAX action, lfb_removeFile, can be used to delete arbitrary files on a vulnerable site:

public function removeFile(){
$formSession = sanitize_text_field($_POST['formSession']);
$file = sanitize_text_field($_POST['file']);
$fileName = $formSession . '_' . $file;
if(file_exists($this->uploads_dir .$fileName)){
unlink($this->uploads_dir .$fileName);
}
die();
}

The workflow for exploiting an arbitrary file delete flaw is usually the same: Delete the vulnerable site’s wp-config.php file. With no database configuration, WordPress assumes a fresh install is taking place. The attacker is then free to connect the site to their own remote database, log in as an administrator, and upload backdoors through the dashboard.

The lfb_removeFile AJAX action (as well as the associated internal removeFile function shown above) were both removed when the developer became aware of exploits and released version 9.644.

New Vulnerability – Upload Directory Traversal

Following the discovery of the earlier patched vulnerabilities, we spent some time reviewing the patches themselves to ensure they were sufficient. In the version we tested, the uploader had been improved in a few ways:

  • The blacklist of disallowed strings in filenames was expanded to block .htaccess uploads.
  • An admin-controlled whitelist of allowed filetypes was added, by default only allowing PNG, JPG, GIF, RAR, and ZIP files.
  • Forms could now have an internal randomSeed value, a short alphanumeric string appended to the user-input upload path in order to prevent existing directories from being accessed.

However, our investigation revealed a bypass case, allowing attackers to overwrite any file with a whitelisted type on an affected site.

 $ext = $this->get_extension($value["name"]);
$allowedFiles = explode(",", $item->allowedFiles);
if (in_array('.' . strtolower($ext), $allowedFiles)) {
if (!is_dir($this->uploads_dir . $formSession . $form->randomSeed)) {
mkdir($this->uploads_dir . $formSession . $form->randomSeed);
chmod($this->uploads_dir . $formSession . $form->randomSeed, $this->chmodWrite);
}
move_uploaded_file($value["tmp_name"], $this->uploads_dir . $formSession . $form->randomSeed . '/' . $fileName);
chmod($this->uploads_dir . $formSession . $form->randomSeed . '/' . $fileName, 0644);
}

The code block above, taken from the patched version of the uploadFormFiles function, shows the $formSession and $form->randomSeed variables used in a number of places to define an upload path for a given file.

The intent of this behavior is for each visitor to a site to be given a unique $formSession value as a hidden field in each form, so files they upload can be grouped appropriately in directories. However, this value is ultimately user-supplied when the form is submitted, and is susceptible to directory traversal attacks. For example, submitting a $formSession value like ../../.. would place the uploaded file in the document root of a site, rather than in wp-content/uploads/CostEstimationPayment as intended.

This vulnerability is mitigated in part by the addition of the randomSeed value, which would break most directory traversal attempts by appending a random alphanumeric string following the $formSession input. Uploads could still be made to unintended directories, but it would prevent existing files from being overwritten.

Unfortunately, only forms created in the patched version would have an associated randomSeed value stored in the database. Forms which existed prior to the patch, which would certainly be the case for the majority of users, had an empty randomSeed value. This empty value does nothing when appended to the $formSession path, which leaves these forms vulnerable.

Even with a whitelist only allowing images and archives to be uploaded, an attacker could cause serious trouble with an exploit. Any image on a site could be overwritten, allowing defacement campaigns to replace them en masse. If any backups are kept in an accessible location in a zip archive, an attacker could replace this backup with their own poisoned version, containing new users in the database or backdoors buried elsewhere in the file structure. When the backup is restored (perhaps following a mysterious case of overwritten images), these backdoors would be deployed.

Coordinated Disclosure

Once we became aware of the remaining issues, we contacted the developer to begin the process of patching them. We quickly received a response from Charly Biscay of Loopus Plugins. Vendor responses to vulnerability disclosures can be unpredictable, if a response comes at all, but Charly was happy to be notified and worked closely with us through the process of developing a patch.

For this patch, we made three recommendations which were all implemented:

  1. Reject any $formSession containing any non-alphanumeric characters.
  2. Generate new randomSeed values for forms where a value is not present.
  3. Implement .htaccess restrictions in upload directories, so scripts are inaccessible in the event that a successful upload takes place.

The timeline of the discovery, disclosure, and patching of this flaw is as follows:

2019-01-26: Upload directory traversal vulnerability discovered. Vendor contacted through form on CodeCanyon profile.
2019-01-28: Received response from vendor. Continued correspondence via email. Informed vendor of specific issues in WP Cost Estimation plugin.
2019-01-31: Patched version of WP Cost Estimation plugin released on CodeCanyon.

Next Steps

As usual, updating to the latest version of WP Cost Estimation should be made a priority. If your site has been running a vulnerable version of the plugin and you believe your site may have been compromised, consider working with our team on a security audit.

Some good news, sites making use of the Wordfence Firewall are in the clear. Exploits against each vulnerability described in this post are blocked by broad rules which were already built into the WAF, so free users don’t even need to wait. Still, even as a Wordfence user, we recommend performing all available plugin and theme updates to ensure the security of your site.

Conclusion

To recap, our team identified attacks against outdated versions of the WP Cost Estimation & Payment Forms Builder plugin for WordPress. After review, we identified additional flaws and reported these to the developer who quickly released a patch. We recommend all users of this plugin update to the latest version as soon as possible.

For any questions regarding our vulnerability disclosure process, please check out our official policy.

Credits: Initial attack data discovered by Security Analyst Nate Smith. Vulnerability assessment and vendor correspondence by Threat Analyst Mikey Veenstra. Thanks to Charly Biscay of Loopus Plugins for the cooperation and quick patch release.

Did you enjoy this post? Share it!

Comments

2 Comments
  • Love these posts! You guys should offer some service to plugin authors to "certify" their work. Do a code review, then work into their update workflow to review changes and add a little seal and link to your review page on that plugin. Probably get some revenue out of it, they'd get a boost for being security conscious and for those that don't know about your products and services it'd be a great introduction! :)

    Keep up the great work!

  • I second Terrace's comment! +1