HappyForms <= 1.26.12 - Authenticated (Admin+) Local File Inclusion
Description
The Happyforms – Form Builder for WordPress: Drag & Drop Contact Forms, Surveys, Payments & Multipurpose Forms plugin for WordPress is vulnerable to Local File Inclusion in all versions up to, and including, 1.26.12 via the happyforms_get_form_partial() function. This makes it possible for authenticated attackers, with Administrator-level access and above, to include and execute arbitrary .php files on the server, allowing the execution of any PHP code in those files. This can be used to bypass access controls, obtain sensitive data, or achieve code execution in cases where .php file types can be uploaded and included.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:HTechnical Details
What Changed in the Fix
Changes introduced in v1.26.13
Source Code
WordPress.org SVNThis research plan targets a Local File Inclusion (LFI) vulnerability in the Happyforms plugin (versions <= 1.26.12). The vulnerability resides in the `happyforms_get_form_partial()` function, which fails to properly sanitize template paths before inclusion. ### 1. Vulnerability Summary * **Vulne…
Show full research plan
This research plan targets a Local File Inclusion (LFI) vulnerability in the Happyforms plugin (versions <= 1.26.12). The vulnerability resides in the happyforms_get_form_partial() function, which fails to properly sanitize template paths before inclusion.
1. Vulnerability Summary
- Vulnerability: Authenticated (Admin+) Local File Inclusion (LFI).
- Vulnerable Function:
happyforms_get_form_partial()(likely located ininc/classes/class-happyforms.phporcore/helpers/helper-misc.php). - Reason: The function accepts a user-provided parameter (representing a template or partial name) and uses it directly in an
includeorrequirestatement without sufficient sanitization or path validation. This allows an administrator to use directory traversal (../) to include arbitrary.phpfiles. - Impact: Execution of arbitrary PHP code (RCE) if an attacker can upload a
.phpfile (e.g., via the media library) or inclusion of sensitive files that execute logic (likewp-config.php).
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.phpor a specific admin page (e.g., the Form Builder/Customizer). - Action: Likely
happyforms_get_form_partial. - Parameter: Typically
template,part, orpartial(to be confirmed via grep). - Authentication: Administrator-level access is required.
- Preconditions: The plugin must be active.
3. Code Flow (Manual Discovery)
The researcher/agent must first locate the vulnerable sink:
- Search for the function:
grep -rn "function happyforms_get_form_partial" . - Trace the Sink: Inside the function, look for
include,include_once,require, orrequire_once. Identify the variable being passed. - Find the Entry Point: Search for where this function is called or registered as an AJAX action:
grep -rn "happyforms_get_form_partial" .
Look foradd_action( 'wp_ajax_...', ... ).
4. Nonce Acquisition Strategy
Happyforms heavily utilizes the WordPress Customizer and AJAX. Admin nonces are typically localized for the dashboard.
- Identify the Script/Nonce: Search for
wp_localize_scriptin the codebase to find the handle.grep -rn "wp_localize_script" . - Likely Variable: In Happyforms, nonces are often attached to
window.happyforms_adminorwindow.happyforms_customizer. - Acquisition Steps:
- Create a test form (if none exist) to ensure the builder scripts load:
wp eval "HappyForms()->initialize_plugin();"(or use WP-CLI to create a form). - Navigate to the Happyforms admin page:
browser_navigate("http://localhost:8080/wp-admin/admin.php?page=happyforms"). - Extract the nonce using
browser_eval:// Hypothetical identifiers based on Happyforms naming conventions window.happyforms_admin?.nonce || window.happyforms_settings?.nonce
- Create a test form (if none exist) to ensure the builder scripts load:
5. Exploitation Strategy
Step 1: Discover Parameters
Assuming the function is an AJAX handler, use http_request to test parameters.
- Action:
happyforms_get_form_partial - Target Parameter: (Discovered in Step 3, e.g.,
partial)
Step 2: Test Path Traversal
Attempt to include a known WordPress file to confirm the LFI. We will target wp-config (which won't display source but will confirm execution/inclusion if no error occurs vs a 404 for a non-existent file).
Request (Example):
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
action=happyforms_get_form_partial&partial=../../../../wp-config&nonce=[NONCE]
Step 3: RCE via Media Library (If applicable)
- Upload a file named
poc.php(containing<?php echo "VULN_SUCCESS"; ?>) to the WordPress Media Library. - Identify the path (usually
wp-content/uploads/YYYY/MM/poc.php). - Include it via the LFI:
partial=../../../../wp-content/uploads/2025/01/poc(Note: The plugin likely appends.php).
6. Test Data Setup
- Login as Admin: Use the
login_as_admintool. - Create a Form: Ensure at least one Happyforms form exists to populate admin interfaces.
wp post create --post_type=happyform --post_title="Exploit Test" --post_status=publish - Locate Plugin Path: Confirm plugin is at
wp-content/plugins/happyforms/.
7. Expected Results
- Success: The HTTP response body contains the output of the included PHP file (e.g., "VULN_SUCCESS") or a lack of "File not found" errors when targeting valid system files.
- Evidence: If including
wp-config.php, the response may be empty (200 OK) becausewp-config.phpdefines constants but produces no output. Including a non-existent file should trigger a PHP warning/error ifWP_DEBUGis on, or a plugin-specific error message.
8. Verification Steps
- Error Log Check: Check
wp-content/debug.logfor "failed to open stream" errors which indicate the inclusion path attempted:tail -n 20 /var/www/html/wp-content/debug.log - PHP Execution: If a shell was uploaded, verify the existence of a file created by the shell.
9. Alternative Approaches
- Wrapper Inclusion: If direct inclusion is blocked by
.phpextension enforcement, tryphp://filter/convert.base64-encode/resource=wp-configto read the configuration.- Note: If the plugin appends
.php, the payload would bephp://filter/convert.base64-encode/resource=../../../../wp-config(the filter will look forwp-config.php).
- Note: If the plugin appends
- Template Directives: Check if the vulnerability is accessible via the
[happyforms]shortcode processing if it dynamically loads parts based on attributes.
Summary
The Happyforms plugin for WordPress is vulnerable to Local File Inclusion (LFI) in versions up to and including 1.26.12 through the happyforms_get_form_partial() function. This allows authenticated administrators to include and execute arbitrary PHP files on the server by exploiting insufficient sanitization of template paths containing directory traversal sequences.
Security Fix
@@ -5,7 +5,7 @@ * Plugin URI: https://happyforms.io * Description: Form builder to get in touch with visitors, grow your email list and collect payments. * Author: Happyforms - * Version: 1.26.12 + * Version: 1.26.13 * Author URI: https://happyforms.io * Upgrade URI: https://happyforms.io/upgrade */ @@ -22,7 +22,7 @@ /** * The current version of the plugin. */ -define( 'HAPPYFORMS_VERSION', '1.26.12' ); +define( 'HAPPYFORMS_VERSION', '1.26.13' ); if ( ! function_exists( 'happyforms_get_version' ) ): @@ -143,7 +143,7 @@ /** * Start general admin and frontend hooks. */ -add_action( 'plugins_loaded', array( HappyForms(), 'initialize_plugin' ) ); +add_action( 'init', array( HappyForms(), 'initialize_plugin' ), 0 ); /** * Start Customize screen specific hooks. @@ -3,9 +3,9 @@ Contributors: happyforms Tags: contact form, WordPress form plugin, forms, form builder, custom form Requires at least: 5.0 -Tested up to: 6.8.3 +Tested up to: 6.9.4 Requires PHP: 7.0 -Stable tag: 1.26.12 +Stable tag: 1.26.13 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -289,6 +289,13 @@ == Changelog == += March 30, 2026 = + +Version 1.26.13 changelog: + +* Update: Compatibility with WordPress 6.9.4. +* Fixed: Translation loading notice triggered on WordPress 6+. + = November 18, 2025 = Version 1.26.12 changelog: @@ -649,6 +656,11 @@ == Upgrade Notice == += 1.26.13 = + +* Update: Compatibility with WordPress 6.9.4. +* Fixed: Translation loading notice triggered on WordPress 6+. + = 1.26.12 =
Exploit Outline
1. Authenticate as a user with Administrator-level access to the WordPress site. 2. Obtain a valid security nonce from the Happyforms admin dashboard, typically localized in the builder or customizer scripts (e.g., from `window.happyforms_admin.nonce`). 3. Trigger an AJAX request (POST) to `wp-admin/admin-ajax.php` with the `action` parameter set to `happyforms_get_form_partial`. 4. Provide a parameter (likely `partial` or `template`) containing a directory traversal payload like `../../../../wp-config` (targeting `wp-config.php` assuming the plugin appends the extension) or a path to a malicious PHP file previously uploaded to the Media Library. 5. Upon processing, the plugin will use the provided path in an `include` or `require` statement, executing the targeted PHP file in the context of the WordPress environment.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.