[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fwFzk0FlTa1Dzp9BpCcZRrjlFI-La_HdZ6e04rPVdjeU":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":22,"research_verified":23,"research_rounds_completed":24,"research_plan":25,"research_summary":26,"research_vulnerable_code":27,"research_fix_diff":28,"research_exploit_outline":29,"research_model_used":30,"research_started_at":31,"research_completed_at":32,"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":23,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":23,"source_links":33},"CVE-2025-13859","affiliatex-authenticated-subscriber-missing-authorization-to-stored-cross-site-scripting-via-savecustomizationsettings","AffiliateX 1.0.0 - 1.3.9.3 - Authenticated (Subscriber+) Missing Authorization to Stored Cross-Site Scripting via save_customization_settings","The AffiliateX – Amazon Affiliate Plugin plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the save_customization_settings AJAX action in versions 1.0.0 to 1.3.9.3. This makes it possible for authenticated attackers, with Subscriber-level access and above, to store arbitrary JavaScript that executes whenever an AffiliateX block renders on the site.","affiliatex",null,">=1.0.0 \u003C=1.3.9.3","1.4.0","medium",6.4,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Missing Authorization","2026-01-15 00:30:33","2026-01-15 13:23:26",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F36d57b8d-7e62-413b-8ea9-87963b8cd469?source=api-prod",1,[],"researched",false,3,"# Research Plan: CVE-2025-13859 AffiliateX Missing Authorization to Stored XSS\n\n## 1. Vulnerability Summary\nThe **AffiliateX** plugin (versions 1.0.0 - 1.3.9.3) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `save_customization_settings` AJAX action. The vulnerability exists because the plugin registers an AJAX handler that lacks a capability check (e.g., `current_user_can( 'manage_options' )`), allowing any authenticated user—including those with **Subscriber** permissions—to update plugin settings. These settings are subsequently rendered on the frontend when AffiliateX blocks are displayed, leading to arbitrary JavaScript execution in the context of site visitors and administrators.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action:** `save_customization_settings` (hooked via `wp_ajax_save_customization_settings`)\n- **Vulnerable Parameter:** Likely a POST parameter named `settings`, `custom_settings`, or individual fields within the customization payload.\n- **Authentication:** Required (Subscriber level or higher).\n- **Preconditions:**\n    - The attacker must be logged in as a Subscriber.\n    - A valid WordPress nonce for the action must be obtained (usually exposed in the admin dashboard for all logged-in users).\n    - At least one AffiliateX block must be present or added to a post\u002Fpage to trigger the XSS.\n\n## 3. Code Flow\n1. **Registration:** The plugin registers the AJAX action in the admin initialization phase:\n   `add_action( 'wp_ajax_save_customization_settings', [ $class, 'save_customization_settings' ] );`\n2. **Handler Execution:** The `save_customization_settings` function is called. It likely performs a nonce check using `check_ajax_referer()` but fails to verify the user's capabilities.\n3. **Data Storage:** The function retrieves user-supplied data from `$_POST` and saves it using `update_option()` (e.g., `update_option( 'affiliatex_customization_settings', ... )`) without proper sanitization\u002Fescaping of HTML\u002FScript tags.\n4. **Frontend Rendering:** When a post containing an AffiliateX block (e.g., \"Pros and Cons\", \"Comparison Table\") is viewed, the plugin retrieves the saved option via `get_option()` and echoes it into the page source (often within a `\u003Cscript>` or `\u003Cstyle>` block, or as an attribute), triggering the stored XSS.\n\n## 4. Nonce Acquisition Strategy\nThe `save_customization_settings` action is used in the plugin's administration\u002Fcustomization interface. WordPress plugins typically localize nonces into the page source for their JavaScript components.\n\n1. **Access Point:** Subscribers can access `\u002Fwp-admin\u002Fprofile.php`. The plugin likely enqueues its admin scripts on all admin pages or specifically on a customization page that Subscribers might reach if authorization is missing.\n2. **Identification:**\n   - Search for `wp_localize_script` in the plugin source to find the variable name.\n   - **Inferred Variable Name:** `affiliatex_admin_obj` or `affiliatex_vars`.\n   - **Inferred Nonce Key:** `nonce` or `security`.\n3. **Extraction:**\n   - Log in as a Subscriber.\n   - Navigate to `\u002Fwp-admin\u002Fprofile.php`.\n   - Execute: `browser_eval(\"window.affiliatex_admin_obj?.nonce\")` (Replace with the actual variable discovered during initial recon).\n4. **Action String:** The nonce is likely created for the action `save_customization_settings`.\n\n## 5. Exploitation Strategy\n1. **Reconnaissance:**\n   - Use `grep -r \"save_customization_settings\" .` to find the handler and identify the exact parameter name used for the settings data.\n   - Identify the nonce action string and the JS localization key.\n2. **Data Setup:**\n   - Create a Subscriber user.\n   - Create a public post containing an AffiliateX block (e.g., `[affiliatex_pros_cons]`).\n3. **Payload Construction:**\n   - Construct a JSON payload or POST string that includes the XSS: `\u003Cscript>alert(document.domain)\u003C\u002Fscript>`.\n4. **Execution:**\n   - Authenticate as the Subscriber via the `http_request` tool to get cookies.\n   - Use `http_request` to send a POST to `admin-ajax.php`.\n   ```http\n   POST \u002Fwp-admin\u002Fadmin-ajax.php HTTP\u002F1.1\n   Content-Type: application\u002Fx-www-form-urlencoded\n\n   action=save_customization_settings&security=[NONCE]&settings[custom_js]=\u003Cscript>alert(1)\u003C\u002Fscript>\n   ```\n   *(Note: The exact structure of `settings` must be verified in the source code.)*\n5. **Trigger:**\n   - Navigate to the frontend page containing the AffiliateX block to confirm the alert fires.\n\n## 6. Test Data Setup\n- **User:** `wp user create attacker attacker@example.com --role=subscriber --user_pass=password`\n- **Content:** \n  ```bash\n  wp post create --post_type=post --post_status=publish --post_title=\"Affiliate Review\" --post_content='\u003C!-- wp:affiliatex\u002Fpros-cons -->[affiliatex_pros_cons]\u003C!-- \u002Fwp:affiliatex\u002Fpros-cons -->'\n  ```\n- **Settings:** Ensure the plugin is active and at least one default block is configured.\n\n## 7. Expected Results\n- The `admin-ajax.php` request should return a `200 OK` response, likely with a JSON body: `{\"success\": true}`.\n- The `affiliatex_customization_settings` (or equivalent) option in the `wp_options` table will contain the `\u003Cscript>` payload.\n- Upon visiting the frontend post, a JavaScript alert will execute.\n\n## 8. Verification Steps\n1. **DB Check:** Use WP-CLI to verify the option was changed:\n   `wp option get affiliatex_customization_settings` (Verify the exact option name in the code).\n2. **Frontend Check:** Use the `http_request` tool to fetch the frontend page and `grep` for the payload in the HTML source.\n   `http_request GET \u002Faffiliate-review\u002F | grep \"\u003Cscript>alert(1)\u003C\u002Fscript>\"`\n\n## 9. Alternative Approaches\n- **CSS Injection:** If the plugin only allows \"Custom CSS\", use an XSS payload inside a CSS context: `body { background-image: url(\"javascript:alert(1)\"); }` or using `-webkit-mask`.\n- **Different Parameters:** If `settings` is not a single array, try injecting into specific fields like `button_text` or `label_color` if they are echoed without escaping.\n- **REST API:** Check if the plugin also exposes this functionality via a REST API endpoint (e.g., `\u002Fwp-json\u002Faffiliatex\u002Fv1\u002Fsettings`) which might have the same missing authorization.","The AffiliateX plugin for WordPress (versions 1.0.0 - 1.3.9.3) fails to perform authorization checks on its 'save_customization_settings' AJAX action. This allows authenticated users with low-level permissions, such as Subscribers, to modify plugin settings and inject arbitrary JavaScript that executes when site visitors view posts containing AffiliateX blocks.","\u002F\u002F File: includes\u002FAjax\u002FAjax.php (approximate path)\n\npublic function __construct() {\n    \u002F\u002F Registration of the vulnerable AJAX action\n    add_action( 'wp_ajax_save_customization_settings', [ $this, 'save_customization_settings' ] );\n}\n\n\u002F**\n * Vulnerable handler lacking capability checks\n *\u002F\npublic function save_customization_settings() {\n    \u002F\u002F Check for nonce, but NO check for user capabilities (e.g., current_user_can('manage_options'))\n    check_ajax_referer( 'affiliatex_nonce', 'security' );\n\n    if ( isset( $_POST['settings'] ) ) {\n        $settings = $_POST['settings'];\n        \u002F\u002F The data is saved directly to the database without proper sanitization for JS\u002FHTML\n        update_option( 'affiliatex_customization_settings', $settings );\n    }\n\n    wp_send_json_success();\n}","--- a\u002Fincludes\u002FAjax\u002FAjax.php\n+++ b\u002Fincludes\u002FAjax\u002FAjax.php\n@@ -10,6 +10,10 @@\n \tpublic function save_customization_settings() {\n \t\tcheck_ajax_referer( 'affiliatex_nonce', 'security' );\n \n+\t\tif ( ! current_user_can( 'manage_options' ) ) {\n+\t\t\twp_send_json_error( array( 'message' => 'Unauthorized access.' ) );\n+\t\t}\n+\n \t\tif ( isset( $_POST['settings'] ) ) {\n-\t\t\t$settings = $_POST['settings'];\n+\t\t\t$settings = map_deep( $_POST['settings'], 'sanitize_text_field' );\n \t\t\tupdate_option( 'affiliatex_customization_settings', $settings );\n \t\t}","The exploit targets the 'save_customization_settings' AJAX endpoint, which is accessible to any authenticated user. An attacker follows these steps: 1. Authenticate as a Subscriber-level user. 2. Extract the security nonce for the 'save_customization_settings' action, typically found in the global JavaScript objects (e.g., affiliatex_admin_obj) localized by the plugin on admin pages like \u002Fwp-admin\u002Fprofile.php. 3. Send a POST request to \u002Fwp-admin\u002Fadmin-ajax.php with the 'action' parameter set to 'save_customization_settings', the 'security' parameter set to the extracted nonce, and a 'settings' parameter containing a malicious payload like '\u003Cscript>alert(document.domain)\u003C\u002Fscript>'. 4. The malicious script is saved in the 'affiliatex_customization_settings' option. 5. Any visitor, including administrators, who views a page containing an AffiliateX block (such as a 'Pros and Cons' or 'Comparison Table' block) will trigger the stored JavaScript execution.","gemini-3-flash-preview","2026-05-05 08:50:48","2026-05-05 08:52:39",{"type":34,"vulnerable_version":35,"fixed_version":11,"vulnerable_browse":36,"vulnerable_zip":37,"fixed_browse":38,"fixed_zip":39,"all_tags":40},"plugin","1.3.9.3","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Faffiliatex\u002Ftags\u002F1.3.9.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Faffiliatex.1.3.9.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Faffiliatex\u002Ftags\u002F1.4.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Faffiliatex.1.4.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Faffiliatex\u002Ftags"]