
Responsible disclosure of unpatched vulnerability in FluentCRM by WPManageNinja
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.
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.
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 ([email protected]).
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.
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:
The website’s assigned data controller shall handle potential data leaks of personal information according to laws and regulations in the affected jurisdictions.
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).
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.
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.