Update 2023-06-12: You no longer need the snippet. WPManageNinja patched the vulnerability two hours after public disclosure (93 days after reporting). 

Update 2024-01-27: The related issue with with everlasting hash values is now fully addressed. 

# Responsible disclosure of unpatched vulnerability CVE-2023-1430 in FluentCRM by WPManageNinja

_tl;dr Attackers can view and edit contact details in FluentCRM. WPManageNinja hasn’t patched the vulnerability within the 90-day responsible disclosure time window. I provide a mitigation snippet to prevent vulnerability exploitation while waiting for an official patch._

- Vulnerability: CVE-2023-1430 Insufficient Use of Hash as Authorization Control
- CVSS: 6.5 (Medium)
- Software: FluentCRM
- Affected versions: vulnerability detected in 2.7.40
- Patched version: 2.8.02
- Developer: WPManageNinja
- Researcher: Karl Emil Nikka, Nikka Systems (reported through [Wordfence](
- Publicly published: 2023-06-12
- Last updated: 2023-06-12

## Overview

Today, I’m publishing information about a vulnerability I found in the popular WordPress plugin FluentCRM by WPManageNinja. The vulnerability, CVE-2023-1430, is caused by FluentCRM’s insufficient use of an email address hash as authorization control. I responsibly disclosed the vulnerability according to Google Zero’s vulnerability disclosure policy. WPManageNinja has neither provided a patch within the 90-day window nor requested a time extension. 

In this report, _contact_ refers to a FluentCRM contact object, while _user_ refers to a WordPress user object. A contact can be linked to a user, but it isn’t a requirement. Details about exploiting the vulnerability are withheld until an official patch is available. Security professionals can reach out to me for the full report ( 

## General impact and required actions

On sites running FluentCRM, an attacker can view and edit a contact’s name, email address, and list setting by knowing the contact’s email address. Since the contact’s name is often included in newsletters through merge tags, an attacker can replace the contact’s name with foul language, causing the website owner to send vulgar newsletters. If the website admin has enabled FluentCRM’s shortcode for managing preferences and added it to a public web page, an attacker can view and edit all exposed personal information, i.e., title, phone number, date of birth, and address (depending on FluentCRM configuration).

FluentCRM is installed on more than 30 000 sites. Site admins for FluentCRM sites can prevent vulnerability exploitation by adding my mitigation snippet to their child-theme’s functions.php file. 

The snippet doesn’t patch the vulnerability. It replaces the vulnerable content on FluentCRM’s page for unsubscribing (unsubscribe.php) and page for managing preferences (manage_subscription.php) with an error message telling the contact to reach out via email instead. The email address in the error message is the site’s admin email address (it can be changed from the snippet). The mitigation snippet also ensures that non-logged-in visitors cannot render FluentCRM’s vulnerable shortcode for managing preferences (fluentcrm_pref). A non-logged-in visitor will instead see an error message telling the visitor to log in. All strings are translatable with the provided POT file. 

### Required actions

I recommend website owners to implement either the mitigation I provide or their own corresponding mitigation. Website owners should also verify the integrity of their FluentCRM contact data and, if possible, check their logs for potential data leaks. Checking the logs is especially important for the following sites:

- sites where a contact’s registered name is sensitive information
- sites where FluentCRM’s shortcode for managing preferences is or has been present on public pages for non-logged-in visitors
- sites where list management is enabled and the name of the lists a contact subscribes to is sensitive information. 

The website’s assigned data controller shall handle potential data leaks of personal information according to laws and regulations in the affected jurisdictions. 

## Additional potential impact

If FluentCRM is configured to sync a contact’s settings to its corresponding user, an attacker can change the user’s name. Fortunately, FluentCRM does not sync email addresses from contacts to users. If FluentCRM did, this vulnerability would enable full site takeovers. An attacker would have been able to gain privileged access by changing the site admin’s email address and then resetting the admin’s password. 

However, other solutions can sync all metadata from contacts to users, e.g., WP Fusion and FluentCRM’s API. WP Fusion is probably the most popular third-party plugin for syncing contacts’ metadata between a CRM (e.g., FluentCRM) and WordPress. Luckily, the current version of WP Fusion isn’t hooked into metadata changes initialized from the vulnerable forms. I’ve informed the developers of WP Fusion, and they won’t address this limitation until WPManageNinja has patched the vulnerability. 

FluentCRM’s API can also be used to update contact and user data. Site owners who use FluentCRM’s API to update users’ email addresses must disable this update when initiated from FluentCRM’s page or shortcode for managing preferences (or add my mitigation snippet to ensure no settings can be updated from the vulnerable forms). 

## Exploiting the vulnerability

FluentCRM lets contacts unsubscribe and manage preferences from public web pages. Links to these pages are included in every newsletter. Changes made on these pages are authorized by MD5 hashes of the contact’s email addresses, passed as URL parameters. The MD5 hash of an email address is not a secret and can be calculated by anyone. An attacker can exploit the incorrect use of hashes to unsubscribe specific contacts or unsubscribe contacts with known email addresses en masse.

While the unsubscription page solely relies on the MD5 hash for authorization, the page for managing preferences requires an additional URL parameter called ce_id. In this case, the ce_id refers to the contact’s ID in the fc_subscribers table. This ID is an incrementing integer. The ce_id value is therefore easily found by testing all possible values (the search space is the site’s number of ever-registered contacts). Admin users probably have low values. 

From the page for managing preferences, an attacker can also exfiltrate the contact’s secure_hash value. By updating the contact’s email address, the contact’s “secure_hash” value is stored in a cookie called fc_hash_secure. With this cookie in place, the attacker can display all contact information made available by the FluentCRM’s preference form shortcode. 

## Related minor issue: everlasting hash values

The previously mentioned “secure_hash” is a value FluentCRM (in some situations) relies on instead of or as an alternative to the MD5 email address hash. Since the release of FluentCRM 2.8.0, the unsubscribe page exclusively relies on the secure_hash value for authorization. The page for managing preferences accepts both the new secure_hash value and the old MD5 email address hash for authorization. 

While the secure_hash value cannot be derived from the email address, FluentCRM’s usage does not follow good security practices. The secure_hash value is generated once per contact. It is never updated, and it never expires. This is problematic since the secure_hash value is included in every newsletter. If an attacker gains access to a contact’s inbox, the attacker can change the contact’s settings indefinitely. If the affected contact is linked to a user with admin privileges, and email address changes are synced from FluentCRM to WordPress, every single newsletter sent to this contact will contain a never-expiring token to take over the site. 

I reported this related issue to WPManageNinja on 2023-03-15. Two months later (2023-05-15), WPManageNinja replied that their security advisors didn’t consider the static secure_hash value an issue. WPManageNinja’s security advisors said that it was “ok to use this type of one-time generated tokens to identify the contact” and that it was “similar to API tokens of SaaS services which are not delivered to any other contacts but only sent to the actual contact who owns the email address”. 

WPManageNinja were open-minded and told me to let them know if I still thought it was a security concern, which I did. I explained why the secure_hash values couldn’t be compared to API tokens. (API tokens can be made sure are only sent over TLS connections, and access to API tokens can be restricted. That is not the case with cleartext values in emails. Most importantly, API tokens can be revoked while there is no way for a contact to revoke a secure_hash value.)

Later that same day, WPManageNinja thanked me and said they considered combining the email record ID with the hash. That will solve the issue if implemented in conjunction with automatic revocation of either old (time-based expiration) or previous (counter-based expiration) secure_hash values. This feature has not yet been implemented, but I don’t consider using static hash values a part of this CVE.

## Timeline

- 2023-03-11 I reported the vulnerability to WPManageNinja. At this point, I had only found the vulnerability on the unsubscription page.
- 2023-03-13 WPManageNinja acknowledged that they had received my report.
- 2023-03-14 WPManageNinja’s developers denied using MD5 hashes of email addresses for authorization, stating that they were using wp_generate_uuid4 tokens.
- 2023-03-14 I explained and proved that they relied on MD5 hashes of email addresses.
- 2023-03-15 Due to WPManageNinja’s initial response, I dug deeper and found the same vulnerability on the page for managing preferences. I reported my findings to WPManageNinja and explained why it made the vulnerability more severe. I also submitted a report to Wordfence and requested a CVE.
- 2023-03-16 WPManageNinja acknowledged that they had received my updated report.
- 2023-03-16 Wordfence confirmed the vulnerability and assigned it CVE-2023-1430.
- 2023-04-10 I sent a 30-day reminder to WPManageNinja.
- 2023-04-14 WPManageNinja released FluentCRM 2.8.0 with no mention of any security patches (just “improvements and bug fixes”).
- 2023-04-22 I informed WPManageNinja that the 2.8.0 update only fixed the vulnerability on the unsubscription page and that it persisted on the page for managing preferences.
- 2023-04-24 WPManageNinja acknowledged that they had received my updated report.
- 2023-05-14 I sent a 60-day reminder to WPManageNinja.
- 2023-05-15 WPManageNinja said they would send me a patched beta version the following week (which they never did).
- 2023-06-01 I asked WPManageNinja if I should give the developer of WP Fusion a heads-up before the public disclosure. WPManageNinja told me I didn’t have to since they would release an update following week (which they didn’t).
- 2023-06-08 I told WPManageNinja that I would delay the public disclosure to 2023-06-12 since the original public disclosure date was next to the weekend.
- 2023-06-09 I informed WPManageNinja that they had reached 90 days and that the next responsible step would be to publish information about the vulnerability so that everyone could implement mitigations while waiting. I also asked the developers of WP Fusion not to address the sync limitation until WPManageNinja had patched the vulnerability.
- 2023-06-09 Wordfence published initial details about the vulnerability, incorrectly stating that the vulnerability has been patched. This was due to a miscommunication between me a Wordfence.
- 2023-06-12 I published this report with the exploit details withheld.
- 2023-06-12 WPManageNinja patched the vulnerability two hours after public disclosure (93 days after reporting) without mentioning anything about the vulnerability in their changelog (just “Use Secure Hash instead of MD5 for subscription preference page”).
- 2023-06-12 I updated this report with information about the patch and the previously withheld details.
- 2023-06-12 WPManageNinja added information about the CVE to the plugin’s changelog.
- 2024-01-17 WPMangeNinja reached out to me to get my take on their solution to the issue with everlasting hash values. 
- 2024-01-27 WPMangeNinja released FluentCRM 2.8.40, addressing the issue with everlasting hash values and with my suggested improvements implemented.
- 2024-01-27 WPMangeNinja released FluentCRM 2.8.41, making sure a contact’s old authentication hash gets invalidated when the connected WordPress user changes password.  
- 2024-01-27 I considered the related minor issue with everlasting hash values addressed.

## Nikka Systems Academy (Project Opal) is NOT affected

In Q1 2023, we started migrating from our previous newsletter tool (Sendy) to FluentCRM. I found the vulnerability while integrating FluentCRM with Nikka Systems Academy (Project Opal). Since we replaced FluentCRM’s subscription management system with our custom plugin, FluentCRM’s vulnerable forms have never affected our site or our customers’ data. 

## Recommendations for WPManageNinja

FluentCRM is a great plugin, but WPManageNinja’s handling of the vulnerability disclosure leaves much room for improvement. The following list is my suggestion for how WPManageNinja could improve the situation.

- They should consult a third-party auditor to audit the current codebase. The CVE-2023-1430 vulnerability is a textbook example of how not to use hashes. In conjunction with WPManageNinja’s initial denial of even using MD5 email address hashes for authorization, this tells me that it’s probably time for a third-party audit of the codebase.
- They should publish a security.txt file (RFC 9116) so security researchers can contact their developers directly. They missed important mitigation days since I had to report the vulnerability through their customer support department, which initially and incorrectly dismissed the vulnerability report.
- They should establish a better procedure to patch vulnerabilities in a timely manner. An easily patched vulnerability like this should be addressed [within 30 days]( Not having a patch ready within the 90-day responsible disclosure window is unacceptable. 
- They should always disclose addressed vulnerabilities and implemented security improvements in their changelogs so that their customers know how important the updates are. 

That being said, I still have trust in WPManageNinja. There are bugs in all software, and a single mismanaged vulnerability report is not a reason to stop using their plugins. 

~~Update 2023-06-12: The fact that they still tried to hide the vulnerability in their changelog makes me genuinely worried.~~ (They’ve now added the CVE.)

## Changelog

- 2023-06-12 Initial publication. 
- 2023-06-12 Updated with information about patch availability and previously withheld exploitation details.
- 2023-06-12 Added to timeline: WPManageNinja adds information about the CVE to the plugin’s changelog.
- 2023-06-12 Fixed spelling errors. CVSS bumped up to 6.5 by Wordfence.
- 2024-01-27 Added info about how FluentCRM 2.8.40 and 2.8.41 addressed the related minor issue with everlasting hash values.