CartFlows – Checkout & Funnel Builder for WooCommerce <= 2.1.19 - Authenticated (Administrator+) PHP Object Injection
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:HTechnical Details
What Changed in the Fix
Changes introduced in v2.2.0
Source Code
WordPress.org SVN# 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_settingscapability). - Vulnerable Parameter:
_cartflows_facebook(or any integration field in theintegrationstab). - Nonce:
securityparameter, verified against actioncartflows_save_global_settings.
3. Code Flow
- Entry Point: The AJAX action
wp_ajax_cartflows_save_global_settingsis registered inadmin-core/ajax/common-settings.phpviaregister_ajax_events(). - Permission Check:
save_global_settings()verifiescurrent_user_can( 'cartflows_manage_settings' ). - Nonce Verification:
check_ajax_referer( 'cartflows_save_global_settings', 'security', false )is called. - Route Logic: If
$_POST['setting_tab']is set to'integrations',save_integration_settings()is invoked. - Vulnerable Sink:
save_integration_settings()takes input from$_POST['_cartflows_facebook'], unslashes it, and passes it to$this->sanitize_form_inputs(). - 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 theO:..ora:..pattern, triggeringunserialize().
4. Nonce Acquisition Strategy
The nonce is localized for the CartFlows admin interface. To obtain it:
- Identify Trigger: The settings page loads the necessary scripts.
- Access Admin: Navigate to the CartFlows dashboard.
- Variable Extraction: The plugin localizes its settings in the
cartflows_adminorcartflows_admin_commonobject. Based on the AJAX class structure, the nonce is likely found incartflows_admin.
Steps for PoC Agent:
- Navigate to
/wp-admin/admin.php?page=cartflows&path=settings. - Use
browser_evalto extract the nonce:browser_eval("window.cartflows_admin?.save_global_settings_nonce")
(Note: If the above is undefined, checkwindow.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
securitynonce forcartflows_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
- Plugin Installation: Ensure CartFlows <= 2.1.19 is active.
- User Creation: An administrator user must exist.
- Capability Check: Ensure the user has the
cartflows_manage_settingscapability (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_DEBUGis on) indicating an error during theunserialize()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 viawp post create --post_type=cartflows_flow).
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
@@ -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.