CVE-2026-22480

WebToffee WooCommerce Product Feeds – Google Shopping, Pinterest, TikTok Ads, & More <= 2.3.3 - Authenticated (Shop manager+) PHP Object Injection

mediumDeserialization of Untrusted Data
6.6
CVSS Score
6.6
CVSS Score
medium
Severity
2.3.4
Patched in
8d
Time to patch

Description

The WebToffee WooCommerce Product Feeds – Google Shopping, Pinterest, TikTok Ads, & More plugin for WordPress is vulnerable to PHP Object Injection in versions up to, and including, 2.3.3 via deserialization of untrusted input. This makes it possible for authenticated attackers, with shop manager-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.3.3
PublishedMarch 5, 2026
Last updatedMarch 12, 2026
Affected pluginwebtoffee-product-feed

What Changed in the Fix

Changes introduced in v2.3.4

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-22480 - WebToffee WooCommerce Product Feeds PHP Object Injection ## 1. Vulnerability Summary The **WebToffee WooCommerce Product Feeds** plugin (up to version 2.3.3) is vulnerable to **PHP Object Injection** due to the insecure use of `maybe_unserialize()` on …

Show full research plan

Exploitation Research Plan: CVE-2026-22480 - WebToffee WooCommerce Product Feeds PHP Object Injection

1. Vulnerability Summary

The WebToffee WooCommerce Product Feeds plugin (up to version 2.3.3) is vulnerable to PHP Object Injection due to the insecure use of maybe_unserialize() on user-controlled data. Specifically, the export() method in the Webtoffee_Product_Feed_Sync_Export_Ajax class processes the form_data POST parameter without sufficient validation or prior sanitization, allowing an authenticated attacker (Shop Manager or higher) to inject arbitrary PHP objects into the application scope.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: wt_pf_export_ajax (inferred from plugin module structure and naming conventions)
  • Vulnerable Parameter: form_data
  • Authentication Level: Authenticated (Shop Manager or Admin). Shop managers have the manage_woocommerce capability, which is typically checked by this plugin via Wt_Pf_Sh::check_write_access().
  • Preconditions: The attacker must obtain a valid WordPress nonce associated with the webtoffee_product_feed action.

3. Code Flow

  1. An AJAX request is sent to admin-ajax.php with the action wt_pf_export_ajax.
  2. The plugin's AJAX dispatcher instantiates the class Webtoffee_Product_Feed_Sync_Export_Ajax (located in admin/modules/export/classes/class-export-ajax.php).
  3. The dispatcher calls the export() method (or routes via a step parameter like steps[]=export).
  4. Inside admin/modules/export/classes/class-export-ajax.php, the export() method is executed:
    public function export($out) {
        $offset=(isset($_POST['offset']) ? intval($_POST['offset']) : 0);
        // ...
        if( 0 == $offset ) /* first batch */
        {
            /* process form data */
            $form_data=(isset($_POST['form_data']) ? Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata(maybe_unserialize(wp_unslash($_POST['form_data']))) : array());
            // ...
        }
    }
    
  5. wp_unslash($_POST['form_data']) retrieves the raw serialized string.
  6. maybe_unserialize() is called on this string, triggering the instantiation of any PHP objects defined in the payload if their classes are loaded.

4. Nonce Acquisition Strategy

The plugin uses a centralized security helper Wt_Pf_Sh::check_write_access(WEBTOFFEE_PRODUCT_FEED_ID) where WEBTOFFEE_PRODUCT_FEED_ID is 'webtoffee_product_feed'. The nonce is localized for the admin interface.

  1. Identify Page: The "Product Feed" export page is usually found at wp-admin/admin.php?page=wt-product-feed&view=export.
  2. Locate Nonce: The nonce is stored in a JavaScript object localized by the plugin, typically named wt_pf_common_params.
  3. Extraction:
    • Navigate to the Export page: browser_navigate("http://localhost:8080/wp-admin/admin.php?page=wt-product-feed&view=export").
    • Extract the nonce: browser_eval("window.wt_pf_common_params?.nonce").
    • Alternatively, check for wt_pf_export_params.nonce.

5. Exploitation Strategy

The exploit involves sending a crafted AJAX request containing a serialized PHP object in the form_data parameter.

  • Request Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: wt_pf_export_ajax
    • steps[]: export
    • offset: 0
    • _wpnonce: [EXTRACTED_NONCE]
    • form_data: [SERIALIZED_PAYLOAD] (e.g., O:8:"stdClass":0:{} for testing)

Payload Construction:
Since the goal is to prove Object Injection, we can use a basic stdClass or, if searching for a POP chain, look for classes with __destruct or __wakeup methods in WordPress core or WooCommerce.

Research Findings
Static analysis — not yet PoC-verified

Summary

The plugin is vulnerable to PHP Object Injection via the `form_data` parameter used in product export and synchronization tasks. This occurs because the plugin uses `maybe_unserialize()` on user-controlled input without validation, allowing authenticated attackers with Shop Manager or higher privileges to inject arbitrary PHP objects.

Vulnerable Code

// admin/modules/export/classes/class-export-ajax.php
public function export($out)
{
    // ... (lines 191-196)
    if( 0 == $offset ) /* first batch */
    {
        /* process form data */
        $form_data=(isset($_POST['form_data']) ? Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata(maybe_unserialize(wp_unslash($_POST['form_data']))) : array()); //phpcs:ignore
---
// admin/modules/cron/cron.php
// ... (lines 1122)
/* process form data */
$form_data=(isset($_POST['form_data']) ? Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata(maybe_unserialize(wp_unslash($_POST['form_data']))) : array()); //phpcs:ignore

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/webtoffee-product-feed/2.3.3/admin/modules/cron/cron.php /home/deploy/wp-safety.org/data/plugin-versions/webtoffee-product-feed/2.3.4/admin/modules/cron/cron.php
--- /home/deploy/wp-safety.org/data/plugin-versions/webtoffee-product-feed/2.3.3/admin/modules/cron/cron.php	2025-11-17 04:43:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/webtoffee-product-feed/2.3.4/admin/modules/cron/cron.php	2025-12-22 13:12:50.000000000 +0000
@@ -1119,7 +1119,9 @@
 		}
 
 		/* process form data */
-		$form_data=(isset($_POST['form_data']) ? Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata(maybe_unserialize(wp_unslash($_POST['form_data']))) : array()); //phpcs:ignore
+		$form_data = isset( $_POST['form_data'] ) ? Webtoffee_Product_Feed_Sync_Common_Helper::wt_decode_data( $_POST['form_data'] ) : array(); //phpcs:ignore
+		/* process form data */
+		$form_data = Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata($form_data);
 
 		/* loading export module class object */
 		$this->module_obj=Webtoffee_Product_Feed_Sync::load_modules($action_type);
@@ -1359,7 +1361,9 @@
         $cron_form_details = maybe_unserialize($cron_details['data']);
 
         /* process form data */
-        $form_data = (isset($_POST['form_data']) ? Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata(maybe_unserialize(wp_unslash($_POST['form_data']))) : array()); //phpcs:ignore
+		$form_data = isset( $_POST['form_data'] ) ? Webtoffee_Product_Feed_Sync_Common_Helper::wt_decode_data( $_POST['form_data'] ) : array(); //phpcs:ignore
+		/* process form data */
+		$form_data = Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata($form_data);
 
         /* loading export module class object */
         $this->module_obj = Webtoffee_Product_Feed_Sync::load_modules($action_type);
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/webtoffee-product-feed/2.3.3/admin/modules/export/classes/class-export-ajax.php /home/deploy/wp-safety.org/data/plugin-versions/webtoffee-product-feed/2.3.4/admin/modules/export/classes/class-export-ajax.php
--- /home/deploy/wp-safety.org/data/plugin-versions/webtoffee-product-feed/2.3.3/admin/modules/export/classes/class-export-ajax.php	2025-11-17 04:43:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/webtoffee-product-feed/2.3.4/admin/modules/export/classes/class-export-ajax.php	2025-12-22 13:12:50.000000000 +0000
@@ -193,7 +193,10 @@
 		if( 0 == $offset ) /* first batch */
 		{
 			/* process form data */
-			$form_data=(isset($_POST['form_data']) ? Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata(maybe_unserialize(wp_unslash($_POST['form_data']))) : array()); //phpcs:ignore
+			$form_data = isset( $_POST['form_data'] ) ? Webtoffee_Product_Feed_Sync_Common_Helper::wt_decode_data( $_POST['form_data'] ) : array(); //phpcs:ignore
+			/* process form data */
+			$form_data = Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata($form_data);
+
 			//sanitize form data
 			$form_data=Wt_Pf_IE_Basic_Helper::sanitize_formdata($form_data, $this->export_obj);
 						
@@ -300,8 +303,10 @@
 
 			$tb=$wpdb->prefix. Webtoffee_Product_Feed_Sync::$template_tb;
 			
+			/* decode data */
+			$form_data = isset( $_POST['form_data'] ) ? Webtoffee_Product_Feed_Sync_Common_Helper::wt_decode_data( $_POST['form_data'] ) : array(); //phpcs:ignore
 			/* process form data */
-			$form_data=(isset($_POST['form_data']) ? Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata(maybe_unserialize(wp_unslash($_POST['form_data']))) : array()); //phpcs:ignore
+			$form_data = Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata($form_data);
 
 			//sanitize form data
 			$form_data=Wt_Pf_IE_Basic_Helper::sanitize_formdata($form_data, $this->export_obj);
@@ -666,7 +671,9 @@
 		$template_data=$this->get_mapping_template_by_id($id);
 		if($template_data)
 		{
-			$decoded_form_data=Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata(maybe_unserialize($template_data['data']));
+			$decoded_form_data = isset( $template_data['data'] ) ? Webtoffee_Product_Feed_Sync_Common_Helper::wt_decode_data( $template_data['data'] ) : array(); //phpcs:ignore
+			/* process form data */
+			$decoded_form_data = Webtoffee_Product_Feed_Sync_Common_Helper::process_formdata($decoded_form_data);
 			$this->selected_template_form_data=(!is_array($decoded_form_data) ? array() : $decoded_form_data);
 		}
 	}

Exploit Outline

1. Gain authenticated access to a WordPress site with at least Shop Manager privileges. 2. Access the Product Feed export or configuration page to retrieve a valid WordPress nonce (usually named `webtoffee_product_feed`). 3. Construct a malicious serialized PHP object payload. 4. Send a POST request to `wp-admin/admin-ajax.php` with the action `wt_pf_export_ajax` and include the serialized payload in the `form_data` parameter. 5. If a suitable POP chain is present in the environment (e.g., in other plugins or themes), the object injection will trigger during the `maybe_unserialize` call, leading to arbitrary code execution or file deletion.

Check if your site is affected.

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