CVE-2024-0685 Ninja Contact Forms Data Export SQLi

The Ninja Forms Contact Form Plugin for WordPress is susceptible to an SQL injection vulnerability when processing data export requests.

tldr;

The Ninja Forms Contact Form Plugin for WordPress is susceptible to an SQL injection vulnerability when processing data export requests. By crafting a malicious email address containing single quotes, an attacker can escape the query encapsulation and inject arbitrary SQL. This vulnerability gains an intersting twist under GDPR, as data export requests are a legal requirement, potentially making this much more likely to be exploitable.

Affected Versions: <=3.7.1
CVSS Score: 5.9
Links: Mitre, NVD
Active installations: 800,000+
Bounty: $165 (WordFence)

About Ninja Contact Forms

Ninja Contact Forms is a popular drag-and-drop form builder for WordPress, known for its user-friendly interface and flexibility in creating various types of forms. It's widely used due to its ease of integration with other WordPress plugins and services.

Vulnerability

In the Ninja Contacts Form code, the function get_subs_by_email is called when performing a data export requests. The user's email address is directly inserted into a raw SQL statement.

 private function get_subs_by_email( $email_address ) {
     global $wpdb;

     // query to find any submission with our requester's email as value
     $anon_subs_query = "SELECT DISTINCT(m.post_id) FROM `" . $wpdb->prefix
            . "postmeta` m
             JOIN `" . $wpdb->prefix . "posts` p ON p.id = m.post_id
             WHERE m.meta_value = '" . $email_address . "'
             AND p.post_type = 'nf_sub'";

SQL injections often occur in code where developers assume data is already safe, and concatenation is used instead of prepared statements.

🤓
RFC Side Note: Two primary RFCs define what a valid email can be, RFC 5321 governing the domain part (the bit after @) and RFC 5322 for the local part (the bit before @), these supeced an older RFC 822

According to RFC 5322, the local (first) part of email addresses (before @domain) can contain several special characters, including single quotes, forward slashes, asterisks and several others. This gives interesting potential attack vectors including but not limited to: XSS, SQL injection and path traversal.

🗒️
Note that not all email providers will allow all special characters according to RFC5321 specifications. Testing with a custom domain on ProtonMail showed they allowed these chars and a maximum length of around 250 chars.

So now we can escape the string boundaries in SQL query and while spaces are not valid, SQL comments /**/ can be used instead to separate keywords. So a malcious email address could be crafted look like this:

QuiRkyEmAil'/**/OR/**/1!='@stealthcopter.com

This would make the where clause of the SQL statement become the following:

WHERE m.meta_value = 'QuiRkyEmAil'/**/OR/**/1!='@stealthcopter.com'

This will result in every single row in the table being returned.

PHP / WordPress Validations

It's important to understand how sanitization and validation functions in WordPress and PHP handle potentially malicious email addresses. The WordPress function is_email does not adhere to the RFC standard and will reject email addresses containing single quotes. On the other hand, the sanitize_email function in WordPress, while still not fully conforming fully to the RFC, permits some special characters, including single quotes. In contrast, PHP's filter_var function with FILTER_VALIDATE_EMAIL is more aligned with the RFC standards and will allow all valid email addresses.

Exploitation

The data export process is typically manual and performed by an admin level user. This may seem unlikely to occur, however, under GDPR and other similar regulations data export requests must be performed upon requests from users.

A succesfully exploit of this attack results in a data export containing all user data, posing a significant privacy and security risk as shown in the export below:

Timeline

  • 17/01/24 ( 0 day) - Discovery and disclosure to WordFence
  • 18/01/24 (+1 day) - WordFence validated but marked as out of scope as an admin level user must perform the exploit
  • 19/01/24 (+2 days) - WordFence agreed to raise the impact due to the GDPR argument, giving a bounty of $165
  • 29/01/24 (+12 days) - Ninja Forms fix published (version 3.7.2)
  • 01/02/24 (+15 days) - Vulnerability Published

Conclusion

This vulnerability highlights the importance of sanitizing all user inputs, even those that developers might assume to be safe, like email addresses. It's a classic example of second-order SQL injection, where the exploit occurs not at the point of input but at a later stage in data handling. Developers should always use parameterized queries or prepared statements to prevent such vulnerabilities. This case serves as a reminder that security is a continuous process, requiring vigilance at every stage of development and data processing.