[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fS5in0IdS6EphyHzeuN3gvlxxNZ52YB-rp_wyvneybHM":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-1582","wp-all-export-unauthenticated-sensitive-information-exposure-via-php-type-juggling","WP All Export \u003C= 1.4.14 - Unauthenticated Sensitive Information Exposure via PHP Type Juggling","The WP All Export plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 1.4.14 via the export download endpoint. This is due to a PHP type juggling vulnerability in the security token comparison which uses loose comparison (==) instead of strict comparison (===). This makes it possible for unauthenticated attackers to bypass authentication using \"magic hash\" values when the expected MD5 hash prefix happens to be numeric-looking (matching pattern ^0e\\d+$), allowing download of sensitive export files containing PII, business data, or database information.","wp-all-export",null,"\u003C=1.4.14","1.4.15","low",3.7,"CVSS:3.1\u002FAV:N\u002FAC:H\u002FPR:N\u002FUI:N\u002FS:U\u002FC:L\u002FI:N\u002FA:N","Exposure of Sensitive Information to an Unauthorized Actor","2026-02-17 00:00:00","2026-02-18 12:28:37",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F9a92c682-b8b3-4d23-bd84-97d7440ee525?source=api-prod",2,[22,23,24,25,26,27,28,29],"actions\u002Fpmxe_after_export.php","actions\u002Fwp_ajax_wpae_preview.php","actions\u002Fwp_loaded.php","classes\u002FXMLWriter.php","classes\u002Fhandler.php","classes\u002Fhelper.php","controllers\u002Fcontroller\u002Fadmin.php","helpers\u002Fpmxe_prepare_price.php","researched",false,3,"# Exploitation Research Plan - CVE-2026-1582\n\n## 1. Vulnerability Summary\nThe **WP All Export** plugin (up to version 1.4.14) is vulnerable to **Sensitive Information Exposure** due to a PHP type juggling vulnerability in its export download authentication logic. The plugin uses a loose comparison (`==`) instead of a strict comparison (`===`) when validating a security token provided in the URL against a generated MD5 hash.\n\nIn `actions\u002Fwp_loaded.php`, the plugin validates the `security_token` or `export_hash` by comparing it to the first 16 characters of `md5($cron_job_key . $export_id)`. If the resulting MD5 hash starts with \"0e\" followed only by numbers (a \"magic hash\"), PHP interprets both the hash and an attacker-provided magic hash (e.g., `0e12345...`) as the float `0.0`, bypassing the authentication check. This allows unauthenticated attackers to download export files containing sensitive site data.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** The main WordPress root (`\u002F`) triggers the `wp_loaded` hook.\n- **Parameters:**\n    - `action`: Must be `get_data` or `get_bundle`.\n    - `export_id`: The ID of the export to download (e.g., `1`).\n    - `security_token` (or `export_hash`): The attacker-provided payload.\n- **Authentication:** Unauthenticated.\n- **Preconditions:**\n    - An export must exist.\n    - The `cron_job_key` concatenated with the `export_id` must result in an MD5 hash that starts with `0e` followed by digits (within the first 16 characters).\n\n## 3. Code Flow\n1. **Entry Point:** `actions\u002Fwp_loaded.php` defines `pmxe_wp_loaded()`, which is hooked to `wp_loaded`.\n2. **Input Capture:**\n   - The function checks `if ( ! empty($_GET['action']) && ! empty($_GET['export_id']) && (!empty($_GET['export_hash']) || !empty($_GET['security_token'])))`.\n   - It retrieves `$securityToken` from `$_GET['security_token']` or `$_GET['export_hash']`.\n3. **Vulnerable Logic (Line ~16):**\n   ```php\n   $cron_job_key = PMXE_Plugin::getInstance()->getOption('cron_job_key');\n   if ( $securityToken == substr(md5($cron_job_key . $_GET['export_id']), 0, 16) )\n   ```\n4. **Sink:** If the comparison returns `true`, the code proceeds to locate the export file path and either redirects the user to the file (`wp_redirect($fileurl)`) or serves it via `readfile($filepath)`.\n\n## 4. Nonce Acquisition Strategy\n**No nonce is required.** The `pmxe_wp_loaded` function executes early in the WordPress lifecycle and does not perform any nonce or capability checks, relying solely on the vulnerable hash comparison for \"security.\"\n\n## 5. Exploitation Strategy\nTo demonstrate the vulnerability in an automated test environment, we must ensure the `cron_job_key` and `export_id` result in a magic hash.\n\n### Step-by-Step Plan:\n1. **Identify\u002FSet `cron_job_key`:** Use WP-CLI to find a key that, when combined with `export_id=1`, produces an MD5 starting with `0e` followed by digits.\n   - Example: `md5(\"7313010\" . \"1\")` = `0e71630...`.\n   - We will set the plugin's `cron_job_key` to `7313010`.\n2. **Create an Export:** Use WP-CLI to create at least one export record so ID 1 exists and has a file associated with it.\n3. **Execute Attack:** Send a GET request to the site root with `action=get_data`, `export_id=1`, and `security_token=0e000000000000000000000000000000`.\n4. **Verify Bypass:** The server should respond with a `302 Redirect` to the export file or a `200 OK` containing the file data, rather than a `403` \"Export hash is not valid.\"\n\n## 6. Test Data Setup\n1. **Install Plugin:** Ensure `wp-all-export` v1.4.14 is active.\n2. **Configure Secret Key:**\n   ```bash\n   # Set a key that produces a magic hash for ID 1\n   # md5(\"7313010\" . \"1\") starts with 0e71... (digits only)\n   wp eval 'PMXE_Plugin::getInstance()->updateOption(\"cron_job_key\", \"7313010\");'\n   ```\n3. **Create Export Data:**\n   - Create a dummy post or user to export.\n   - Create an export record in the database.\n   ```bash\n   # Use wp eval to create an export record for ID 1\n   wp eval '\n   $export = new PMXE_Export_Record();\n   $export->set(array(\n       \"id\" => 1,\n       \"options\" => array(\n           \"filepath\" => \"wp-content\u002Fuploads\u002Fwp-all-export\u002Fexports\u002Ftest.csv\",\n           \"export_to\" => \"csv\"\n       ),\n       \"attch_id\" => 0\n   ))->save();\n   mkdir(ABSPATH . \"wp-content\u002Fuploads\u002Fwp-all-export\u002Fexports\u002F\", 0777, true);\n   file_put_contents(ABSPATH . \"wp-content\u002Fuploads\u002Fwp-all-export\u002Fexports\u002Ftest.csv\", \"id,secret_data\\n1,vulnerable_info\");\n   '\n   ```\n\n## 7. Expected Results\n- **Request:** `GET \u002F?action=get_data&export_id=1&security_token=0e11111111111111`\n- **Response:** `HTTP 302` (Redirect to the CSV file) or `HTTP 200` (File content).\n- **Failure Case (Strict Comparison):** If the comparison were `===`, the response would be `HTTP 403` with the message `{\"status\":403,\"message\":\"Export hash is not valid.\"}`.\n\n## 8. Verification Steps\n1. **Check Response Code:** Ensure the response is not a `403 Forbidden`.\n2. **Check Content:** If the response is a redirect, follow it and verify the CSV content matches the dummy data created in Step 6.\n3. **Confirm PHP version:** Type juggling behavior is most prominent in PHP 7.x, though it persists in PHP 8.x for string-to-string comparisons like this one.\n\n## 9. Alternative Approaches\nIf setting the `cron_job_key` via `updateOption` fails, the agent can:\n1. Manually insert the option into the `wp_options` table using `wp db query`.\n2. Brute-force the `export_id` (e.g., from 1 to 5000) while keeping a fixed `security_token=0e123...` until one hits a magic hash collision with the existing `cron_job_key`. This is more \"realistic\" but slower.\n3. If the export file is served via `wp_redirect`, the agent must ensure it captures the `Location` header to verify access to the actual file.","The WP All Export plugin for WordPress is vulnerable to Sensitive Information Exposure due to a PHP type juggling flaw in its export download authentication logic. By using a loose comparison (==) to validate security tokens, the plugin allows unauthenticated attackers to bypass authentication using 'magic hash' values (e.g., 0e123...) when the server-side MD5 hash prefix also matches a numeric scientific notation pattern.","\u002F\u002F actions\u002Fwp_loaded.php line 19\n\t\t$cron_job_key = PMXE_Plugin::getInstance()->getOption('cron_job_key');\n\n\t\tif ( $securityToken == substr(md5($cron_job_key . $_GET['export_id']), 0, 16) )\n\t\t{","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.14\u002Factions\u002Fpmxe_after_export.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.15\u002Factions\u002Fpmxe_after_export.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.14\u002Factions\u002Fpmxe_after_export.php\t2022-12-08 07:43:36.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.15\u002Factions\u002Fpmxe_after_export.php\t2026-02-07 04:59:38.000000000 +0000\n@@ -71,12 +71,12 @@\n             $in  = fopen($tmp_file, 'r');\n             $out = fopen($filepath, 'w');\n \n-            $headers = fgetcsv($in, 0, XmlExportEngine::$exportOptions['delimiter']);\n+            $headers = fgetcsv($in, 0, XmlExportEngine::$exportOptions['delimiter'], '\"', '\\\\');\n \n             if (is_resource($in)) {\n                 $lineNumber = 0;\n                 while ( ! feof($in) ) {\n-                    $data = fgetcsv($in, 0, XmlExportEngine::$exportOptions['delimiter']);\n+                    $data = fgetcsv($in, 0, XmlExportEngine::$exportOptions['delimiter'], '\"', '\\\\');\n                     if ( empty($data) ) continue;\n                     $data_assoc = array_combine($headers, array_values($data));\n                     $line = array();\n@@ -85,10 +85,10 @@\n                     }\n                     if ( ! $lineNumber && XmlExportEngine::$exportOptions['include_bom']){\n                         fwrite($out, chr(0xEF).chr(0xBB).chr(0xBF));\n-                        fputcsv($out, $line, XmlExportEngine::$exportOptions['delimiter']);\n+                        fputcsv($out, $line, XmlExportEngine::$exportOptions['delimiter'], '\"', '\\\\');\n                     }\n                     else{\n-                        fputcsv($out, $line, XmlExportEngine::$exportOptions['delimiter']);\n+                        fputcsv($out, $line, XmlExportEngine::$exportOptions['delimiter'], '\"', '\\\\');\n                     }\n                     apply_filters('wp_all_export_after_csv_line', $out, XmlExportEngine::$exportID);\n                     $lineNumber++;\n@@ -197,9 +197,9 @@\n \n \t\t\t\t\t\t$rowCount  = 0;\n \t\t\t\t\t\t$fileCount = 1;\n-\t\t\t\t\t\t$headers = fgetcsv($in);\n+\t\t\t\t\t\t$headers = fgetcsv($in, 0, ',', '\"', '\\\\');\n \t\t\t\t\t\twhile (!feof($in)) {\n-\t\t\t\t\t\t    $data = fgetcsv($in);\n+\t\t\t\t\t\t    $data = fgetcsv($in, 0, ',', '\"', '\\\\');\n \t\t\t\t\t\t    if (empty($data)) continue;\n \t\t\t\t\t\t    if (($rowCount % $splitSize) == 0) {\n \t\t\t\t\t\t        if ($rowCount > 0) {\n@@ -213,9 +213,9 @@\n \t\t\t\t\t\t    }\n \t\t\t\t\t\t    if ($data){\n \t\t\t\t\t\t    \tif (($rowCount % $splitSize) == 0) {\n-\t\t\t\t\t\t    \t\tfputcsv($out, $headers);\n+\t\t\t\t\t\t    \t\tfputcsv($out, $headers, ',', '\"', '\\\\');\n \t\t\t\t\t\t    \t}\n-\t\t\t\t\t\t        fputcsv($out, $data);\n+\t\t\t\t\t\t        fputcsv($out, $data, ',', '\"', '\\\\');\n \t\t\t\t\t\t    }\n \t\t\t\t\t\t    $rowCount++;\n \t\t\t\t\t\t}\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.14\u002Factions\u002Fwp_ajax_wpae_preview.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.15\u002Factions\u002Fwp_ajax_wpae_preview.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.14\u002Factions\u002Fwp_ajax_wpae_preview.php\t2024-07-24 11:50:12.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.15\u002Factions\u002Fwp_ajax_wpae_preview.php\t2026-02-07 04:59:38.000000000 +0000\n@@ -416,7 +416,7 @@\n \t\t\t\t\t\t\t\u003Ctable class=\"pmxe_preview\" cellpadding=\"0\" cellspacing=\"0\">\n \t\t\t\t\t\t\t\u003C?php\n \t\t\t\t\t\t\tforeach ($csv_rows as $rkey => $row) {\n-\t\t\t\t\t\t\t\t$cells = str_getcsv($row, $exportOptions['delimiter']);\n+\t\t\t\t\t\t\t\t$cells = str_getcsv($row, $exportOptions['delimiter'], '\"', '\\\\');\n \t\t\t\t\t\t\t\tif ($cells){\n \t\t\t\t\t\t\t\t\t?>\n \t\t\t\t\t\t\t\t\t\u003Ctr>\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.14\u002Factions\u002Fwp_loaded.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.15\u002Factions\u002Fwp_loaded.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.14\u002Factions\u002Fwp_loaded.php\t2021-10-18 12:29:48.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-all-export\u002F1.4.15\u002Factions\u002Fwp_loaded.php\t2026-02-07 04:59:38.000000000 +0000\n@@ -16,7 +16,7 @@\n \n \t\t$cron_job_key = PMXE_Plugin::getInstance()->getOption('cron_job_key');\n \n-\t\tif ( $securityToken == substr(md5($cron_job_key . $_GET['export_id']), 0, 16) )\n+\t\tif ( hash_equals( substr( md5( $cron_job_key . $_GET['export_id'] ), 0, 16 ), $securityToken ) )\n \t\t{\n \t\t\t$export = new PMXE_Export_Record();","To exploit this vulnerability, an unauthenticated attacker identifies a numeric export ID and targets the WordPress root endpoint using the `action=get_data` parameter. The attacker provides a 'magic hash' value (such as `0e12345678901234`) via the `security_token` or `export_hash` GET parameter. If the server-side generated token (the first 16 characters of the MD5 hash of the secret `cron_job_key` and the `export_id`) also begins with `0e` followed by digits, PHP's loose comparison evaluates both sides as `0.0`, resulting in a match. This bypasses authentication and causes the plugin to either redirect the attacker to the sensitive export file or serve its contents directly.","gemini-3-flash-preview","2026-04-20 21:34:18","2026-04-20 21:34:47",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","1.4.14","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-all-export\u002Ftags\u002F1.4.14","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-all-export.1.4.14.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-all-export\u002Ftags\u002F1.4.15","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-all-export.1.4.15.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-all-export\u002Ftags"]