Happyforms – Form Builder for WordPress: Drag & Drop Contact Forms, Surveys, Payments & Multipurpose Forms <= 1.26.13 - Unauthenticated PHP Object Injection
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:HTechnical Details
What Changed in the Fix
Changes introduced in v1.26.14
Source Code
WordPress.org SVNThis 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 aswp_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
- Entry Point: An unauthenticated user sends a
POSTrequest toadmin-ajax.phpwithaction=happyforms_submit. - Processing: The submission controller (likely
HappyForms_Submission_Controller) handles the request. - Sink Path: To generate a response or send notification emails, the plugin processes the submitted field values.
- Vulnerable Function: It calls
happyforms_get_message_part_value( $value, ... )incore/helpers/helper-misc.php. - Execution: Inside
happyforms_get_message_part_value():
Ifif ( is_string( $value ) ) { $value = maybe_unserialize( $value ); // <--- VULNERABILITY }$valuestarts withO:,a:,s:, etc.,maybe_unserializewill 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_settingsJavaScript object. - Extraction Steps:
- Find a page containing a Happyforms form.
- Navigate to the page using
browser_navigate. - 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
- Reconnaissance: Visit the target site and find a page with a form. Note the
form_idand field names (e.g.,happyforms_6,happyforms_7). - 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:{}). - Submission: Use the
http_requesttool 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)
- Method:
- Trigger: Ensure
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
@@ -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 ); @@ -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.