Dynamic Widget Content <= 1.3.6 - Authenticated (Contributor+) Stored Cross-Site Scripting via Widget Content Field
Description
The Dynamic Widget Content plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the widget content field in the Gutenberg editor sidebar in all versions up to, and including, 1.3.6 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
<=1.3.6Source Code
WordPress.org SVN## Vulnerability Research Plan: CVE-2026-1268 - Dynamic Widget Content Stored XSS ### 1. Vulnerability Summary The **Dynamic Widget Content** plugin (<= 1.3.6) contains a Stored Cross-Site Scripting (XSS) vulnerability. The flaw exists because the plugin fails to sanitize and escape user-supplied i…
Show full research plan
Vulnerability Research Plan: CVE-2026-1268 - Dynamic Widget Content Stored XSS
1. Vulnerability Summary
The Dynamic Widget Content plugin (<= 1.3.6) contains a Stored Cross-Site Scripting (XSS) vulnerability. The flaw exists because the plugin fails to sanitize and escape user-supplied input entered into the "Widget Content" field within the Gutenberg editor's sidebar. This field is intended to allow users to define dynamic content for widgets, but because the input is saved as post metadata and subsequently rendered on the frontend without proper security filtering (like wp_kses or esc_html), a user with Contributor-level permissions or higher can inject malicious JavaScript.
2. Attack Vector Analysis
- Endpoint: WordPress REST API Post endpoint:
/wp/v2/posts/{id}or/wp/v2/pages/{id}. - Vulnerable Parameter:
metaobject containing the plugin's specific meta key (likelydwc_widget_contentor_dwc_widget_content- inferred). - Authentication: Contributor-level access or higher is required. Contributors can create and edit their own posts, giving them access to the Gutenberg editor and its sidebar components.
- Preconditions: The plugin must be active, and a post must exist (or be created) where the dynamic widget content is assigned.
3. Code Flow
- Input (JS): In the Gutenberg editor, the plugin registers a
PluginSidebarorInspectorControlscomponent. When a user types into the "Widget Content" text area, the state is updated and prepared for saving. - Storage (REST API): Upon clicking "Save" or "Update" in WordPress, a
POSTrequest is sent to/wp/v2/posts/{id}. If the plugin has registered its meta field usingregister_post_metawith'show_in_rest' => true, the raw XSS payload is saved into thewp_postmetatable. - Processing (PHP): The plugin likely uses a filter such as
the_contentor a specific widget rendering hook (e.g.,dynamic_sidebar_paramsor a shortcode callback) to retrieve this meta value. - Sink (PHP): The retrieved meta value is output directly to the page:
// Predicted vulnerable code pattern $content = get_post_meta( get_the_ID(), 'dwc_widget_content', true ); echo $content; // MISSING: esc_html(), wp_kses(), or similar
4. Nonce Acquisition Strategy
To update post metadata via the REST API, the _wpnonce for the wp_rest action is required.
- Create Content: The agent will create a post as a Contributor.
- Access Editor: Use
browser_navigateto go to the edit page for that post:/wp-admin/post.php?post={ID}&action=edit. - Extract Nonce: The WordPress REST API nonce is globally available in the Gutenberg editor via the
wpApiSettingsobject.- Tool:
browser_eval - Script:
window.wpApiSettings.nonce
- Tool:
- Identify Meta Key: If the exact meta key is unknown, the agent will inspect the
wp.datastore in the browser console:- Script:
wp.data.select('core/editor').getCurrentPost().meta
- Script:
5. Exploitation Strategy
- Authentication: Log in as a user with the Contributor role.
- Target Selection: Create a new post to get a valid Post ID.
- Nonce Retrieval: Navigate to the editor and extract the REST nonce using
browser_eval. - Payload Injection: Perform an
http_request(POST) to the REST API to update the post's metadata with the XSS payload.- Method:
POST - URL:
/wp-json/wp/v2/posts/{ID} - Headers:
Content-Type: application/jsonX-WP-Nonce: {EXTRACTED_NONCE}
- Body:
{ "meta": { "dwc_widget_content": "<script>alert('CVE-2026-1268_XSS')</script>" } }
- Method:
- Trigger: Navigate to the public URL of the modified post.
6. Test Data Setup
- User: A user with username
contributor_userand rolecontributor. - Post: A post titled "XSS Test" created by
contributor_user. - Plugin Config: Ensure Dynamic Widget Content is active. No specific internal settings are usually required as the sidebar is enabled by default for posts.
7. Expected Results
- The REST API should return a
200 OKresponse confirming the update of the post meta. - Upon navigating to the post frontend, the browser should execute the JavaScript, resulting in an alert box with
CVE-2026-1268_XSS. - The page source should show the raw
<script>tag within the widget area or content area.
8. Verification Steps
- Check Database: Use WP-CLI to verify the payload is stored in the metadata.
wp post meta get {ID} dwc_widget_content
- Verify Unsanitized Output: Use
http_request(GET) on the post URL and check if the payload is escaped.- Check if
<script>appears as<script>(safe) or<script>(vulnerable).
- Check if
- Context Check: Verify the XSS executes in a standard browser environment using
browser_navigate.
9. Alternative Approaches
- If REST API is restricted: Attempt to save the payload via the standard
admin-ajax.phporpost.phpsave routine by intercepting the form submission. - If
dwc_widget_contentis not the key: Usegrep -r "update_post_meta" .in the plugin directory to find the actual meta key used to save sidebar data. - Shortcode Injection: If the sidebar input is reflected in a shortcode, try injecting the payload via the post content:
[dwc_render_widget content="<img src=x onerror=alert(1)>"](inferred shortcode).
Summary
The Dynamic Widget Content plugin for WordPress (up to 1.3.6) is vulnerable to Stored Cross-Site Scripting via the 'Widget Content' field. Authenticated users with Contributor-level permissions or higher can inject malicious JavaScript into post metadata through the Gutenberg editor sidebar, which executes when the post is viewed because the input is not sanitized upon storage and the output is not escaped during rendering.
Vulnerable Code
// File: dynamic-widget-content.php (likely location) // Meta registration allowing unsanitized input via REST API register_post_meta( 'post', 'dwc_widget_content', array( 'show_in_rest' => true, 'single' => true, 'type' => 'string', ) ); --- // Rendering logic failing to escape output // Predicted vulnerable code pattern in a filter or widget callback $content = get_post_meta( get_the_ID(), 'dwc_widget_content', true ); echo $content; // MISSING: esc_html(), wp_kses(), or similar sanitization
Security Fix
@@ -10,6 +10,7 @@ register_post_meta( 'post', 'dwc_widget_content', array( 'show_in_rest' => true, 'single' => true, 'type' => 'string', + 'sanitize_callback' => 'wp_kses_post', ) ); } @@ -25,5 +26,5 @@ function dwc_render_widget_content( $content ) { $meta_content = get_post_meta( get_the_ID(), 'dwc_widget_content', true ); if ( ! empty( $meta_content ) ) { - echo $meta_content; + echo wp_kses_post( $meta_content ); }
Exploit Outline
The exploit requires an authenticated user with at least Contributor-level access to the WordPress dashboard. 1. Log in as a Contributor and create a new post to obtain a valid Post ID. 2. Access the Gutenberg editor for the newly created post to retrieve the WordPress REST API nonce from the `wpApiSettings.nonce` JavaScript object. 3. Identify the vulnerable meta key (e.g., `dwc_widget_content`) used by the plugin for the sidebar content field. 4. Send an authenticated HTTP POST request to the WordPress REST API endpoint `/wp-json/wp/v2/posts/{ID}`. 5. Include the payload in the `meta` object of the JSON body: `{ "meta": { "dwc_widget_content": "<script>alert('XSS')</script>" } }`. 6. The payload is stored in the database. When any user (including administrators) views the public post, the script will execute in their browser context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.