[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f6V5h9F0k51T1cArYjWicjVMRNcEpmA6R4v4Dsta5uOo":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-9022","splide-carousel-block-authenticated-contributor-stored-cross-site-scripting-via-url-block-attribute","Splide Carousel Block \u003C= 1.7.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'url' Block Attribute","The Splide Carousel Block plugin for WordPress is vulnerable to Stored Cross-Site Scripting via 'url' Block Attribute in all versions up to, and including, 1.7.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. The injected payload must be published before it executes for site visitors, which requires an editor or administrator to approve and publish the contributor's post.","splide-carousel",null,"\u003C=1.7.1","1.7.2","medium",6.4,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-05-26 13:53:27","2026-05-27 02:27:37",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F268f2ae1-5360-4ec5-bcd9-dc3ab11396dc?source=api-prod",1,[22,23,24,25,26,27,28,29],"build\u002Fcarousel-item\u002Fblock.json","build\u002Fcarousel-item\u002Findex.asset.php","build\u002Fcarousel-item\u002Findex.js","build\u002Fcarousel\u002Fblock.json","build\u002Fcarousel\u002Findex.asset.php","build\u002Fcarousel\u002Findex.js","build\u002Fcarousel\u002Fview.asset.php","build\u002Fcarousel\u002Fview.js","researched",false,3,"# Exploitation Research Plan - CVE-2026-9022\n\n## 1. Vulnerability Summary\nThe **Splide Carousel Block** plugin (versions \u003C= 1.7.1) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists in the `cloudcatch\u002Fsplide-carousel-item` block, specifically within the `url` attribute. While the editor UI attempts to prepend protocols, it does not sanitize or validate against the `javascript:` protocol. Furthermore, the block's `save` function stores this attribute in a JSON string within a `data-link` attribute on the frontend. When a user clicks the carousel item, the frontend JavaScript parses this JSON and executes the URL, leading to script execution if a `javascript:` URI is used.\n\n## 2. Attack Vector Analysis\n*   **Endpoint:** WordPress REST API Post creation\u002Fupdate (`\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts`) or the Gutenberg Editor (`\u002Fwp-admin\u002Fpost.php`).\n*   **Vulnerable Block:** `cloudcatch\u002Fsplide-carousel-item`.\n*   **Vulnerable Attribute:** `url`.\n*   **Payload Type:** `javascript:` protocol URI.\n*   **Authentication Level:** Contributor or higher. Contributors can create and save posts (stored XSS), which then execute when viewed by an Editor or Administrator (e.g., during post review).\n*   **Preconditions:** The plugin must be active, and the attacker must have credentials for a Contributor-level account.\n\n## 3. Code Flow\n1.  **Input:** A Contributor user creates or edits a post and inserts a `cloudcatch\u002Fsplide-carousel-item` block with a malicious `url` attribute (e.g., `javascript:alert(window.origin)`).\n2.  **Editor Processing (`build\u002Fcarousel-item\u002Findex.js`):**\n    *   The `edit` function uses `(0, g.prependHTTP)(e)` to process the URL.\n    *   `prependHTTP` (from `wp-url`) allows any string starting with a protocol (matching `[a-z]+:`), thus permitting `javascript:`.\n3.  **Storage (`build\u002Fcarousel-item\u002Findex.js`):**\n    *   The `save` function generates the frontend HTML:\n        ```javascript\n        \"data-link\": c ? JSON.stringify({ url: i, target: o, rel: a }) : null\n        ```\n    *   The attribute `i` (the raw `url`) is placed into a JSON object and stringified into the `data-link` attribute.\n4.  **Frontend Execution (`build\u002Fcarousel\u002Fview.js`):**\n    *   The frontend script initializes the Splide carousel.\n    *   It retrieves the `data-link` attribute from the slide element.\n    *   Upon a click event, it parses the JSON and performs a navigation\u002Fredirection using the `url` property (e.g., `window.location.href = data.url` or `window.open(data.url)`).\n5.  **Sink:** The browser executes the `javascript:` URI, resulting in XSS.\n\n## 4. Nonce Acquisition Strategy\nTo interact with the REST API for post creation, a REST Nonce (`_wpnonce`) is required.\n1.  **Login:** Authenticate as a Contributor using the `browser_navigate` tool.\n2.  **Navigation:** Navigate to the \"New Post\" screen: `\u002Fwp-admin\u002Fpost-new.php`.\n3.  **Extraction:** Use `browser_eval` to extract the nonce from the `wpApiSettings` object which is globally available in the Gutenberg editor context.\n    *   **JavaScript:** `window.wpApiSettings.nonce`\n\n## 5. Exploitation Strategy\n1.  **Setup Account:** Ensure a Contributor user exists.\n2.  **Get Nonce:** Login as Contributor and extract the REST API nonce from `\u002Fwp-admin\u002Fpost-new.php`.\n3.  **Create Malicious Post:** Use the `http_request` tool to send a `POST` request to `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts`.\n    *   **Headers:**\n        *   `Content-Type: application\u002Fjson`\n        *   `X-WP-Nonce: [EXTRACTED_NONCE]`\n    *   **Payload:**\n        ```json\n        {\n          \"title\": \"XSS Carousel Test\",\n          \"content\": \"\u003C!-- wp:cloudcatch\u002Fsplide-carousel -->\\n\u003Cdiv class=\\\"wp-block-cloudcatch-splide-carousel\\\">\\n\u003C!-- wp:cloudcatch\u002Fsplide-carousel-item {\\\"url\\\":\\\"javascript:alert(document.domain)\\\"} -->\\n\u003Cdiv class=\\\"wp-block-cloudcatch-splide-carousel-item\\\">\u003C\u002Fdiv>\\n\u003C!-- \u002Fwp:cloudcatch\u002Fsplide-carousel-item -->\\n\u003C\u002Fdiv>\\n\u003C!-- \u002Fwp:cloudcatch\u002Fsplide-carousel -->\",\n          \"status\": \"draft\"\n        }\n        ```\n4.  **Obtain Post URL:** Extract the `link` from the REST API response.\n5.  **Trigger XSS:** \n    *   Navigate to the post's preview URL or the published URL (if an admin publishes it).\n    *   Click on the carousel slide item.\n\n## 6. Test Data Setup\n1.  **Plugin:** Install and activate `splide-carousel` version 1.7.1.\n2.  **User:** Create a user with the `contributor` role.\n3.  **Environment:** Ensure the WordPress instance is configured to allow blocks (standard in WP 5.0+).\n\n## 7. Expected Results\n*   The REST API call should return `201 Created`.\n*   The post content in the database (verified via `wp post get [ID] --field=post_content`) should contain: `\u003C!-- wp:cloudcatch\u002Fsplide-carousel-item {\"url\":\"javascript:alert(document.domain)\"} -->`.\n*   The frontend HTML for the slide should contain: `data-link='{\"url\":\"javascript:alert(document.domain)\", ...}'`.\n*   Upon clicking the slide on the frontend, an alert box showing the document domain should appear.\n\n## 8. Verification Steps\n1.  **Database Check:**\n    *   `wp post list --post_type=post --post_status=draft`\n    *   `wp post get [POST_ID] --field=post_content`\n2.  **Frontend Render Check:**\n    *   Navigate to the post URL.\n    *   Inspect the HTML for the element with class `splide__slide`.\n    *   Confirm the presence of `data-link` containing the `javascript:` payload.\n\n## 9. Alternative Approaches\n*   **Bypassing `prependHTTP`:** If `prependHTTP` was more restrictive, an attacker could try `data-link` attribute breakout:\n    *   Payload for `url`: `\"}>\u003Cimg src=x onerror=alert(1)>`\n    *   This targets the way `JSON.stringify` output is placed into the HTML attribute if the plugin failed to use `esc_attr` on the block's `save` output (though Gutenberg usually handles this).\n*   **Editor-Side XSS:** Check if the `url` is rendered unsanitized within the Gutenberg Editor (`edit` function in `index.js`). If the editor displays the link in an `\u003Ca>` tag or an `\u003Ciframe>` preview, the XSS might trigger for the Contributor immediately upon saving.","The Splide Carousel Block plugin is vulnerable to Stored Cross-Site Scripting (XSS) via the 'url' attribute of the Splide Carousel Item block. Authenticated attackers with contributor-level access can inject 'javascript:' URIs into the block, which are stored in a data attribute and executed in the browser context when a user clicks on the carousel slide.","\u002F\u002F build\u002Fcarousel-item\u002Findex.js line 101 (edit function processing)\nonChange:({url:e=\"\",opensInNewTab:t})=>{x({url:(0,g.prependHTTP)(e)}),A!==t&&(e=>{const t=e?\"_blank\":void 0;let r=_;t&&!_?r=w:t||_!==w||(r=void 0),x({target:t,rel:r})})(t)}\n\n---\n\n\u002F\u002F build\u002Fcarousel-item\u002Findex.js line 109 (save function rendering)\nsave:function(e){const{attributes:t}=e,{urlHash:r,verticalAlignment:n,url:i,target:o,rel:a}=t,c=!!i,u=p.useBlockProps.save({className:l()(\"wp-block-splide-carousel__slide\",\"splide__slide\",{[`is-vertically-aligned-${n}`]:n}),\"data-link\":c?JSON.stringify({url:i,target:o,rel:a}):null,...r&&{\"data-splide-hash\":r}}),d=p.useInnerBlocksProps.save(u);return(0,s.jsx)(\"div\",{...d})}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsplide-carousel\u002F1.7.1\u002Fbuild\u002Fcarousel\u002Fblock.json \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsplide-carousel\u002F1.7.2\u002Fbuild\u002Fcarousel\u002Fblock.json\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsplide-carousel\u002F1.7.1\u002Fbuild\u002Fcarousel\u002Fblock.json\t2026-01-17 13:52:28.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsplide-carousel\u002F1.7.2\u002Fbuild\u002Fcarousel\u002Fblock.json\t2026-05-19 16:12:38.000000000 +0000\n@@ -2,7 +2,7 @@\n   \"$schema\": \"https:\u002F\u002Fschemas.wp.org\u002Ftrunk\u002Fblock.json\",\n   \"apiVersion\": 3,\n   \"name\": \"cloudcatch\u002Fsplide-carousel\",\n-  \"version\": \"1.7.1\",\n+  \"version\": \"1.7.2\",\n   \"title\": \"Splide Carousel\",\n   \"category\": \"media\",\n   \"attributes\": {\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsplide-carousel\u002F1.7.1\u002Fbuild\u002Fcarousel\u002Findex.asset.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsplide-carousel\u002F1.7.2\u002Fbuild\u002Fcarousel\u002Findex.asset.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsplide-carousel\u002F1.7.1\u002Fbuild\u002Fcarousel\u002Findex.asset.php\t2026-01-17 13:52:28.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsplide-carousel\u002F1.7.2\u002Fbuild\u002Fcarousel\u002Findex.asset.php\t2026-05-19 16:12:38.000000000 +0000\n@@ -1 +1 @@\n-\u003C?php return array('dependencies' => array('lodash', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-notices'), 'version' => '51deb1d6947788b25af6');\n+\u003C?php return array('dependencies' => array('lodash', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-notices'), 'version' => 'e8fd1be94de72804e90b');","To exploit this vulnerability, an attacker with Contributor-level credentials (or higher) navigates to the WordPress block editor and inserts a 'Splide Carousel Item' block. The attacker then modifies the 'url' attribute of this block to include a malicious JavaScript payload (e.g., 'javascript:alert(document.domain)'). Because the plugin uses the 'prependHTTP' helper without further validation, it allows the 'javascript:' protocol. When the post is saved and subsequently viewed (either in preview mode or after being published by an editor), the frontend JavaScript retrieves the malicious URL from the 'data-link' attribute and executes it via browser navigation when the carousel slide is clicked.","gemini-3-flash-preview","2026-06-04 19:24:41","2026-06-04 19:25:36",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","1.7.1","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsplide-carousel\u002Ftags\u002F1.7.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsplide-carousel.1.7.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsplide-carousel\u002Ftags\u002F1.7.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsplide-carousel.1.7.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsplide-carousel\u002Ftags"]