CVE-2026-25316

CartFlows – Checkout & Funnel Builder for WooCommerce <= 2.1.19 - Authenticated (Administrator+) PHP Object Injection

mediumDeserialization of Untrusted Data
6.6
CVSS Score
6.6
CVSS Score
medium
Severity
2.2.0
Patched in
99d
Time to patch

Description

The CartFlows – Checkout & Funnel Builder for WooCommerce plugin for WordPress is vulnerable to PHP Object Injection in versions up to, and including, 2.1.19 via deserialization of untrusted input. This makes it possible for authenticated attackers, with administrator-level access and above, to inject a PHP Object. No known POP chain is present in the vulnerable software. If a POP chain is present via an additional plugin or theme installed on the target system, it could allow the attacker to delete arbitrary files, retrieve sensitive data, or execute code.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
High
Privileges Required
High
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=2.1.19
PublishedJanuary 26, 2026
Last updatedMay 4, 2026
Affected plugincartflows

What Changed in the Fix

Changes introduced in v2.2.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-25316 - CartFlows PHP Object Injection ## 1. Vulnerability Summary The **CartFlows** plugin (<= 2.1.19) is vulnerable to **PHP Object Injection** due to the use of insecure deserialization on untrusted user input within its administrative AJAX handlers. Speci…

Show full research plan

Vulnerability Research Plan: CVE-2026-25316 - CartFlows PHP Object Injection

1. Vulnerability Summary

The CartFlows plugin (<= 2.1.19) is vulnerable to PHP Object Injection due to the use of insecure deserialization on untrusted user input within its administrative AJAX handlers. Specifically, the save_global_settings action in CartflowsAdmin\AdminCore\Ajax\CommonSettings processes user-supplied configuration data and stores it via AdminHelper::update_admin_settings_option. In vulnerable versions, the sanitization or storage logic (likely within sanitize_form_inputs or the update_admin_settings_option helper) incorrectly applies maybe_unserialize() to input strings, allowing an attacker to trigger unserialize() by providing a crafted PHP serialized object.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: cartflows_save_global_settings
  • Method: POST
  • Authentication: Required (Administrator or user with cartflows_manage_settings capability).
  • Vulnerable Parameter: _cartflows_facebook (or any integration field in the integrations tab).
  • Nonce: security parameter, verified against action cartflows_save_global_settings.

3. Code Flow

  1. Entry Point: The AJAX action wp_ajax_cartflows_save_global_settings is registered in admin-core/ajax/common-settings.php via register_ajax_events().
  2. Permission Check: save_global_settings() verifies current_user_can( 'cartflows_manage_settings' ).
  3. Nonce Verification: check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) is called.
  4. Route Logic: If $_POST['setting_tab'] is set to 'integrations', save_integration_settings() is invoked.
  5. Vulnerable Sink: save_integration_settings() takes input from $_POST['_cartflows_facebook'], unslashes it, and passes it to $this->sanitize_form_inputs().
  6. Data Persistence: The "sanitized" data is passed to AdminHelper::update_admin_settings_option('_cartflows_facebook', $new_settings, false), where it is eventually processed by a function that interprets the string as serialized data if it matches the O:.. or a:.. pattern, triggering unserialize().

4. Nonce Acquisition Strategy

The nonce is localized for the CartFlows admin interface. To obtain it:

  1. Identify Trigger: The settings page loads the necessary scripts.
  2. Access Admin: Navigate to the CartFlows dashboard.
  3. Variable Extraction: The plugin localizes its settings in the cartflows_admin or cartflows_admin_common object. Based on the AJAX class structure, the nonce is likely found in cartflows_admin.

Steps for PoC Agent:

  1. Navigate to /wp-admin/admin.php?page=cartflows&path=settings.
  2. Use browser_eval to extract the nonce:
    browser_eval("window.cartflows_admin?.save_global_settings_nonce")
    (Note: If the above is undefined, check window.cartflows_admin_common?.save_global_settings_nonce).

5. Exploitation Strategy

Step 1: Authentication and Nonce Retrieval

  • Log in as an administrator.
  • Navigate to the settings page and extract the security nonce for cartflows_save_global_settings.

Step 2: Payload Delivery

Send a POST request to the AJAX endpoint with a serialized object. While no specific POP chain is identified in CartFlows, we can use a standard WordPress core class (like WP_Block_Type_Registry or a simple stdClass) to confirm deserialization.

  • URL: http://<target>/wp-admin/admin-ajax.php
  • Header: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=cartflows_save_global_settings&
    security=<NONCE>&
    setting_tab=integrations&
    _cartflows_facebook[facebook_pixel_id]=O:8:"stdClass":0:{}
    

Step 3: Verification

A successful injection will result in the stdClass object being stored or processed. Since POI is often "blind" without a POP chain, we verify by checking if the input was accepted and if the application behavior changes (e.g., error logs showing serialization errors if the object is invalid for its context).

6. Test Data Setup

  1. Plugin Installation: Ensure CartFlows <= 2.1.19 is active.
  2. User Creation: An administrator user must exist.
  3. Capability Check: Ensure the user has the cartflows_manage_settings capability (default for Admins).

7. Expected Results

  • The server returns a JSON success response: {"success":true,"data":{"messsage":"Successfully saved data!"}}.
  • If a logging mechanism is attached to unserialize, it will trigger.
  • If an invalid/malformed object is sent, PHP might throw a warning (if WP_DEBUG is on) indicating an error during the unserialize() call.

8. Verification Steps (Post-Exploit)

Confirm the object was injected by checking the option value via WP-CLI:

wp option get _cartflows_facebook --format=yaml

If the output shows a PHP object or a serialized string that was interpreted by WordPress, the injection was successful.

9. Alternative Approaches

If the integrations tab is patched or restricted, try the save_flow_meta_settings action in admin-core/ajax/flows.php:

  • Action: cartflows_save_flow_meta_settings
  • Nonce: cartflows_save_flow_meta_settings
  • Vulnerable Sink: MetaOps::save_meta_fields( $flow_id, $post_meta, ... ).
  • Payload Location: Any meta field associated with the flow.
  • Requirement: Requires a valid flow_id (create one via wp post create --post_type=cartflows_flow).
Research Findings
Static analysis — not yet PoC-verified

Summary

The CartFlows plugin for WordPress is vulnerable to PHP Object Injection in versions up to 2.1.19. This occurs because administrative AJAX handlers, such as those for saving global settings and importing data, process user-supplied input using the insecure maybe_unserialize() function without restricting allowed classes. Authenticated administrators can exploit this to inject arbitrary PHP objects, which could lead to remote code execution or file manipulation if a suitable POP chain is available on the site.

Vulnerable Code

/* admin-core/ajax/common-settings.php line 188 */
public function save_integration_settings() {

	$new_settings = array();

	if ( isset( $_POST['_cartflows_facebook'] ) ) { //phpcs:ignore
		$new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_facebook'] ) ); //phpcs:ignore
		AdminHelper::update_admin_settings_option( '_cartflows_facebook', $new_settings, false );
	}

---

/* admin-core/ajax/importer.php line 1325 */
if ( is_serialized( $meta_value, true ) ) {
	$raw_data = maybe_unserialize( stripslashes( $meta_value ) );
}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/cartflows/2.1.19/admin-core/ajax/importer.php	2025-06-26 13:33:38.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/cartflows/2.2.0/admin-core/ajax/importer.php	2026-01-27 08:41:56.000000000 +0000
@@ -1323,7 +1323,12 @@
 			if ( $meta_value ) {
 
 				if ( is_serialized( $meta_value, true ) ) {
-					$raw_data = maybe_unserialize( stripslashes( $meta_value ) );
+					// Security: Using unserialize with allowed_classes=>false to prevent object injection.
+					$raw_data = unserialize( stripslashes( $meta_value ), array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound
+					// Drop malicious payloads completely to prevent fatal errors.
+					if ( false === $raw_data || is_object( $raw_data ) ) {
+						$raw_data = '';
+					}
 				} elseif ( is_array( $meta_value ) ) {
 					$raw_data = json_decode( stripslashes( $meta_value ), true );
 				} else {

Exploit Outline

The exploit requires an authenticated user with administrator privileges (or the `cartflows_manage_settings` capability). An attacker first retrieves a security nonce for the `cartflows_save_global_settings` action from the plugin's settings page (localized in the `cartflows_admin` JavaScript object). The attacker then sends a POST request to `/wp-admin/admin-ajax.php` with the `action` set to `cartflows_save_global_settings` and `setting_tab` set to `integrations`. The payload is delivered via any field within the integrations settings, such as `_cartflows_facebook[facebook_pixel_id]`, containing a serialized PHP object string. When the plugin processes the save request, it passes the unsanitized serialized string to a function that calls `maybe_unserialize()`, resulting in the instantiation of the injected object.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.