Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-56393

CVE-2026-56393: Craft CMS Stored XSS Vulnerability

CVE-2026-56393 is a stored XSS vulnerability in Craft CMS 4.x and 5.x that allows authenticated admins to inject malicious scripts into settings names and field labels. This article covers technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-56393 Overview

CVE-2026-56393 is a stored cross-site scripting (XSS) vulnerability affecting Craft CMS versions 4.x (>= 4.0.0-RC1, < 4.17.0-beta.1) and 5.x (>= 5.0.0-RC1, < 5.9.0-beta.1). The flaw exists in multiple control-panel templates that render administrator-supplied settings and field option labels without HTML sanitization. The checkbox.twig and radio.twig templates used the Twig |raw filter, allowing arbitrary JavaScript to execute in other users' control-panel sessions. Exploitation requires authenticated administrator access with allowAdminChanges enabled. The vulnerability is tracked under [CWE-79] and was fixed in 4.17.0-beta.1 and 5.9.0-beta.1.

Critical Impact

An authenticated administrator can inject persistent JavaScript payloads into section, volume, user group, global set, field, and option labels, executing in any administrator's browser session.

Affected Products

  • Craft CMS 4.x (>= 4.0.0-RC1, < 4.17.0-beta.1)
  • Craft CMS 5.x (>= 5.0.0-RC1, < 5.9.0-beta.1)
  • Craft CMS control panel components rendering label content via {{ label|raw }}

Discovery Timeline

  • 2026-06-21 - CVE-2026-56393 published to NVD
  • 2026-06-22 - Last updated in NVD database

Technical Details for CVE-2026-56393

Vulnerability Analysis

The vulnerability stems from unsafe rendering of user-controlled label content across the Craft CMS control panel. Twig templates including src/templates/_includes/forms/checkbox.twig and src/templates/_includes/forms/radio.twig used the |raw filter, which bypasses Twig's default HTML escaping. An administrator with allowAdminChanges enabled can save arbitrary HTML and JavaScript into settings names, volume names, user group names, global set names, generated field names, checkbox and radio option labels, and custom source labels. The stored payload executes whenever another administrator loads a page rendering that label.

Root Cause

The root cause is improper neutralization of input during web page generation [CWE-79]. Templates rendered label values with {{ label|raw }}, instructing Twig to emit the value without escaping HTML special characters. Additionally, src/fields/BaseOptionsField.php concatenated option labels for display without calling Html::encode(), and src/web/assets/cp/src/js/BaseElementIndex.js removed an escapeHtml() call on option labels in element index sources.

Attack Vector

An attacker with administrator privileges navigates to a settings page that accepts a name or label field, then submits a payload such as <script>...</script> within the value. The payload is persisted to the database. When any control-panel user views a page that renders that label, the script executes in their authenticated session, enabling session theft, control-panel actions on their behalf, or further account takeover.

php
// Patch in src/fields/BaseOptionsField.php
            $labels[] = array_pop($options)['label'];
        }

+       $labels = array_map(fn($label) => Html::encode($label), $labels);
+
        return implode(', ', $labels);
    }

Source: Craft CMS commit 67780a7

twig
{# Patch in src/templates/_includes/forms/checkbox.twig #}
                        <div class="color-preview" style="background-color: {{ color }}"></div>
                    </div>
                {% endif %}
-               <span>{{ label|raw }}</span>
+               <span>{{ label }}</span>
            </div>
        {% else %}
-           {{ label|raw }}
+           {{ label }}
        {% endif %}

Source: Craft CMS commit 67780a7

twig
{# Patch in src/templates/_includes/forms/radio.twig #}
{{ tag('input', inputAttributes) }}

{% tag 'label' with containerAttributes %}
-   {{ label|raw }}
+   {{ label }}
{% endtag %}

Source: Craft CMS commit 943152d

javascript
// Patch in src/web/assets/cp/src/js/BaseElementIndex.js
          return o.optgroup
            ? o
            : {
-               label: Craft.escapeHtml(o.label),
+               label: o.label,
                value: o.attr,
              };
          }),

Source: Craft CMS commit 943152d

Detection Methods for CVE-2026-56393

Indicators of Compromise

  • Section, volume, user group, or global set names containing HTML tags such as <script>, <img onerror=...>, or <svg onload=...>
  • Field option labels (checkbox, radio, dropdown) containing angle brackets or JavaScript event handlers
  • Unexpected outbound HTTP requests from administrator browsers shortly after loading control-panel pages
  • Database rows in craft_fields, craft_sections, craft_volumes, or craft_usergroups with non-printable or markup characters in name columns

Detection Strategies

  • Query the Craft CMS database for label and name fields containing <, >, script, or on[a-z]+= patterns
  • Audit administrator action logs for changes to section, volume, user group, global set, and field configuration entries
  • Review web server logs for control-panel POST requests with suspicious payloads in name or label parameters

Monitoring Recommendations

  • Monitor accounts holding administrator privileges and any change to allowAdminChanges in config/general.php
  • Alert on creation or modification of fields, sections, and option labels by non-standard administrator accounts
  • Enable a Content Security Policy (CSP) in report-only mode to surface inline script execution attempts in the control panel

How to Mitigate CVE-2026-56393

Immediate Actions Required

  • Upgrade Craft CMS to 4.17.0-beta.1 or 5.9.0-beta.1 (or later stable releases incorporating these fixes)
  • Audit existing field, section, volume, user group, and global set names for embedded HTML or script content and remediate any malicious values
  • Restrict administrator accounts and disable allowAdminChanges in production environments where it is not required
  • Rotate administrator session cookies and credentials if injected payloads are discovered

Patch Information

Craft CMS resolved the issue by replacing {{ label|raw }} with default-escaped {{ label }} in checkbox.twig and radio.twig, reintroducing Craft.escapeHtml() on element index source labels in BaseElementIndex.js, and applying Html::encode() to concatenated option labels in BaseOptionsField.php. Patch details are available in the Craft CMS GHSA-4mgv-366x-qxvx security advisory and the VulnCheck advisory on Craft CMS.

Workarounds

  • Set allowAdminChanges to false in production config/general.php to prevent further injection via settings UI
  • Limit administrator role assignments to a minimal trusted set of users until patching is complete
  • Deploy a strict Content Security Policy that disallows inline scripts for control-panel routes
bash
# Configuration example: disable admin changes in production
# config/general.php
return [
    '*' => [
        // ... other settings
    ],
    'production' => [
        'allowAdminChanges' => false,
    ],
];

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.