Executive brief
Grav Admin is a popular content management system administration interface. Versions up to 2.0.19 contain a stored cross-site scripting vulnerability where an attacker can register an account with an HTML payload as their username. When administrators view UI elements displaying the username (such as two-factor authentication prompts or page-lock notices), the malicious script executes in their authenticated session, potentially allowing account compromise or administrative actions without consent.
Technical details
The vulnerability is a stored XSS in the tHtml() function (src/lib/stores/i18n.svelte.ts) which interpolates untrusted user parameters into translation templates before passing the result to marked.parseInline() for markdown parsing. The root cause is twofold: (1) Grav's server-side username validation (DataUser::isValidUsername) blocks filesystem-dangerous characters but fails to block HTML metacharacters (<, >, ", '), and (2) the tHtml() function performs string interpolation before markdown parsing, allowing HTML syntax in interpolated values to be processed as live markup. Attack requires an authenticated account creation with a malicious username payload (e.g., <img src=x onerror=alert()>), plus admin-level user interaction viewing the username through vulnerable UI surfaces. Exploitation yields XSS execution in the admin's authenticated context. Fixed in version 2.0.21.
Affected products
- Grav Grav Admin2 ≤ 2.0.19
Timeline
- 2026-08-20: disclosed
- 2026-09-04: advisory
- 2026: patched: Fixed in version 2.0.21