CVE-2026-0633

MetForm – Contact Form, Survey, Quiz, & Custom Form Builder for Elementor <= 4.1.0 - Unauthenticated Form Submission Exposure via Forgeable Cookie Value

lowImproper Authentication
3.7
CVSS Score
3.7
CVSS Score
low
Severity
4.1.1
Patched in
1d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=4.1.0
PublishedJanuary 23, 2026
Last updatedJanuary 24, 2026
Affected pluginmetform

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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:
    1. A form must be created and active.
    2. A victim must have submitted the form within the last 15 minutes.
    3. The attacker must guess or know the Entry ID (entries use auto-incrementing IDs).

3. Code Flow (Inferred from Patch and Description)

  1. Submission: When a user submits a MetForm form, Metform\Core\Entries\Action::insert() is called.
  2. Transient Storage: The plugin saves the submission data to a transient named mf_entry_{entry_id} with a 15-minute expiration.
  3. Cookie Generation: The plugin sets a cookie mf_recent_entry_id with the value {$entry_id}|{$user_id}.
  4. Display Logic: When the form page is reloaded or the user is redirected to a success page, the [metform_form] shortcode (rendered via Metform\Core\Forms\Shortcode::render_form()) checks for the mf_recent_entry_id cookie.
  5. Vulnerability: The code splits the cookie by | and takes the first part as the $entry_id. It then fetches get_transient('mf_entry_' . $entry_id).
  6. 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.

  1. Identify Shortcode: The plugin uses [metform_form id="<ID>"].
  2. JS Localization: The plugin localizes data into the mf_objects variable.
  3. Nonce Retrieval:
    • Navigate to the page containing the form.
    • Execute browser_eval("window.mf_objects?.nonce") or browser_eval("window.mf_objects?.rest_nonce").
  4. Submission Nonce: For the POST /wp-json/metform/v1/entries/insert/ request, the _wpnonce header 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 (where 101 is the guessed ID and 0 is 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

  1. Create a MetForm Form:
    • Use wp post create to create a metform-form post type.
    • Set meta metform_form_config to include some fields (Name, Email).
  2. 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>"]'
  3. Simulate Victim:
    • Perform a submission as an unauthenticated user with sensitive info (e.g., Name: SecretAgent, Email: victim@example.com).

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

  1. Check Transients: Use wp transient get mf_entry_101 to confirm the data exists in the database.
  2. 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 forgeable mf_recent_entry_id cookie 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 + 1 to last_id + 20 to sweep for any active transients from other users.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/metform/core/entries/action.php
+++ b/metform/core/entries/action.php
@@ -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);

--- a/metform/core/forms/shortcode.php
+++ b/metform/core/forms/shortcode.php
@@ -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.