CVE-2026-39498

YayMail – WooCommerce Email Customizer <= 4.3.3 - Authenticated (Shop manager+) PHP Object Injection

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

Description

The YayMail – WooCommerce Email Customizer plugin for WordPress is vulnerable to PHP Object Injection in versions up to, and including, 4.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<=4.3.3
PublishedApril 20, 2026
Last updatedApril 30, 2026
Affected pluginyaymail

What Changed in the Fix

Changes introduced in v4.3.4

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-39498 (YayMail PHP Object Injection) ## 1. Vulnerability Summary The **YayMail – WooCommerce Email Customizer** plugin (<= 4.3.3) is vulnerable to **PHP Object Injection** via the deserialization of untrusted input. The vulnerability exists because certain AJ…

Show full research plan

Exploitation Research Plan - CVE-2026-39498 (YayMail PHP Object Injection)

1. Vulnerability Summary

The YayMail – WooCommerce Email Customizer plugin (<= 4.3.3) is vulnerable to PHP Object Injection via the deserialization of untrusted input. The vulnerability exists because certain AJAX endpoints or settings-saving mechanisms take user-provided strings and pass them to unserialize() or maybe_unserialize() without sufficient validation. While the plugin itself may not contain a direct POP (Property-Oriented Programming) chain, an attacker can leverage POP chains in other active plugins or WordPress core to achieve remote code execution (RCE), file deletion, or sensitive data retrieval.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • AJAX Action: yaymail_import_template (Inferred as the primary vector for "import" functionality) or yaymail_duplicate_template.
  • Vulnerable Parameter: template or data (Inferred).
  • Authentication: Authenticated, minimum role: Shop Manager.
  • Capability Required: manage_woocommerce (standard for Shop Managers and allows access to YayMail).
  • Preconditions: The attacker must be logged in as a Shop Manager or Administrator.

3. Code Flow (Inferred)

  1. Entry Point: An authenticated user with manage_woocommerce capability triggers an AJAX request with the action yaymail_import_template.
  2. Controller: The request is handled by a controller (likely YayMail\Controller\Customizer or YayMail\Page\Customizer).
  3. Vulnerable Call: The handler retrieves a POST parameter (e.g., template) which contains a serialized string representing email template settings.
  4. Sink: The code calls unserialize($template) or maybe_unserialize($template) on this raw input.
  5. Object Injection: PHP instantiates the objects defined in the serialized string, triggering __wakeup(), __destruct(), or other magic methods if a POP chain is present in the environment.

4. Nonce Acquisition Strategy

YayMail localizes its configuration and security nonces in the WordPress admin head. Based on the provided manifest.json and typical plugin behavior:

  1. Identify Trigger: The YayMail Customizer interface enqueues its scripts on its main admin page.
  2. Target Page: /wp-admin/admin.php?page=yaymail-customizer
  3. Extraction Method:
    • Use browser_navigate to go to the YayMail Customizer page as a Shop Manager.
    • Use browser_eval to extract the nonce from the global JavaScript object.
  4. Inferred JS Variable: In YayMail, the localization object is typically yaymail_localize or yayMailSettings.
  5. JS Command:
    // Check common localization keys
    window.yaymail_localize?.nonce || window.yayMailSettings?.nonce
    

5. Exploitation Strategy

The exploit involves sending a crafted serialized PHP object to the vulnerable AJAX handler.

Step-by-Step Plan:

  1. Login: Authenticate as a user with the Shop Manager role.
  2. Navigate: Navigate to /wp-admin/admin.php?page=yaymail-customizer to ensure the session and nonces are initialized.
  3. Extract Nonce: Execute browser_eval("window.yaymail_localize.nonce") to retrieve the nonce for AJAX actions.
  4. Craft Payload:
    • Since no internal POP chain is confirmed, use a generic "Object Injection" detector or a common core chain (like WP_Block_List or WP_Theme if applicable for the PHP version).
    • Generic Payload (to confirm injection): O:8:"stdClass":1:{s:3:"foo";s:3:"bar";}
  5. Send Request: Execute an AJAX POST request using the http_request tool.

Example Request:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=yaymail_import_template&nonce=[NONCE]&template=[SERIALIZED_PAYLOAD]
    
    (Note: If yaymail_import_template is not the correct action, try yaymail_duplicate_template or yaymail_save_template with the data parameter).

6. Test Data Setup

  1. User Creation: Create a user with the shop_manager role.
    wp user create attacker attacker@example.com --role=shop_manager --user_pass=password
    
  2. Plugin Activation: Ensure woocommerce and yaymail are active.
    wp plugin activate woocommerce yaymail
    
  3. Environment Check: Verify the YayMail Customizer page is accessible.

7. Expected Results

  • Successful Injection: The server may return a 200 OK or a PHP error/notice if the injected object properties mismatch expected types. If a POP chain is triggered, the specific side effect (e.g., file creation, error log entry) will occur.
  • Vulnerability Confirmation: If the application attempts to process the stdClass object as a template and fails with an error like Catchable fatal error: Object of class stdClass could not be converted to string, it confirms the unserialize() call was reached and executed.

8. Verification Steps

  1. Check PHP Error Logs: Look for errors related to unserialize() or unexpected classes.
    tail -f /var/www/html/wp-content/debug.log
    
  2. Trace Execution: Use a tool or custom code to monitor calls to unserialize within the yaymail directory.
    grep -rn "unserialize" /var/www/html/wp-content/plugins/yaymail/
    
  3. Verify via WP-CLI: After the exploit, check if any settings were changed or if any injected objects persisted in the database (if using a "save" endpoint).

9. Alternative Approaches

If yaymail_import_template requires a specific file format (e.g., JSON), the vulnerability might reside in:

  • yaymail_save_settings: Check if settings are stored as serialized objects.
  • yaymail_duplicate_template: This often copies data from one template to another, possibly deserializing the source data.
  • Shortcode/Frontend: Check if any frontend customizer previews allow passing serialized data to display how an email would look.

Potential Payload (if Base64 encoding is required):
Some YayMail versions expect the template data to be Base64 encoded before deserialization.

  • Payload: base64_encode('O:8:"stdClass":1:{s:3:"foo";s:3:"bar";}')
Research Findings
Static analysis — not yet PoC-verified

Summary

The YayMail plugin for WordPress is vulnerable to PHP Object Injection in versions up to 4.3.3. This occurs because the plugin's template import functionality deserializes user-provided input via the `maybe_unserialize` function without sufficient validation. Authenticated attackers with Shop Manager privileges can exploit this to execute arbitrary code or perform other malicious actions if a suitable POP chain is available in the environment.

Vulnerable Code

// src/Ajax.php - Inferred logic around line 80-87 leading into the import handler
$template = isset($_POST['template']) ? $_POST['template'] : '';
$imported_data = maybe_unserialize(base64_decode($template));

/* src/Ajax.php line 87 */
$source_version = $imported_data->version;

$backup_data = [
    'posts'        => $imported_data->posts,
    'postmeta'     => $imported_data->postmeta,
    'options'      => $imported_data->options,
    'created_date' => $imported_data->created_date ?? current_datetime()->format( 'Y-m-d H:i:s' ),
    'name'         => '_yaymail_import_backup_' . $source_version,
    'version'      => $source_version,
];

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.3/src/Ajax.php	2026-02-12 15:49:00.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.4/src/Ajax.php	2026-03-12 02:31:54.000000000 +0000
@@ -87,10 +87,51 @@
 
             $source_version = $imported_data->version;
 
+            $imported_posts = array_values(
+                array_filter(
+                    $imported_data->posts,
+                    function( $post ) {
+                        return isset( $post->post_type ) && 'yaymail_template' === $post->post_type;
+                    }
+                )
+            );
+
+            $imported_postmeta = array_values(
+                array_filter(
+                    $imported_data->postmeta,
+                    function( $postmeta ) use ( $imported_posts ) {
+                        if ( empty( $imported_posts ) ) {
+                            return false;
+                        }
+                        if ( ! isset( $postmeta->post_id ) || ! isset( $postmeta->meta_key ) ) {
+                            return false;
+                        }
+                        if ( strpos( (string) $postmeta->meta_key, 'yaymail' ) === false ) {
+                            return false;
+                        }
+                        foreach ( $imported_posts as $post ) {
+                            if ( isset( $post->ID ) && (int) $post->ID === (int) $postmeta->post_id ) {
+                                return true;
+                            }
+                        }
+                        return false;
+                    }
+                )
+            );
+
+            $imported_options = array_values(
+                array_filter(
+                    $imported_data->options,
+                    function( $option ) {
+                        return isset( $option->option_name ) && strpos( (string) $option->option_name, 'yaymail' ) !== false;
+                    }
+                )
+            );
+
             $backup_data = [
-                'posts'        => $imported_data->posts,
-                'postmeta'     => $imported_data->postmeta,
-                'options'      => $imported_data->options,
+                'posts'        => $imported_posts,
+                'postmeta'     => $imported_postmeta,
+                'options'      => $imported_options,
                 'created_date' => $imported_data->created_date ?? current_datetime()->format( 'Y-m-d H:i:s' ),
                 'name'         => '_yaymail_import_backup_' . $source_version,
                 'version'      => $source_version,

Exploit Outline

The exploit targets the `yaymail_import_template` AJAX action. An attacker with Shop Manager or higher privileges first authenticates and retrieves a security nonce from the global `yaymail_localize` JavaScript object on the YayMail Customizer page. The attacker then constructs a malicious PHP serialized string (a POP chain) and Base64 encodes it. This payload is sent in a POST request to `wp-admin/admin-ajax.php` using the `template` parameter. When the server processes this request, it calls `maybe_unserialize()` on the decoded payload, triggering the execution of the injected object's magic methods.

Check if your site is affected.

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