Neutron, notre moteur d’IA, a obtenu un score de 97,1 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Analyse approfondie

Analyse approfondie

Contournement de contrôle d'accès Drupal Commerce, exploité dans la nature (CVE-2026-118539)

Drupal Commerce avant 2.41 et 3.3.13 laisse des comptes limités s'approprier variations et coupons d'autres vendeurs via JSON:API ; exploité.

En bref

  • Drupal Commerce Core ne protège pas les champs d'entité qui relient produits, variations, coupons et promotions au-delà des frontières de propriété. Un utilisateur à permissions limitées peut s'approprier les variations d'un autre vendeur ou lier les coupons d'autres promotions via JSON:API ou REST. Les clients pouvaient aussi modifier des champs restreints de leur propre panier, comme son état et ses totaux.
  • Drupal note la faille Highly critical, 21 sur 25 sur sa propre échelle, et indique qu'elle est activement exploitée dans la nature. L'avis ne donne ni acteurs, ni ampleur, ni indicateurs. CVE-2026-118539 ne porte aucun score CVSS au 2026-10-30.
  • Toutes les versions de Commerce Core avant 2.41 et de 3.0.0 à 3.3.12 sont affectées. La branche 2.x n'avait plus connu de version depuis août 2024 avant ce correctif.
  • Passez à Commerce Core 3.3.13, ou 2.41 sur l'ancienne branche, et auditez les commandes existantes à la recherche d'incohérences, comme l'exige l'avis. Voir « Comment vérifier si j'ai été compromis ? » pour les angles d'audit que le matériel public permet.

Suis-je concerné ?

Vous êtes exposé quand toutes ces conditions sont réunies :

  • Le module Commerce Core est installé en version antérieure à 2.41, ou entre 3.0.0 et 3.3.12 inclus.
  • Des comptes autres que les administrateurs complets de la boutique peuvent écrire sur des entités commerce : des clients qui mettent à jour leur propre panier, ou des comptes avec des permissions commerce comme mettre à jour leurs propres produits, gérer des variations ou modifier leurs propres promotions. La faille est un contournement de contrôle d'accès pour de tels comptes.
  • Un accès JSON:API ou REST existe : les tests du correctif l'utilisent pour chaque chemin de prise de contrôle. JSON:API est fourni avec le cœur de Drupal.

Les clients n'ont besoin d'aucune permission commerce : avant le correctif, un client disposant de l'accès en mise à jour à son propre panier pouvait modifier via JSON:API ou REST des champs restreints de la commande et de ses lignes, comme state, total_price, adjustments, unit_price et title, et atteindre les routes d'administration de cette commande. Les chemins de prise de contrôle entre vendeurs exigent un compte avec des permissions commerce limitées. Un compte qui ne pouvait pas consulter certaines variations, comme celles de produits non publiés, pouvait aussi déduire de filtres sur les collections JSON:API qu'elles existaient.

Branche Versions affectées Version corrigée
Commerce Core 3.x 3.0.0 à 3.3.12 3.3.13 (2026-10-28)
Commerce Core 2.x et antérieur Toutes les versions avant 2.41 8.x-2.41 (2026-10-28)

Les deux correctifs sont sortis le même jour. La version précédente de la branche 2.x, 8.x-2.40, date du 2024-08-22 : les sites qui la suivaient étaient restés plus de deux ans sans mise à jour.

Vérifiez ce que tourne sur votre site :

# Composer-installed sites: the resolved version in the lock file
grep -A1 '"name": "drupal/commerce"' composer.lock | grep version

# Or ask Composer directly
composer show drupal/commerce 2>/dev/null | grep ^versions

# Without Composer: the module's .info.yml carries the version when
# packaged from drupal.org
grep -r "version:" web/modules/contrib/commerce/*.info.yml 2>/dev/null

Comment corriger ?

  1. Avec Composer, selon le flux Drupal habituel : composer require 'drupal/commerce:^3.3.13' --with-all-dependencies, ou composer require 'drupal/commerce:^2.41' --with-all-dependencies sur la branche 2.x.
  2. Exécutez le pipeline de mise à jour de votre site après le changement de code, puis videz les caches : drush cr.
  3. Réappliquez tout correctif personnalisé sur le module, car les deux branches ont bougé.
  4. Auditez les commandes existantes, comme l'exige l'avis, avec les vérifications ci-dessous.

Aucune solution de contournement de configuration n'existe. L'avis n'en nomme aucune, et les tests du correctif montrent que les chemins de contournement ne se ferment qu'avec les nouveaux contrôles au niveau des champs.

Quelle est la cause racine ?

Les entités Commerce avaient un contrôle d'accès au niveau de l'entité mais aucun au niveau des champs, donc les champs de référence qui relient les entités d'un vendeur à celles d'un autre pouvaient être modifiés par tout compte autorisé à toucher ses propres entités.

Commerce Core vend des biens et services via produits, variations, promotions et coupons, chacun avec ses permissions. Un compte doté de « update own commerce_product » peut modifier ses produits. Le diff de 3.3.13 montre que rien n'empêchait un tel compte de modifier aussi les champs de référence qui franchissent la propriété : la liste de variations d'un produit, la promotion d'un coupon, les coupons référencés par une promotion, et l'indicateur de boutique qui change le comportement du site entier.

Le correctif introduit une propriété restricted_fields sur chaque type d'entité et la vérifie dans de nouveaux gestionnaires de contrôle d'accès. Voici le gestionnaire des variations, tiré du diff 3.3.13 :

// modules/product/src/ProductVariationAccessControlHandler.php
$restricted_fields = $this->entityType->get('restricted_fields') ?? [];
if ($operation == 'edit' && in_array($field_definition->getName(), $restricted_fields, TRUE)) {
  $result = AccessResult::allowedIfHasPermission($account, $this->entityType->getAdminPermission());
  /** @var \Drupal\commerce_product\Entity\ProductVariationInterface|null $variation */
  $variation = $items?->getEntity();
  if ($result->isAllowed() || !$variation?->isNew()) {
    return $result;
  }
  // Users who can create variations can set these fields on new variations
  // (e.g. through JSON:API or REST), as on the variation add form, which
  // allows adding variations to any product.
  return $result->orIf(AccessResult::allowedIfHasPermission($account, "manage {$variation->bundle()} commerce_product_variation"));
}

return parent::checkFieldAccess($operation, $field_definition, $account, $items);

Les champs restreints incluent désormais :

  • Variations : product_id.
  • Coupons : promotion_id.
  • Promotions : coupons.
  • Boutiques : is_default.
  • Commandes et lignes de commande : les champs qui portent la propriété, les montants et l'état, comme uid, store_id, state, order_items, total_price, adjustments, unit_price, title et purchased_entity, plus coupons, cart, checkout_flow, checkout_step, payment_gateway et payment_method sur les commandes.
  • Moyens de paiement : les champs de passerelle, de propriétaire, d'identifiant distant et de carte.

Leur modification exige la permission d'administration, une permission de mise à jour globale ou par type de commande là où le gestionnaire l'accepte, ou, pour les nouvelles variations, la permission de gestion que le formulaire d'ajout utilise lui-même.

Le même diff ferme les chemins de suppression que ces références ouvraient. Promotion::postDelete() ne supprime plus que les coupons dont la promotion correspond à l'entité supprimée, avec le commentaire « Coupons belonging to other promotions might be referenced (e.g. through JSON:API), those must not be deleted ». L'équivalent produit ne supprime que les propres variations du produit.

Des routes manquaient aussi de contrôle de permission : chaque route d'administration de commande sous /admin/ qui ne vérifiait que l'accès à la commande, dont entity.commerce_order.manage_coupons_form, la route de mode de formulaire de commande et les routes des lignes de commande, exige désormais « access commerce administration pages », car les clients ont l'accès en mise à jour à leur propre panier. Et les coupons n'avaient aucun gestionnaire de contrôle d'accès sur les requêtes : la route d'autocomplétion d'entité listait donc tous les codes de coupon pour tout compte qui pouvait l'atteindre ; un nouveau plugin de sélection réserve cette liste aux comptes ayant « administer commerce_promotion » ou « update any commerce_promotion ».

Comment la faille est-elle exploitée ?

Aucun code d'exploit n'est public. Les tests fonctionnels du correctif documentent les requêtes exactes qui fonctionnaient avant et échouent après :

  1. Un utilisateur autorisé à créer ses propres produits envoie un POST JSON:API qui crée un produit référençant la variation d'un autre produit. Avant le correctif, la variation passait sous la propriété du nouveau produit ; après, la requête échoue en 422 avec le message « belongs to another product ».
  2. Un propriétaire de promotion envoie un PATCH JSON:API qui déplace l'un de ses coupons vers une autre promotion en modifiant sa relation promotion_id. Avant le correctif, le coupon se déplaçait ; après, la requête renvoie 403.
  3. Tout compte pouvant atteindre la route d'autocomplétion d'entité recevait des codes de coupon dans les résultats, y compris ceux de promotions qu'il ne gérait pas.
  4. Les requêtes de collection JSON:API trouvaient des variations que le compte ne pouvait pas consulter, dont des variations de produits non publiés, et les listaient comme omises, révélant qu'un filtre les avait trouvées.

L'exploitation active est l'affirmation de Drupal. Aucune publication publique ne nomme les attaquants, les requêtes envoyées ou les sites touchés, et l'avis ne donne aucun indicateur.

Quel est le flux de l'attaque ?

En une phrase : un compte à permissions limitées modifie un champ de référence via JSON:API ou REST, rattache la variation d'un autre vendeur ou le coupon d'une autre promotion à une entité qu'il contrôle, et la plateforme déplace la propriété avec.

flowchart LR A["Compte à permissions
commerce limitées"] --> B["Requête d'écriture
JSON:API ou REST"] B --> C["Modification d'un champ de référence :
variations, coupons,
promotion_id"] C --> D["Aucun contrôle au niveau du champ :
requête acceptée"] D --> E["La variation d'un autre vendeur
ou un autre coupon est lié
à l'entité de l'attaquant"] E --> F["Impact financier
pour le vendeur"] G["Route d'autocomplétion :
codes de coupon listés
à tout appelant"] --> D

Que peut faire un attaquant ?

Avec un compte doté de permissions commerce limitées, un attaquant peut déplacer les variations d'un autre vendeur vers une entité qu'il contrôle, déplacer des coupons entre promotions et lister des codes de coupon qui ne lui étaient pas destinés. Avant le correctif, supprimer une promotion forgée supprimait aussi des coupons appartenant à d'autres promotions. Drupal résume l'impact en un contournement de contrôle d'accès « qui peut avoir un impact financier sur le vendeur ».

Les conditions : un client avec un panier, pour altérer les champs de commande de ce panier, ou un compte avec une permission commerce pour les prises de contrôle entre vendeurs, plus un accès API. Un compte sans droit de consultation pouvait aussi apprendre l'existence de variations cachées, selon le correctif sur les requêtes.

La déclaration d'exploitation de l'avis est la seule preuve publique d'abus. Elle ne nomme rien au-delà d'« activement exploitée dans la nature », donc traitez tout site en version affectée avec des comptes multi-vendeurs ou multi-rôles comme exposé depuis une date inconnue, et auditez comme demandé.

Pourquoi le correctif fonctionne-t-il ?

Le correctif ajoute la couche manquante : un contrôle d'accès au niveau des champs, exactement sur ceux qui franchissent les frontières de propriété, appliqué par des gestionnaires qui s'exécutent avant l'écriture de la référence. Le schéma est le même pour chaque entité : le type d'entité déclare ses champs restreints, et checkFieldAccess() refuse la modification si le compte ne détient ni la permission d'administration ni, selon le type d'entité, une permission de mise à jour globale ou par type de commande.

Trois changements annexes ferment les chemins restants :

  • CouponSelection::getReferenceableEntities() renvoie une liste vide pour les comptes qui ne peuvent pas administrer les promotions, donc la route d'autocomplétion ne fuit plus de codes. L'utilisation d'un coupon reste possible, car la validation est un chemin distinct de la liste.
  • Promotion::postDelete() et son équivalent produit vérifient la propriété avant de supprimer les entités référencées.
  • Les routes d'administration de commande, dont manage_coupons_form, gagnent leur exigence de permission.

Le correctif est complet pour les chemins couverts par ses tests : prise de contrôle de variation, déplacement de coupon, liste des coupons, modifications par les clients des champs restreints de panier, de ligne de commande et de moyen de paiement, effets de suppression et routes d'administration.

Les modules contribués qui ajoutent leurs propres champs de référence doivent les verrouiller eux-mêmes. Le mécanisme restricted_fields est extensible : les modules y inscrivent leurs champs via hook_entity_type_build(), le hook que le correctif utilise lui-même pour le champ coupons des commandes. Vérifiez les modules de votre site.

Comment vérifier si j'ai été compromis ?

L'avis donne une seule consigne : auditez vos commandes à la recherche d'incohérences. Aucun indicateur, motif de requête ou détail d'acteur n'est publié, donc l'audit part du modèle de données :

  • Listez les variations dont le product_id a changé vers un produit détenu par un compte différent de celui qui a créé la variation. Les bascules de propriété sont la signature de la prise de contrôle.
  • Listez les coupons dont le promotion_id diffère de la promotion qui les a émis, et les promotions dont les listes de coupons ont gagné des coupons jamais attribués.
  • Cherchez les utilisations de coupons qui précèdent ou contournent la promotion sous laquelle elles ont été faites, et les commandes dont les coupons appliqués appartiennent à des promotions sans rapport.
  • Passez en revue les journaux d'accès pour les écritures JSON:API et REST sur les entités commerce depuis des comptes à permissions limitées, et pour les requêtes d'autocomplétion d'entité ayant renvoyé des coupons.
  • Vérifiez qu'aucune suppression de promotion n'a retiré des coupons appartenant encore à d'autres promotions.

Les journaux d'accès comptent plus que le code : une tentative échouée laisse un 403 ou 422 dans le journal API dès qu'une requête a atteint le champ, et une prise de contrôle réussie sur une version affectée laisse un 2xx (200, ou 201 pour une création JSON:API).

Chronologie

  • 2024-08-22 : Commerce Core 8.x-2.40 sort, dernière version de la branche 2.x avant ce correctif.
  • 2026-10-28 : Commerce Core 3.3.13 et 8.x-2.41 sont étiquetés avec le correctif de contrôle d'accès.
  • 2026-10-28 : Drupal publie SA-CONTRIB-2026-254, notée Highly critical 21/25, indiquant que la faille est activement exploitée dans la nature.
  • 2026-10-29 : Le catalogue KEV de la CISA ne liste toujours rien pour cette faille, vérifié dans le flux.
  • 2026-10-30 : L'enregistrement CVE-2026-118539 est publié par le CNA Drupal, sans score CVSS, décrivant une autorisation manquante permettant le parcours forcé. The Hacker News couvre l'avis le même jour.

Un écart de deux ans dans la branche 2.x, puis un correctif et un avis le même jour : la chronologie publique de CVE-2026-118539.
Figure 1 : La chronologie publique de CVE-2026-118539, de la version 2.x précédente au correctif, l'avis et l'enregistrement CVE. Sources : les étiquettes Git de Commerce, l'avis Drupal et l'enregistrement CVE, consultés le 30 octobre 2026

FAQ

Un score CVSS est-il disponible ?

Non. L'enregistrement CVE publié le 2026-10-30 ne porte aucune métrique, et l'avis utilise l'échelle de risque propre à Drupal : Highly critical, 21/25. Son vecteur suit la notation de Drupal, pas CVSS : AC:None/A:None/CI:Some/II:Some/E:Exploit/TD:All. La composante E:Exploit reflète la déclaration d'exploitation dans la nature.

Des visiteurs anonymes peuvent-ils s'approprier des produits ?

Non. Prendre le contrôle de produits ou de variations exige un compte avec des permissions commerce limitées. Le correctif empêche toutefois les clients, qui n'ont besoin d'aucune permission commerce, de modifier via JSON:API ou REST des champs restreints de leur propre panier, comme son état et ses totaux, et les variations cachées n'apparaissent plus comme entrées omises dans les collections JSON:API. L'avis cadre la faille pour les vendeurs, et sa consigne d'audit vise les données de commande.

Mon site n'a que des administrateurs. Suis-je exposé ?

Pas aux prises de contrôle entre vendeurs, qui exigent un compte détenant une permission commerce sans la permission d'administration. Les clients restent un risque : tout client avec un panier pouvait modifier via JSON:API ou REST des champs restreints de ce panier, comme son état, ses totaux et les prix de ses lignes. Corrigez maintenant et auditez les commandes comme l'avis le demande.

Le correctif change-t-il le fonctionnement de ma boutique ?

Cela peut, à un endroit : les utilisateurs qui géraient des coupons sur des commandes sans la permission d'administration saisissent désormais un code au lieu de le choisir dans une autocomplétion, car cette liste fuyait les codes. L'utilisation des coupons est inchangée.

Qu'est-ce que la branche 2.x a manqué ?

Tout depuis août 2024, et ce correctif y applique la même couche de contrôle d'accès. Le tag 2.41 porte les restrictions de champs pour les anciens chemins de code : la mise à jour reste le même déplacement en une étape.

Dois-je désactiver JSON:API le temps de corriger ?

Cela réduit la surface de prise de contrôle, et le mode lecture seule empêche toute écriture, mais cela ne remplace pas la mise à jour : la route d'autocomplétion et les effets de suppression n'avaient pas besoin de JSON:API. Corrigez d'abord, puis réactivez la surface d'API dont vos consommateurs ont besoin.

Inventoriez vos sites Drupal avec Ostorlab

La découverte de surface d'attaque d'Ostorlab cartographie vos sites exposés sur Internet et leurs plateformes, pour trouver chaque propriété Drupal publique et vérifier les versions de ses modules avant un attaquant.

Références

  • Avis de sécurité Drupal SA-CONTRIB-2026-254, Commerce Core, Highly critical, publié le 2026-10-28 : https://www.drupal.org/sa-contrib-2026-254 (consulté le 30 octobre 2026)
  • Enregistrement CVE-2026-118539, CNA Drupal, publié le 2026-10-30, autorisation manquante permettant le parcours forcé : https://cveawg.mitre.org/api/cve/CVE-2026-118539 (consulté le 30 octobre 2026)
  • Comparaison Commerce Core 3.3.12 à 3.3.13, le correctif de contrôle d'accès, ses gestionnaires et ses tests : https://git.drupalcode.org/project/commerce/-/compare/3.3.12...3.3.13 (consulté le 30 octobre 2026)
  • Étiquette Commerce Core 8.x-2.41, créée le 2026-10-28 : https://git.drupalcode.org/project/commerce/-/tags/8.x-2.41 (consulté le 30 octobre 2026)
  • Étiquette Commerce Core 8.x-2.40, créée le 2024-08-22, version 2.x précédente : https://git.drupalcode.org/project/commerce/-/tags/8.x-2.40 (consulté le 30 octobre 2026)
  • The Hacker News, « Hackers Actively Exploit Highly Critical Drupal Commerce Core Authorization Bypass Flaw », 30 octobre 2026 : https://thehackernews.com/2026/10/hackers-actively-exploit-highly.html (consulté le 30 octobre 2026)
  • Flux JSON du catalogue KEV de la CISA, vérifié pour CVE-2026-118539 le 30 octobre 2026 (absent) : https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (consulté le 30 octobre 2026)