High Severity Vulnerability Patched in Ninja Forms

On April 27, 2020, the Wordfence Threat Intelligence team discovered a Cross-Site Request Forgery(CSRF) vulnerability in Ninja Forms, a WordPress plugin with over 1 million installations. This vulnerability could allow an attacker to trick an administrator into importing a contact form containing malicious JavaScript and replace any existing contact form with the malicious version.

We reached out to Ninja Form’s security team according to their Responsible Disclosure Guidelines and they replied within a few hours. The plugin was patched less than 24 hours after our initial contact, on April 28, 2020.

All Wordfence users, including both Wordfence Premium and free Wordfence users, are protected from XSS attempts against this vulnerability by the Wordfence Firewall’s built-in XSS protection.

Description: Cross-Site Request Forgery to Stored Cross-Site Scripting
Affected Plugin: Ninja Forms
Plugin Slug: ninja-forms
Affected Versions: < 3.4.24.2
CVE ID: CVE-2020-12462
CVSS Score: 8.8 (High)
CVSS Vector:CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Fully Patched Version: 3.4.24.2

The Ninja Forms plugin features a “legacy” mode which allows users to revert its styling and features to those of the plugin’s final 2.9.x version. As part of this feature, it adds several AJAX functions which appear to be intended to import forms and fields between the “legacy” mode and the default mode. While all of these functions used capability checks, two of the functions failed to check nonces, which are used to verify that a request was intentionally sent by a legitimate user. One function in particular, ninja_forms_ajax_import_form, allowed importing forms containing custom HTML:

add_action( 'wp_ajax_ninja_forms_ajax_import_form', 'ninja_forms_ajax_import_form');functionninja_forms_ajax_import_form(){if( ! current_user_can( apply_filters( 'ninja_forms_admin_upgrade_import_form_capabilities', 'manage_options') ) ) return;$import= stripslashes( $_POST[ 'import'] );$form_id= ( isset( $_POST[ 'formID'] ) ) ? absint( $_POST[ 'formID'] ) : '';WPN_Helper::delete_nf_cache( $form_id); // Bust the cache.Ninja_Forms()->form()->import_form( $import, TRUE, $form_id, TRUE );if( isset( $_POST[ 'flagged'] ) && $_POST[ 'flagged'] ){$form= Ninja_Forms()->form( $form_id)->get();$form->update_setting( 'lock', TRUE );$form->save();}echojson_encode( array( 'export'=> WPN_Helper::esc_html($_POST['import']), 'import'=> $import) );wp_die();}

As such, if an attacker was able to trick an administrator into clicking a crafted link, they could spoof a request using that administrator’s session and import a form containing malicious JavaScript into the site. Worse yet, it was possible to replace any existing form on the site with one of these imported forms by setting the formID $_POST parameter to the ID of an existing form.

Depending on where the JavaScript was placed in the imported form, it could be executed in a victim’s browser whenever they visited a page containing the form, whenever an Administrator visited the plugin’s Import/Export page, or whenever an Administrator attempted to edit any of the form’s fields. As is typical with Cross-Site Scripting (XSS) attacks, a malicious script executed in an Administrator’s browser could be used to add new administrative accounts, leading to complete site takeover, while a malicious script executed in a visitor’s browser could be used to redirect that visitor to a malicious site.

Vulnerability Disclosure Policies are Important

One of the reasons this plugin was patched so quickly was because the plugin’s team maintains a Responsible Security Disclosure Policy, often referred to as a Vulnerability Disclosure Policy. This allowed us to contact them directly with our full disclosure rather than spending days trying to find or verify the appropriate contact channel. While we have occasionally seen plugins patched in less than 24 hours in the past, responses like this are exceptional and indicate a serious dedication to security.

If you are responsible for any kind of software product or service, having a Vulnerability Disclosure Policy (VDP) not only improves your chances of being alerted to serious security issues, but also allows you to set expectations for your response. Most importantly, it reduces the risk of vulnerabilities in your products being prematurely or irresponsibly disclosed and attacked by bad actors before you have a chance to fix them. For these reasons, we strongly recommend implementing a VDP to improve not only the efficiency of your response to specific flaws, but also the general security of your product.

Timeline

April 27, 2020 19:00 UTC – Our Threat Intelligence Team discovers and analyzes the vulnerability and verifies that our existing Firewall Rules provide sufficient protection against XSS.
April 27, 2020 19:24 UTC – We provide full disclosure to the plugin’s developer as per their Responsible Security Disclosure Policy.
April 27, 2020 20:27 UTC – We receive a response that a patch should be available the next day.
April 28, 2020 19:00 UTC – Patched version of the plugin released.