CVE-2026-49768

Happyforms – Form Builder for WordPress: Drag & Drop Contact Forms, Surveys, Payments & Multipurpose Forms <= 1.26.13 - Unauthenticated PHP Object Injection

highDeserialization of Untrusted Data
8.1
CVSS Score
8.1
CVSS Score
high
Severity
1.26.14
Patched in
5d
Time to patch

Description

The Happyforms – Form Builder for WordPress: Drag & Drop Contact Forms, Surveys, Payments & Multipurpose Forms plugin for WordPress is vulnerable to PHP Object Injection in versions up to, and including, 1.26.13 via deserialization of untrusted input. This makes it possible for unauthenticated attackers 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:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=1.26.13
PublishedJune 4, 2026
Last updatedJune 8, 2026
Affected pluginhappyforms

What Changed in the Fix

Changes introduced in v1.26.14

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps required to analyze and demonstrate the Unauthenticated PHP Object Injection vulnerability in the Happyforms plugin (CVE-2026-49768). ### 1. Vulnerability Summary The **Happyforms** plugin is vulnerable to **PHP Object Injection** due to the use of `maybe_unser…

Show full research plan

This research plan outlines the steps required to analyze and demonstrate the Unauthenticated PHP Object Injection vulnerability in the Happyforms plugin (CVE-2026-49768).

1. Vulnerability Summary

The Happyforms plugin is vulnerable to PHP Object Injection due to the use of maybe_unserialize() on untrusted user input. Specifically, the function happyforms_get_message_part_value() in core/helpers/helper-misc.php attempts to unserialize field values submitted by users. If a form is configured to display submitted values in a confirmation message or email, an unauthenticated attacker can provide a serialized PHP object in a form field, which will be executed during the submission processing phase.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: happyforms_submit (typically registered as wp_ajax_nopriv_happyforms_submit)
  • Vulnerable Parameter: Any form field parameter (e.g., happyforms_field_123)
  • Authentication: None (Unauthenticated)
  • Preconditions: A Happyforms form must be published on a page, and its "Success Message" or confirmation settings must be configured to process or display the submitted values (triggering the sink).

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with action=happyforms_submit.
  2. Processing: The submission controller (likely HappyForms_Submission_Controller) handles the request.
  3. Sink Path: To generate a response or send notification emails, the plugin processes the submitted field values.
  4. Vulnerable Function: It calls happyforms_get_message_part_value( $value, ... ) in core/helpers/helper-misc.php.
  5. Execution: Inside happyforms_get_message_part_value():
    if ( is_string( $value ) ) {
        $value = maybe_unserialize( $value ); // <--- VULNERABILITY
    }
    
    If $value starts with O:, a:, s:, etc., maybe_unserialize will attempt to reconstruct the PHP object, triggering __wakeup() or __destruct() methods if a POP chain is available.

4. Nonce Acquisition Strategy

Happyforms protects its submission endpoint with a WordPress nonce.

  • Nonce Action: happyforms_submit (or similar, verify in source).
  • Localization: The nonce is localized in the happyforms_settings JavaScript object.
  • Extraction Steps:
    1. Find a page containing a Happyforms form.
    2. Navigate to the page using browser_navigate.
    3. Extract the nonce and the form ID using browser_eval:
      // Example extraction
      const settings = window.happyforms_settings;
      return {
          nonce: settings.ajax_nonce,
          form_id: document.querySelector('input[name="happyforms_form_id"]')?.value
      };
      

5. Exploitation Strategy

  1. Reconnaissance: Visit the target site and find a page with a form. Note the form_id and field names (e.g., happyforms_6, happyforms_7).
  2. Payload Generation: Create a serialized PHP object payload. Since no internal POP chain is identified, use a generic object to test for deserialization (e.g., O:8:"stdClass":0:{}).
  3. Submission: Use the http_request tool to send the payload.
    • Method: POST
    • URL: http://<target>/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      action=happyforms_submit&
      happyforms_form_id=<FORM_ID>&
      _wpnonce=<NONCE>&
      <FIELD_NAME>=O:8:"stdClass":0:{}&
      ... (other required fields)
      
  4. Trigger: Ensure
Research Findings
Static analysis — not yet PoC-verified

Summary

The Happyforms plugin for WordPress is vulnerable to unauthenticated PHP Object Injection due to the insecure use of the `maybe_unserialize` function on user-controlled form field values. Attackers can exploit this by submitting a form with a serialized PHP object payload, which triggers deserialization and can lead to remote code execution if a suitable POP chain is present on the system.

Vulnerable Code

/* core/helpers/helper-misc.php line 145 */
function happyforms_get_message_part_value( $value, $part = array(), $destination = '' ) {
	$original_value = $value;

	if ( is_string( $value ) {
		$value = maybe_unserialize( $value );
	}

---

/* core/classes/class-form-controller.php line 649 */
$form_meta = array_map( 'maybe_unserialize', $form_meta );

---

/* core/classes/class-form-option-limiter.php line 260 */
$messages = array_map( function( $message ) {
	return maybe_unserialize( $message['meta_value'] );
}, $messages );

Security Fix

diff -ru /core/classes/class-form-controller.php /core/classes/class-form-controller.php
--- /core/classes/class-form-controller.php	2023-03-23 10:25:44.000000000 +0000
+++ /core/classes/class-form-controller.php	2026-05-27 22:04:52.000000000 +0000
@@ -646,7 +646,7 @@
 				return $value[0];
 			}, $form_meta );
 
-			$form_meta = array_map( 'maybe_unserialize', $form_meta );
+			$form_meta = array_map( 'happyforms_maybe_unserialize', $form_meta );
 
 			foreach ( $form_meta as $key => $value ) {
 				add_post_meta( $duplicate_id, $key, $value );
diff -ru /core/helpers/helper-misc.php /core/helpers/helper-misc.php
--- /core/helpers/helper-misc.php	2023-06-27 14:29:48.000000000 +0000
+++ /core/helpers/helper-misc.php	2026-05-27 22:04:52.000000000 +0000
@@ -72,6 +72,65 @@
 
 endif;
 
+if ( ! function_exists( 'happyforms_maybe_unserialize' ) ):
+/**
+ * Safely unserialize a stored value.
+ */
+function happyforms_maybe_unserialize( $value ) {
+	if ( ! is_string( $value ) || ! is_serialized( $value ) ) {
+		return $value;
+	}
+
+	$unserialized = @unserialize( trim( $value ), array( 'allowed_classes' => false ) );
+
+	if ( false === $unserialized || happyforms_value_contains_object( $unserialized ) ) {
+		return $value;
+	}
+
+	return $unserialized;
+}
+
+endif;
+
 if ( ! function_exists( 'happyforms_get_message_part_value' ) ):
 /**
  * Get the part submission value in a readable format.
@@ -88,7 +147,7 @@
 	$original_value = $value;
 
 	if ( is_string( $value ) ) {
-		$value = maybe_unserialize( $value );
+		$value = happyforms_maybe_unserialize( $value );
 	}

Exploit Outline

1. **Identify Form**: Locate a page on the target site containing a Happyforms form and note the `form_id` and field names (e.g., `happyforms_field_1`). 2. **Obtain Nonce**: Extract the AJAX nonce from the `happyforms_settings` JavaScript object on the page (`window.happyforms_settings.ajax_nonce`). 3. **Craft Payload**: Generate a PHP serialized object payload targeting a POP chain available in the environment (e.g., from another plugin or theme). 4. **Trigger Injection**: Send an unauthenticated AJAX request to `wp-admin/admin-ajax.php` with the `action` parameter set to `happyforms_submit`, including the correct `_wpnonce`, `happyforms_form_id`, and the serialized payload in one of the form field parameters. 5. **Execution**: The plugin processes the submission and calls `happyforms_get_message_part_value`, which executes `maybe_unserialize` on the malicious payload, triggering the POP chain.

Check if your site is affected.

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