[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f-ZCHsf5eRGyG1iGP2j7aTbQWXNQug1GdFjPj7QoTOm4":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":9,"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-6127","elementor-website-builder-authenticated-contributor-stored-cross-site-scripting-via-rest-api-2","Elementor Website Builder \u003C= 4.0.4 - Authenticated (Contributor+) Stored Cross-Site Scripting via REST API","The Elementor Website Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the _elementor_data meta field in versions up to, and including, 4.0.4. This is due to insufficient input sanitization when processing form-encoded REST API requests. The plugin registers the _elementor_data meta field with show_in_rest but omits a sanitize_callback, relying instead on a rest_pre_insert_post filter (sanitize_post_data function) that only sanitizes JSON-encoded request bodies. When a contributor sends a form-encoded PATCH request to the WordPress REST API, the json_decode() call on the raw body returns null, causing all sanitization to be skipped. The unsanitized data is then stored via update_post_meta() and later output without escaping through multiple widget sinks including the HTML widget's print_unescaped_setting() function. 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.","elementor",null,"\u003C=4.0.4","4.0.5","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-04-30 00:00:00","2026-05-01 05:29:54",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F826a2003-c526-4760-8c21-10d5ae7bb384?source=api-prod",1,[22,23,24,25,26,27,28,29],"app\u002Fmodules\u002Fimport-export-customization\u002Fdata\u002Froutes\u002Fprocess-media.php","assets\u002Fjs\u002Fadmin-modules.strings.js","assets\u002Fjs\u002Fai.strings.js","assets\u002Fjs\u002Fapp-loader.strings.js","assets\u002Fjs\u002Fapp-packages.strings.js","assets\u002Fjs\u002Fapp.strings.js","assets\u002Fjs\u002Feditor-document.strings.js","assets\u002Fjs\u002Feditor-modules.strings.js","researched",false,3,"This research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the Elementor Website Builder plugin (\u003C= 4.0.4).\n\n### 1. Vulnerability Summary\nThe vulnerability exists because Elementor registers the `_elementor_data` meta field for use in the WordPress REST API but fails to provide a `sanitize_callback` during registration. Instead, it relies on a custom filter hooked to `rest_pre_insert_post` (and `rest_pre_insert_page`) named `sanitize_post_data`. \n\nThis filter attempts to decode the raw request body as JSON to sanitize the Elementor layout data. However, if an attacker sends a request with `Content-Type: application\u002Fx-www-form-urlencoded`, the `json_decode()` call on the raw body returns `null`. The function then exits early without performing any sanitization, allowing unsanitized JSON (containing XSS payloads) to be stored in the `_elementor_data` meta field.\n\n### 2. Attack Vector Analysis\n*   **Endpoint:** `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts\u002F\u003CID>` or `\u002Fwp-json\u002Fwp\u002Fv2\u002Fpages\u002F\u003CID>`\n*   **Method:** `POST` (with `_method=PATCH`) or `PATCH`\n*   **Authentication:** Authenticated (Contributor level or higher).\n*   **Vulnerable Parameter:** `meta[_elementor_data]`\n*   **Content-Type:** `application\u002Fx-www-form-urlencoded`\n*   **Payload Sink:** The HTML widget within Elementor, which outputs data via `print_unescaped_setting()`.\n\n### 3. Code Flow\n1.  **Entry Point:** An authenticated user sends a `PATCH` request to the WordPress REST API to update a post they have permission to edit.\n2.  **Meta Registration:** Elementor calls `register_meta( 'post', '_elementor_data', ... )` with `'show_in_rest' => true`, but without a `'sanitize_callback'`.\n3.  **Vulnerable Filter:** The `rest_pre_insert_post` filter triggers Elementor's `sanitize_post_data` function.\n4.  **The Bypass:**\n    *   `sanitize_post_data` calls `json_decode( $request->get_body() )`.\n    *   Because the request is `application\u002Fx-www-form-urlencoded`, the raw body is a string like `meta%5B_elementor_data%5D=...`, which is not valid JSON.\n    *   `json_decode` returns `null`.\n    *   The function returns the `$prepared_post` data unchanged.\n5.  **Storage:** WordPress core processes the `meta` array in the request and updates the `_elementor_data` post meta using `update_post_meta()` because it is registered as `show_in_rest`.\n6.  **Rendering:** When a user views the post, Elementor parses `_elementor_data`. The HTML widget processes the `html` setting and renders it directly to the page without escaping.\n\n### 4. Nonce Acquisition Strategy\nThe WordPress REST API requires a `wp_rest` nonce for authenticated requests.\n\n1.  **Identify Trigger:** The REST nonce is globally available in the WordPress admin dashboard for logged-in users.\n2.  **Access Strategy:**\n    *   Log in as a Contributor.\n    *   Navigate to the WordPress Dashboard (`\u002Fwp-admin\u002F`).\n    *   Use `browser_eval` to extract the nonce from the `wpApiSettings` object.\n3.  **JavaScript Path:** `window.wpApiSettings.nonce`\n\n### 5. Exploitation Strategy\n1.  **Preparation:** Identify or create a post\u002Fpage ID where the Contributor is the author.\n2.  **Payload Construction:** Create a JSON string for `_elementor_data` that defines an HTML widget containing the XSS payload.\n    ```json\n    [{\"id\":\"poc_id\",\"elType\":\"widget\",\"widgetType\":\"html\",\"settings\":{\"html\":\"\u003Cscript>alert(document.domain)\u003C\u002Fscript>\"},\"elements\":[]}]\n    ```\n3.  **Request Execution:** Use the `http_request` tool to send a `POST` request to the REST API with the `_method=PATCH` override.\n    *   **URL:** `https:\u002F\u002F\u003Ctarget>\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts\u002F\u003CID>`\n    *   **Headers:**\n        *   `X-WP-Nonce`: `\u003Cextracted_nonce>`\n        *   `Content-Type`: `application\u002Fx-www-form-urlencoded`\n    *   **Body:** \n        `meta[_elementor_data]=[{\"id\":\"xss\",\"elType\":\"widget\",\"widgetType\":\"html\",\"settings\":{\"html\":\"\u003Cscript>alert(document.domain)\u003C\u002Fscript>\"},\"elements\":[]}]&meta[_elementor_edit_mode]=builder`\n\n### 6. Test Data Setup\n1.  **User:** Create a user with the `contributor` role.\n2.  **Content:** As the contributor, create a draft post:\n    `wp post create --post_type=post --post_title='XSS Test' --post_status=draft --post_author=\u003Cuser_id>`\n3.  **Permissions:** Ensure the contributor can edit this specific post ID.\n\n### 7. Expected Results\n*   The REST API should return a `200 OK` response with the updated post data.\n*   The `_elementor_data` field in the database should contain the raw, unsanitized JSON string with the `\u003Cscript>` tag.\n*   When navigating to the post URL (or previewing it), a browser alert box should appear showing the document domain.\n\n### 8. Verification Steps\n1.  **Database Check:** Use WP-CLI to verify the stored meta value:\n    `wp post meta get \u003CID> _elementor_data`\n    *Confirmation:* The output should contain the raw `\u003Cscript>` payload.\n2.  **Frontend Check:** Use `browser_navigate` to visit the post URL and check for the presence of the script in the DOM or the execution of the alert.\n\n### 9. Alternative Approaches\nIf the standard `posts` endpoint is restricted, try the `pages` endpoint:\n*   **Endpoint:** `\u002Fwp-json\u002Fwp\u002Fv2\u002Fpages\u002F\u003CID>`\n*   **Payload Modification:** If the HTML widget is somehow blocked, attempt to use the `text-editor` widget or the `heading` widget, though these may have more internal sanitization than the `html` widget.\n*   **JSON in Form Body:** If the `meta` parameter is ignored, try sending the payload as a single url-encoded parameter: `_elementor_data=\u003Cpayload>` (though standard REST API updates specifically look for the `meta` key).","Elementor Website Builder (\u003C= 4.0.4) is vulnerable to Stored Cross-Site Scripting via the _elementor_data meta field. Authenticated contributors can bypass the plugin's custom REST API sanitization filter by sending form-encoded requests, which causes a JSON decoding failure that allows unsanitized payloads to be stored and subsequently executed in the context of other users.","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Felementor\u002F4.0.4\u002Fapp\u002Fmodules\u002Fimport-export-customization\u002Fdata\u002Froutes\u002Fprocess-media.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Felementor\u002F4.0.5\u002Fapp\u002Fmodules\u002Fimport-export-customization\u002Fdata\u002Froutes\u002Fprocess-media.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Felementor\u002F4.0.4\u002Fapp\u002Fmodules\u002Fimport-export-customization\u002Fdata\u002Froutes\u002Fprocess-media.php\t2025-10-27 10:18:40.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Felementor\u002F4.0.5\u002Fapp\u002Fmodules\u002Fimport-export-customization\u002Fdata\u002Froutes\u002Fprocess-media.php\t2026-04-30 11:19:30.000000000 +0000\n@@ -31,6 +31,10 @@\n \n \t\t$cloud_kit_library_app = $this->get_cloud_kit_library_app();\n \n+\t\tif ( $cloud_kit_library_app && ! $cloud_kit_library_app->is_connected() ) {\n+\t\t\treturn Response::error( ImportExportCustomizationModule::MEDIA_PROCESSING_ERROR, 'Cloud Library is not connected' );\n+\t\t}\n+\n \t\t$media_urls = $request->get_param( 'media_urls' );\n \t\t$kit = $request->get_param( 'kit' );\n \t\t$quota = null;\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Felementor\u002F4.0.4\u002Fassets\u002Fjs\u002Fadmin-modules.strings.js \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Felementor\u002F4.0.5\u002Fassets\u002Fjs\u002Fadmin-modules.strings.js\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Felementor\u002F4.0.4\u002Fassets\u002Fjs\u002Fadmin-modules.strings.js\t2026-04-28 08:44:08.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Felementor\u002F4.0.5\u002Fassets\u002Fjs\u002Fadmin-modules.strings.js\t2026-04-30 11:19:30.000000000 +0000\n@@ -23,6 +23,6 @@\n __( 'Submit & Deactivate', 'elementor' );\n __( 'Skip & Deactivate', 'elementor' );\n __( 'New Template', 'elementor' );\n+__( 'New Floating Elements', 'elementor' );\n __( 'Sign Up', 'elementor' );\n-__( 'Don\\'t Show Again', 'elementor' );\n-__( 'New Floating Elements', 'elementor' );\n\\ No newline at end of file\n+__( 'Don\\'t Show Again', 'elementor' );\n\\ No newline at end of file\n... (truncated)","1. Authenticate as a user with Contributor level permissions or higher.\n2. Obtain a valid WordPress REST API nonce (found in the `wpApiSettings` JavaScript object on the dashboard).\n3. Identify a post or page ID that the contributor has permission to edit.\n4. Construct a malicious JSON payload for the `_elementor_data` meta field. This payload should define an HTML widget containing a Cross-Site Scripting (XSS) script in its 'html' setting: `[{\"id\":\"xss\",\"elType\":\"widget\",\"widgetType\":\"html\",\"settings\":{\"html\":\"\u003Cscript>alert(document.domain)\u003C\u002Fscript>\"},\"elements\":[]}]`.\n5. Send a `PATCH` (or `POST` with `_method=PATCH`) request to the WordPress REST API endpoint `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts\u002F\u003CID>`.\n6. Set the `Content-Type` header to `application\u002Fx-www-form-urlencoded`.\n7. Include the malicious layout in the `meta[_elementor_data]` parameter. By using form-encoding, the attacker causes the plugin's `rest_pre_insert_post` filter to fail its `json_decode()` attempt on the raw body, which results in the sanitization logic being bypassed.\n8. Once the request is successful, navigate to the modified post. The unsanitized script will execute via the Elementor HTML widget's unescaped output sink.","gemini-3-flash-preview","2026-05-04 17:43:21","2026-05-04 17:43:52",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","4.0.4","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Felementor\u002Ftags\u002F4.0.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Felementor.4.0.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Felementor\u002Ftags\u002F4.0.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Felementor.4.0.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Felementor\u002Ftags"]