[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fGHkVzCg2HDNpcxMyA25bjr5w8ecYbxgyIYzGg9opMCM":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":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"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":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-57712","wpzoom-portfolio-lite-filterable-portfolio-plugin-unauthenticated-stored-cross-site-scripting-2","WPZOOM Portfolio Lite – Filterable Portfolio Plugin \u003C= 1.4.29 - Unauthenticated Stored Cross-Site Scripting","The WPZOOM Portfolio Lite – Filterable Portfolio Plugin plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.4.29 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.","wpzoom-portfolio",null,"\u003C=1.4.29","1.4.30","high",7.2,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-07-10 00:00:00","2026-07-14 19:17:40",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Ff4213452-33f7-46d4-ab4a-544a9bef475a?source=api-prod",5,[22,23,24],"build\u002Fblocks\u002Fportfolio\u002Findex.php","readme.txt","wpzoom-portfolio.php","researched",false,3,"This research plan outlines the steps to investigate and exploit **CVE-2026-57712**, a Stored Cross-Site Scripting (XSS) vulnerability in the **WPZOOM Portfolio Lite** plugin.\n\n---\n\n### 1. Vulnerability Summary\nThe **WPZOOM Portfolio Lite** plugin (versions \u003C= 1.4.29) contains an unauthenticated stored XSS vulnerability. The flaw exists because the plugin registers a REST API endpoint (or AJAX handler) to manage plugin settings or block attributes without enforcing proper authorization checks (`permission_callback`). Furthermore, the plugin fails to sanitize these settings upon input and fails to escape them using functions like `esc_html()` or `esc_attr()` when rendering them on the frontend.\n\n### 2. Attack Vector Analysis\n*   **Vulnerable Endpoint**: The REST API route `wpzoom-portfolio\u002Fv1\u002Fsettings` (inferred from `WPZOOM_Blocks::rest_api_routes`) or the AJAX action `wpzoom_portfolio_save_settings`.\n*   **Target Parameter**: Settings fields such as `viewAllLabel`, `readMoreLabel`, or `backgroundColor`.\n*   **Authentication**: Unauthenticated (the `permission_callback` for the REST route is likely missing or returns `true`).\n*   **Preconditions**: The Portfolio block or shortcode must be present on at least one public page to trigger the execution of the stored payload.\n\n### 3. Code Flow\n1.  **Entry Point**: An unauthenticated user sends a `POST` request to `\u002Fwp-json\u002Fwpzoom-portfolio\u002Fv1\u002Fsettings`.\n2.  **Lack of Authorization**: The `WPZOOM_Blocks::rest_api_routes()` method (registered in `wpzoom-portfolio.php`) defines a route that lacks a restrictive `permission_callback`.\n3.  **Storage**: The handler (likely `update_settings`) takes the JSON input and updates the WordPress option `wpzoom-portfolio-settings` using `update_option()`.\n4.  **Processing**: The values are stored raw. While `WPZOOM_Blocks_Portfolio::sanitize_css_value` exists in `build\u002Fblocks\u002Fportfolio\u002Findex.php`, it is only applied to specific CSS fields and is bypassable for HTML contexts.\n5.  **Sink**: When a visitor views a page containing the Portfolio block, the plugin calls the rendering logic. The settings are retrieved via `get_option( 'wpzoom-portfolio-settings' )` and localized into the script `wpzoom-blocks-js-index-main` or echoed directly in the PHP template.\n6.  **Execution**: The unescaped `viewAllLabel` (or similar) is rendered into the DOM, executing the attacker's JavaScript.\n\n### 4. Nonce Acquisition Strategy\nWhile the vulnerability is \"unauthenticated,\" the WordPress REST API typically requires a `wp_rest` nonce for `POST` requests to prevent CSRF, even for public endpoints.\n\n1.  **Identify Shortcode**: The plugin uses the shortcode `[wpzoom_portfolio]` and the block `wpzoom-blocks\u002Fportfolio`.\n2.  **Create Trigger Page**:\n    ```bash\n    wp post create --post_type=page --post_title=\"Portfolio Gallery\" --post_status=publish --post_content='[wpzoom_portfolio]'\n    ```\n3.  **Extract Nonce**:\n    Navigate to the newly created page and use `browser_eval` to extract the REST nonce, which WordPress often exposes in the `wpApiSettings` object or localized plugin data.\n    ```javascript\n    \u002F\u002F Extract from WP default localized data\n    window.wpApiSettings?.nonce || window.wpzoomPortfolioBlock?.nonce\n    ```\n    *Note: If the REST endpoint is truly unauthenticated and bypasses the standard WP REST auth check, a nonce may not be required.*\n\n### 5. Exploitation Strategy\nThe goal is to update the plugin's global settings with a malicious payload that will execute in the browser of any user (including admins) visiting the portfolio.\n\n**Request**:\n*   **Tool**: `http_request`\n*   **Method**: `POST`\n*   **URL**: `http:\u002F\u002Fvulnerable-wp.local\u002Fwp-json\u002Fwpzoom-portfolio\u002Fv1\u002Fsettings`\n*   **Headers**: \n    *   `Content-Type: application\u002Fjson`\n    *   `X-WP-Nonce: [EXTRACTED_NONCE]` (if required)\n*   **Body**:\n    ```json\n    {\n      \"setting_options\": {\n        \"viewAllLabel\": \"\u003Cimg src=x onerror=alert('XSS_SUCCESS_DOM_DOMAIN_'+document.domain)>\",\n        \"readMoreLabel\": \"Read More\"\n      }\n    }\n    ```\n\n### 6. Test Data Setup\n1.  **Install Plugin**: Ensure WPZOOM Portfolio Lite version 1.4.29 is active.\n2.  **Add Portfolio Items**: Create dummy portfolio items so the block has something to render.\n    ```bash\n    wp post create --post_type=portfolio_item --post_title=\"Project Alpha\" --post_status=publish\n    ```\n3.  **Place Block**: Ensure the Portfolio block or shortcode `[wpzoom_portfolio]` is on the homepage or a public page.\n\n### 7. Expected Results\n*   The REST API should return a `200 OK` or `201 Created` response.\n*   Upon visiting the public page with the portfolio, an alert box showing the document domain should appear.\n*   The payload should be visible (untransformed) in the HTML source code where the \"View All\" button or portfolio labels are rendered.\n\n### 8. Verification Steps\nAfter the `http_request`, verify the option was successfully updated in the database:\n```bash\n# Check the stored settings option\nwp option get wpzoom-portfolio-settings --format=json\n```\nVerify the output contains the payload in the `viewAllLabel` key.\n\n### 9. Alternative Approaches\n*   **CSS Injection**: If labels are sanitized, target the `primaryColor` or `backgroundColor` attributes. The `sanitize_css_value` function only strips `\u003C>{}\\`, which might allow for CSS-based exfiltration or breakout if the output context is an inline `style` attribute.\n*   **Block Attribute Update**: If the global settings REST route is patched, check for a REST route that allows updating individual block attributes in a post's `post_content_filtered` or via a dedicated `wpzoom-portfolio\u002Fv1\u002Flayout` endpoint.\n*   **AJAX Fallback**: Check for `wp_ajax_nopriv_wpzoom_portfolio_save_settings` in `wp-admin\u002Fadmin-ajax.php` if the REST API is disabled.","The WPZOOM Portfolio Lite plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to 1.4.29. The vulnerability exists because the plugin's REST API settings endpoint lacks proper authorization and fails to sanitize or escape settings like 'lightbox_caption' and other labels, allowing unauthenticated attackers to inject arbitrary web scripts into portfolio blocks.","\u002F\u002F build\u002Fblocks\u002Fportfolio\u002Findex.php line 990\n$show_lightbox_image_caption = isset( $args[ 'lightbox_caption' ] ) ? $args[ 'lightbox_caption' ] : true;\n\n---\n\n\u002F\u002F build\u002Fblocks\u002Fportfolio\u002Findex.php line 1070\n$output .= '\u003Cdiv class=\"portfolio-block-entry-thumbnail-popover-content\" data-show-caption=\"' . $show_lightbox_image_caption . '\">';","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpzoom-portfolio\u002F1.4.29\u002Fbuild\u002Fblocks\u002Fportfolio\u002Findex.php\t2026-06-22 16:33:10.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpzoom-portfolio\u002F1.4.30\u002Fbuild\u002Fblocks\u002Fportfolio\u002Findex.php\t2026-06-28 06:44:42.000000000 +0000\n@@ -987,7 +987,13 @@\n \t\t\t);\n \t\t}\n \n-\t\t$show_lightbox_image_caption = isset( $args[ 'lightbox_caption' ] ) ? $args[ 'lightbox_caption' ] : true;\n+\t\t\u002F\u002F Normalize to a strict boolean. This value is concatenated into the\n+\t\t\u002F\u002F data-show-caption HTML attribute below; it must never carry an\n+\t\t\u002F\u002F attacker-supplied string (the load_more_items AJAX action exposes\n+\t\t\u002F\u002F $args['lightbox_caption'] to unauthenticated input). Casting here,\n+\t\t\u002F\u002F combined with esc_attr() at the sink, prevents reflected XSS while\n+\t\t\u002F\u002F preserving the legitimate \"1\"\u002F\"\" output the frontend JS expects.\n+\t\t$show_lightbox_image_caption = isset( $args[ 'lightbox_caption' ] ) ? (bool) $args[ 'lightbox_caption' ] : true;\n \n \t\t\u002F\u002F Perform the query to get the desired portfolio items\n \t\t$query = new WP_Query( $params );\n@@ -1067,7 +1073,7 @@\n \t\t\t\t\t\t\t\u003Ca href='$permalink' title='$title_attr' rel='bookmark'>$thumbnail\u003C\u002Fa>\n \t\t\t\t\t\t\u003C\u002Fdiv>\";\n \t\t\t\t\t\t\n-\t\t\t\t\t$output .= '\u003Cdiv class=\"portfolio-block-entry-thumbnail-popover-content\" data-show-caption=\"' . $show_lightbox_image_caption . '\">';\n+\t\t\t\t\t$output .= '\u003Cdiv class=\"portfolio-block-entry-thumbnail-popover-content\" data-show-caption=\"' . esc_attr( $show_lightbox_image_caption ? '1' : '' ) . '\">';\n \n \t\t\t\t\tif( $args[ 'lightbox' ] ) {\n \t\t\t\t\t\t\u002F\u002F Add the lightbox icon","The exploit involves sending a POST request to the unauthenticated REST API endpoint at `\u002Fwp-json\u002Fwpzoom-portfolio\u002Fv1\u002Fsettings`. An attacker provides a JSON payload that updates global plugin settings or block attributes, specifically targeting fields like 'lightbox_caption' or 'viewAllLabel'. By setting these values to a malicious script (e.g., '>\u003Cscript>alert(document.domain)\u003C\u002Fscript>'), the payload is stored in the WordPress database via 'update_option'. Because the plugin renders these values into HTML attributes without using 'esc_attr()' or proper casting, the script executes in the context of any user who visits a page containing the portfolio block or triggers the 'load_more_items' AJAX action.","gemini-3-flash-preview","2026-07-15 10:12:21","2026-07-15 10:13:33",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","1.4.29","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpzoom-portfolio\u002Ftags\u002F1.4.29","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwpzoom-portfolio.1.4.29.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpzoom-portfolio\u002Ftags\u002F1.4.30","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwpzoom-portfolio.1.4.30.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpzoom-portfolio\u002Ftags"]