Omisión de control de acceso en Drupal Commerce, explotada en la naturaleza (CVE-2026-118539)
Drupal Commerce antes de 2.41 y 3.3.13 deja que usuarios limitados tomen variaciones y cupones de otros vendedores vía JSON:API; explotada.
En resumen
- Drupal Commerce Core no protege los campos de entidad que vinculan productos, variaciones, cupones y promociones más allá de las fronteras de propiedad. Un usuario con permisos limitados puede tomar las variaciones de otro vendedor o enlazar cupones de otras promociones a través de JSON:API o REST. Los clientes también podían cambiar campos restringidos de su propio carrito, como su estado y sus totales.
- Drupal califica la falla como Highly critical, 21 de 25 en su propia escala, y afirma que se está explotando activamente en la naturaleza. El aviso no da actores, escala ni indicadores. CVE-2026-118539 no lleva puntuación CVSS al 2026-10-30.
- Toda versión de Commerce Core anterior a 2.41 y toda versión de 3.0.0 a 3.3.12 está afectada. La rama 2.x no había tenido versión desde agosto de 2024 antes de esta corrección.
- Actualice a Commerce Core 3.3.13, o 2.41 en la rama antigua, y audite los pedidos existentes en busca de inconsistencias, como ordena el aviso. Vea "¿Cómo compruebo si estuve comprometido?" para los ángulos de auditoría que el material público permite.
¿Estoy afectado?
Está expuesto cuando se cumplen todas estas condiciones:
- El módulo Commerce Core está instalado en una versión anterior a 2.41, o entre 3.0.0 y 3.3.12 inclusive.
- Hay cuentas, aparte de los administradores completos de la tienda, que pueden escribir en entidades de comercio: clientes que actualizan su propio carrito, o cuentas con permisos de comercio como actualizar sus propios productos, gestionar variaciones o actualizar sus propias promociones. La falla es una omisión de control de acceso para esas cuentas.
- Existe acceso por JSON:API o REST, que las pruebas de la corrección usan para cada ruta de apropiación. JSON:API viene con el núcleo de Drupal.
Los clientes no necesitan ningún permiso de comercio: antes de la corrección, un cliente con acceso de actualización a su propio carrito podía cambiar por JSON:API o REST campos restringidos del pedido y de sus líneas, como state, total_price, adjustments, unit_price y title, y alcanzar rutas de administración de ese pedido. Las rutas de apropiación entre vendedores necesitan una cuenta con permisos de comercio limitados. Una cuenta que no podía ver ciertas variaciones, como las de productos no publicados, también podía deducir de los filtros de colecciones de JSON:API que existían.
| Rama | Versiones afectadas | Versión corregida |
|---|---|---|
| Commerce Core 3.x | 3.0.0 a 3.3.12 | 3.3.13 (2026-10-28) |
| Commerce Core 2.x y anteriores | Todas las versiones antes de 2.41 | 8.x-2.41 (2026-10-28) |
Ambas correcciones se publicaron el mismo día. La versión anterior de la rama 2.x, 8.x-2.40, data del 2024-08-22, así que los sitios que la usaban llevaban más de dos años sin actualizar.
Compruebe lo que ejecuta su sitio:
# 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
¿Cómo lo corrijo?
- Con Composer, según el flujo habitual de Drupal:
composer require 'drupal/commerce:^3.3.13' --with-all-dependencies, ocomposer require 'drupal/commerce:^2.41' --with-all-dependenciesen la rama 2.x. - Ejecute la rutina de actualización que su sitio use tras los cambios de código y limpie la caché:
drush cr. - Vuelva a aplicar cualquier parche propio sobre el módulo, porque ambas ramas se movieron.
- Audite los pedidos existentes, como ordena el aviso, con las comprobaciones de abajo.
No existe ninguna solución de configuración. El aviso no nombra ninguna, y las pruebas de la corrección muestran que las rutas de omisión solo se cierran con los nuevos controles a nivel de campo.
¿Cuál es la causa raíz?
Las entidades de Commerce tenían control de acceso a nivel de entidad pero ninguno a nivel de campo, de modo que los campos de referencia que vinculan las entidades de un vendedor con las de otro podían editarlos cualquier cuenta autorizada a tocar sus propias entidades.
Commerce Core vende bienes y servicios mediante productos, variaciones, promociones y cupones, cada uno con sus permisos. Una cuenta con "update own commerce_product" puede editar sus productos. El diff de 3.3.13 muestra que nada impedía que esa cuenta editara también los campos de referencia que cruzan la propiedad: la lista de variaciones de un producto, la promoción a la que pertenece un cupón, los cupones que referencia una promoción y el indicador de tienda que cambia el comportamiento de todo el sitio.
La corrección introduce una propiedad restricted_fields en cada tipo de entidad y la comprueba en nuevos gestores de control de acceso. Este es el gestor de variaciones, tomado del diff de 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);
Los campos restringidos incluyen ahora:
- Variaciones:
product_id. - Cupones:
promotion_id. - Promociones:
coupons. - Tiendas:
is_default. - Pedidos y líneas de pedido: los campos que llevan la propiedad, los importes y el estado, como
uid,store_id,state,order_items,total_price,adjustments,unit_price,titleypurchased_entity, además decoupons,cart,checkout_flow,checkout_step,payment_gatewayypayment_methoden pedidos. - Métodos de pago: los campos de pasarela, propietario, ID remoto y tarjeta.
Editarlos exige el permiso de administración, un permiso de actualización global o por tipo de pedido donde el gestor lo admite, o, para variaciones nuevas, el permiso de gestión que el propio formulario de alta usa.
El mismo diff cierra las rutas de borrado que las referencias abrían. Promotion::postDelete() ahora borra solo los cupones cuya promoción coincide con la entidad borrada, con el comentario "Coupons belonging to other promotions might be referenced (e.g. through JSON:API), those must not be deleted". El equivalente de producto borra solo las variaciones propias del producto.
Varias rutas carecían además de comprobación de permiso: toda ruta de administración de pedidos bajo /admin/ que solo comprobaba el acceso al pedido, incluidas entity.commerce_order.manage_coupons_form, la ruta de modo de formulario del pedido y las rutas de líneas de pedido, exige ahora "access commerce administration pages", porque los clientes tienen acceso de actualización a su propio carrito. Y los cupones no tenían ningún gestor de control de acceso sobre consultas, así que la ruta de autocompletado de entidad listaba todos los códigos de cupón para cualquier cuenta que pudiera alcanzarla; un nuevo complemento de selección restringe ese listado a las cuentas con "administer commerce_promotion" o "update any commerce_promotion".
¿Cómo se explota?
No hay código de exploit público. Las pruebas funcionales de la corrección documentan las peticiones exactas que funcionaban antes y fallan después:
- Un usuario con derecho a crear sus propios productos envía un POST de JSON:API que crea un producto referenciando la variación de otro producto. Antes de la corrección, la variación pasaba a ser propiedad del nuevo producto; después, la petición falla con 422 y el mensaje "belongs to another product".
- Un dueño de promoción envía un PATCH de JSON:API que mueve uno de sus cupones a otra promoción cambiando su relación
promotion_id. Antes de la corrección, el cupón se movía; después, la petición devuelve 403. - Cualquier cuenta que pudiera alcanzar la ruta de autocompletado de entidad recibía códigos de cupón en los resultados, incluidos los de promociones que no gestionaba.
- Las consultas de colecciones de JSON:API encontraban variaciones que la cuenta no podía ver, incluidas variaciones de productos no publicados, y las listaban como omitidas, revelando que un filtro las había encontrado.
Que la falla se explote activamente es la afirmación de Drupal. Ninguna publicación pública nombra a los atacantes, las peticiones enviadas ni los sitios afectados, y el aviso no da indicadores.
¿Cómo fluye el ataque?
En una frase: una cuenta con permisos limitados edita un campo de referencia a través de JSON:API o REST, adjunta la variación de otro vendedor o el cupón de otra promoción a una entidad que controla, y la plataforma mueve la propiedad con ello.
de comercio limitados"] --> B["Petición de escritura
JSON:API o REST"] B --> C["Edición de campo de referencia:
variaciones, cupones,
promotion_id"] C --> D["Sin control a nivel de campo:
petición aceptada"] D --> E["La variación de otro vendedor
o un cupón ajeno queda enlazado
a la entidad del atacante"] E --> F["Impacto financiero
para el vendedor"] G["Ruta de autocompletado:
códigos de cupón listados
a cualquier llamador"] --> D
¿Qué puede hacer un atacante?
Con una cuenta que tenga permisos de comercio limitados, un atacante puede mover las variaciones de otro vendedor a una entidad que controla, mover cupones entre promociones y listar códigos de cupón que nunca le estuvieron destinados. Antes de la corrección, borrar una promoción elaborada también eliminaba cupones que pertenecían a otras promociones. Drupal resume el impacto como una omisión de control de acceso "que puede afectar financieramente al vendedor".
Las condiciones: un cliente con carrito, para alterar los campos de pedido de ese carrito, o una cuenta con algún permiso de comercio para las apropiaciones entre vendedores, más acceso API. Una cuenta sin permiso de visualización también podía saber que existían variaciones ocultas, según la corrección de consultas.
La afirmación de explotación del aviso es la única evidencia pública de abuso. No nombra nada más allá de "explotada activamente en la naturaleza", así que trate cualquier sitio con versiones afectadas y cuentas de varios vendedores o varios roles como expuesto desde una fecha desconocida, y audite como se ordena.
¿Por qué funciona la corrección?
La corrección añade la capa que faltaba: control de acceso a nivel de campo, exactamente sobre los campos que cruzan las fronteras de propiedad, aplicado por gestores que se ejecutan antes de escribir la referencia. El patrón es el mismo para cada entidad: el tipo de entidad declara sus campos restringidos y checkFieldAccess() deniega la edición salvo que la cuenta tenga el permiso de administración o, según el tipo de entidad, un permiso de actualización global o por tipo de pedido.
Tres cambios complementarios cierran las rutas restantes:
CouponSelection::getReferenceableEntities()devuelve una lista vacía para las cuentas que no pueden administrar promociones, así la ruta de autocompletado ya no filtra códigos. Canjear un cupón sigue funcionando, porque validar es una ruta distinta de listar.Promotion::postDelete()y su equivalente de producto comprueban la propiedad antes de borrar entidades referenciadas.- Las rutas de administración de pedidos, entre ellas
manage_coupons_form, ganan su requisito de permiso.
La corrección es completa para las rutas que cubren sus pruebas: apropiación de variaciones, movimiento de cupones, listado de cupones, ediciones de clientes en campos restringidos de carritos, líneas de pedido y métodos de pago, efectos de borrado y las rutas de administración.
Los módulos contribuidos que añaden sus propios campos de referencia deben protegerlos ellos mismos. El mecanismo restricted_fields es extensible: los módulos registran sus campos mediante hook_entity_type_build(), el hook que la propia corrección usa para el campo de cupones de los pedidos. Revise los módulos de su sitio.
¿Cómo compruebo si estuve comprometido?
El aviso da una sola instrucción: audite sus pedidos en busca de inconsistencias. No hay indicadores, patrones de petición ni detalles de actores publicados, así que la auditoría parte del modelo de datos:
- Liste las variaciones cuyo
product_idcambió a un producto propiedad de una cuenta distinta de la que creó la variación. Los cambios de dueño son la firma de la apropiación. - Liste los cupones cuyo
promotion_iddifiere de la promoción que los emitió, y las promociones cuyas listas de cupones ganaron cupones nunca asignados. - Busque canjes de cupones que precedan o evadan la promoción bajo la que se hicieron, y pedidos cuyos cupones aplicados pertenezcan a promociones sin relación.
- Revise los registros de acceso de escrituras JSON:API y REST sobre entidades de comercio desde cuentas con permisos limitados, y de peticiones de autocompletado de entidad que devolvieran cupones.
- Confirme que ningún borrado de promoción eliminó cupones que aún pertenecían a otras promociones.
Los registros de acceso importan más que el código: un intento fallido deja un 403 o 422 en el registro de la API en cuanto una petición alcanzó el campo, y una apropiación exitosa en una versión afectada deja un 2xx (200, o 201 en una creación por JSON:API).
Cronología
- 2024-08-22: Se publica Commerce Core 8.x-2.40, última versión de la rama 2.x antes de esta corrección.
- 2026-10-28: Se etiquetan Commerce Core 3.3.13 y 8.x-2.41 con la corrección de control de acceso.
- 2026-10-28: Drupal publica SA-CONTRIB-2026-254, calificada Highly critical 21/25, afirmando que la falla se explota activamente en la naturaleza.
- 2026-10-29: El catálogo KEV de CISA sigue sin listar nada para esta falla, comprobado en el feed.
- 2026-10-30: El registro CVE-2026-118539 es publicado por el CNA de Drupal, sin puntuación CVSS, describiendo una autorización faltante que permite la navegación forzada. The Hacker News cubre el aviso el mismo día.
Preguntas frecuentes
¿Hay una puntuación CVSS disponible?
No. El registro CVE publicado el 2026-10-30 no lleva métricas, y el aviso usa la escala de riesgo propia de Drupal: Highly critical, 21/25. Su vector usa la notación de Drupal, no CVSS: AC:None/A:None/CI:Some/II:Some/E:Exploit/TD:All. El componente E:Exploit refleja la afirmación de explotación en la naturaleza.
¿Pueden los visitantes anónimos apropiarse de productos?
No. Apropiarse de productos o variaciones exige una cuenta con permisos de comercio limitados. Aun así, la corrección impide que los clientes, que no necesitan ningún permiso de comercio, cambien por JSON:API o REST campos restringidos de su propio carrito, como su estado y sus totales, y las variaciones ocultas ya no aparecen como entradas omitidas en las colecciones de JSON:API. El aviso enmarca la falla para los vendedores, y su instrucción de auditoría apunta a los datos de pedidos.
Mi sitio solo tiene administradores. ¿Estoy expuesto?
No a las apropiaciones entre vendedores, que exigen una cuenta con algún permiso de comercio pero sin el de administración. Los clientes siguen siendo un riesgo: cualquier cliente con carrito podía cambiar por JSON:API o REST campos restringidos de ese carrito, como su estado, sus totales y los precios de sus líneas. Corrija ahora y audite los pedidos como indica el aviso.
¿La corrección cambia cómo funciona mi tienda?
Puede, en un punto: los usuarios que gestionaban cupones en pedidos sin el permiso de administración ahora teclean un código de cupón en lugar de elegirlo de un autocompletado, porque ese listado filtraba códigos. El canje de cupones en sí no cambia.
¿Qué se perdió la rama 2.x?
Todo desde agosto de 2024, y esta corrección aplica allí la misma capa de control de acceso. El tag 2.41 lleva las restricciones de campos para las rutas de código antiguas, así que la actualización sigue siendo el mismo movimiento de un paso.
¿Debo desactivar JSON:API mientras corrijo?
Reduce la superficie de apropiación, y el modo de solo lectura impide toda escritura, pero no sustituye a la actualización: la ruta de autocompletado y los efectos de borrado no necesitaban JSON:API. Corrija primero y luego reactive la superficie de API que sus consumidores necesiten.
Inventaríe sus sitios Drupal con Ostorlab
El descubrimiento de superficie de ataque de Ostorlab mapea sus sitios expuestos a Internet y sus plataformas, para que encuentre cada propiedad Drupal pública y compruebe las versiones de sus módulos antes que un atacante.
Referencias
- Aviso de seguridad de Drupal SA-CONTRIB-2026-254, Commerce Core, Highly critical, publicado el 2026-10-28: https://www.drupal.org/sa-contrib-2026-254 (consultado el 30 de octubre de 2026)
- Registro CVE-2026-118539, CNA de Drupal, publicado el 2026-10-30, autorización faltante que permite navegación forzada: https://cveawg.mitre.org/api/cve/CVE-2026-118539 (consultado el 30 de octubre de 2026)
- Comparación de Commerce Core 3.3.12 a 3.3.13, la corrección de control de acceso, sus gestores y pruebas: https://git.drupalcode.org/project/commerce/-/compare/3.3.12...3.3.13 (consultado el 30 de octubre de 2026)
- Etiqueta Commerce Core 8.x-2.41, creada el 2026-10-28: https://git.drupalcode.org/project/commerce/-/tags/8.x-2.41 (consultado el 30 de octubre de 2026)
- Etiqueta Commerce Core 8.x-2.40, creada el 2024-08-22, la versión 2.x anterior: https://git.drupalcode.org/project/commerce/-/tags/8.x-2.40 (consultado el 30 de octubre de 2026)
- The Hacker News, "Hackers Actively Exploit Highly Critical Drupal Commerce Core Authorization Bypass Flaw", 30 de octubre de 2026: https://thehackernews.com/2026/10/hackers-actively-exploit-highly.html (consultado el 30 de octubre de 2026)
- Feed JSON del catálogo KEV de CISA, comprobado para CVE-2026-118539 el 30 de octubre de 2026 (no listado): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (consultado el 30 de octubre de 2026)