Grav CMS CVE-2026-42842
MEDIUMCVSS VectorNVD
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Lifecycle Timeline
2DescriptionNVD
Summary
A Stored Cross-Site Scripting (XSS) vulnerability exists in the Grav CMS Form plugin's select field template. Taxonomy tag and category values are rendered with the Twig |raw filter in the admin panel, bypassing the global autoescape protection. An editor-level user can inject arbitrary JavaScript that executes in any administrator's browser session when they view or edit any page in the admin panel.
Additionally, Grav's built-in XSS detection (Security::detectXss()) can be bypassed by using payloads that close the <option>/<select> context and use unquoted event handlers - the on_events regex fails to match event handlers without quotes or trailing spaces before >.
Important
- The vulnerability is in the Form plugin (
select.html.twig), which is installed by default with Grav - The XSS is cross-page: a malicious taxonomy value on one page executes when an admin edits any page, because taxonomy options are rendered from a shared global pool
- An editor can exploit this without any other vulnerability - taxonomy fields are not in the server-side restricted fields list
- The
HttpOnlyflag on session cookies prevents direct session theft, but the XSS can steal the admin nonce and perform privileged actions via JavaScript
Permissions Needed
- Editor: can create or edit pages and set taxonomy tag/category values
Details
The Form plugin's select field template renders option values using the |raw Twig filter, which outputs content without HTML escaping:
File: user/plugins/form/templates/forms/fields/select/select.html.twig
{
# Line 55 #}
avalue|raw
{
# Line 65 #}
suboption|t|raw
{
# Line 72 #}
item_value|t|rawThe taxonomy field in the page editor uses this select template. When a page has taxonomy values (tags, categories), these values are populated as <option> elements in the select dropdown. The value attribute is properly escaped by the browser's attribute encoding, but the display text between <option> tags is rendered raw:
<option value="<script>alert(1)</script>"><script>alert(1)</script></option>Since taxonomy options are collected globally across all pages (to provide autocomplete/selection), a malicious taxonomy value on any page will appear in the taxonomy dropdown of every page editor - making this a cross-page stored XSS.
The server-side field restriction in the flex-objects plugin only blocks ['form', 'forms', 'process', 'twig'] for non-super users. Taxonomy fields are not restricted, so editors can freely set arbitrary taxonomy values.
XSS Detection Bypass
Grav's Security::detectXss() checks for dangerous_tags (e.g., <script>, <iframe>), on_events (event handlers), and invalid_protocols (e.g., javascript:). However, the on_events regex:
'on_events' => '#(<[^>]+[a-z\x00-\x20"\'\/)(?:on[a-z]+)\s*=[\s|\'"'].*[\s|\'"']>#iUu'requires either quotes around the handler value or a trailing space before >. An unquoted handler like onerror=alert(1)> (no space before >) bypasses this check entirely.
Combined with </option></select> to break out of the select context (neither tag is in dangerous_tags), the full payload evades all three detection layers and triggers no XSS warning in the admin panel.
PoC
#### Step 1: Login as Editor Navigate to http://TARGET/admin/ and authenticate with editor credentials. #### Step 2: Create a Page with Malicious Taxonomy
- Go to Pages → Add → Add Page
- Title:
XSS via editor - Go to Options Tap
- On Taxonomies, Add tag:
</option></select><img src=x onerror=alert('XSS-via-editor')>This payload:
- Closes
</option></select>to break out of the select dropdown context - Injects an
<img>tag with an unquotedonerrorhandler (bypasseson_eventsregex) - Is not in the
dangerous_tagslist (no<script>,<iframe>, etc.) - Triggers no XSS warning in the admin panel
<img width="1221" height="857" alt="image" src="https://github.com/user-attachments/assets/6223cbb2-f04b-46bd-89ce-828c89ad77ab" /> #### Step 3: Trigger the XSS When any administrator navigates to the page editor of any page (not just the malicious one), the JavaScript executes immediately.
<img width="1224" height="856" alt="image" src="https://github.com/user-attachments/assets/f008b0f2-dedb-4b22-a74a-cdc0d7325cb4" />
The XSS fires because taxonomy tag options are collected globally across all pages and rendered with |raw in the select dropdown template. The payload breaks out of the <option> context, and the browser renders the <img> tag as a regular DOM element.
Impact
- Session hijacking: While
HttpOnlyprevents direct cookie theft, the XSS can steal the admin nonce token and perform any admin action via AJAX requests - Privilege escalation: An editor can perform admin-only actions (create users, modify system configuration, install plugins) through the hijacked admin session
- Cross-page impact: A single malicious taxonomy value affects the entire admin panel - every page editor view is compromised
---
Maintainer note - fix applied (2026-04-24)
Fixed across two repos:
- grav-plugin-form 9.0.1 (commit
6bffb4c) - the primary fix. All four|rawfilters intemplates/forms/fields/select/select.html.twig(placeholder, avalue, suboption, item_value) have been removed. Option labels - including taxonomy values that propagate cross-page through the admin's shared selection pool - now go through Twig's default escaper, so a lower-privileged editor can no longer inject script that runs in an admin's browser when they open any page editor. - Grav core on the
2.0branch (commit5a12f9be8, ships in 2.0.0-beta.2) - closes the detection-bypass half of the report. Theon_eventsregex inSecurity::detectXss()is tightened so unquoted handlers likeonerror=alert(1)>are flagged (see separate GHSA-9695-8fr9-hw5q), andoption/selecthave been added to defaultsecurity.xss_dangerous_tagsso</option></select>…tripwires the detector (see separate GHSA-w8cg-7jcj-4vv2).
Sites running admin2 on Grav 2.0.0-beta.2 get the 9.0.1 form plugin automatically via its existing dependency graph.
Files:
templates/forms/fields/select/select.html.twig- four|rawremoved.system/config/security.yaml- dangerous-tags list extended.system/src/Grav/Common/Security.php-on_eventsregex tightened.tests/unit/Grav/Common/Security/DetectXssTest.php- includes the GHSA-c2q3 PoC payload.
AnalysisAI
Stored cross-site scripting in Grav CMS Form plugin allows editor-level users to inject arbitrary JavaScript via taxonomy tag and category values that execute in administrator browsers when viewing any page in the admin panel. The vulnerability exploits unescaped Twig |raw filters in the select field template combined with a bypassable XSS detection regex, enabling privilege escalation through nonce theft and unauthorized admin actions. …
Sign in for full analysis, threat intelligence, and remediation guidance.
Share
External POC / Exploit Code
Leaving vuln.today
GHSA-c2q3-p4jr-c55f