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

CVE-2026-75831: Grav CMS Stored XSS Vulnerability

CVE-2026-75831 is a stored cross-site scripting flaw in Grav CMS that allows attackers to inject malicious HTML and JavaScript through audio and video media rendering. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-75831 Overview

CVE-2026-75831 is a stored cross-site scripting (XSS) vulnerability in Grav CMS versions before 2.0.15. The flaw resides in the sourceParsedownElement method used for audio and video media rendering. Grav concatenates the media URL fragment directly into rawHtml source elements without escaping, letting attackers inject arbitrary HTML and JavaScript. The injected payload executes in the browser sessions of users who view the affected page. Exploitation requires low-privileged authenticated access to author content and user interaction to trigger the stored payload. The vulnerability is classified as [CWE-79] Improper Neutralization of Input During Web Page Generation.

Critical Impact

Authenticated content authors can inject persistent JavaScript that executes against every visitor rendering the affected audio or video markdown.

Affected Products

  • Grav CMS versions prior to 2.0.15
  • Grav sites that render audio media via markdown
  • Grav sites that render video media via markdown

Discovery Timeline

  • 2026-08-18 - CVE-2026-75831 published to the National Vulnerability Database (NVD)
  • 2026-08-18 - Last updated in NVD database

Technical Details for CVE-2026-75831

Vulnerability Analysis

Grav's MediaObjectTrait and related media rendering traits build HTML <source> elements for audio and video assets. The sourceParsedownElement method treats the media URL fragment as trusted output and inserts it into a rawHtml template string. Because the fragment bypasses Parsedown's normal attribute escaping, any HTML metacharacters supplied through the URL survive into the rendered DOM.

An attacker with content-authoring permissions embeds a crafted audio or video reference in a page. When any visitor renders that page, the browser parses the injected markup and executes the attacker's JavaScript in the site's origin. The impact scope covers session theft, administrative action forgery, and drive-by delivery to site visitors.

Root Cause

The root cause is missing output encoding in the media rendering path. Grav treated the URL fragment as safe HTML rather than an untrusted string requiring contextual escaping before inclusion in a rawHtml element. The related media pipeline also accepted keyed style declarations without validating them against a safe-value allowlist, an issue addressed in the same fix series.

Attack Vector

Exploitation requires network access to the Grav instance, authentication with content authoring privileges, and a victim who views the poisoned page. No elevated privileges are needed on the server. The following patch snippets from the upstream commit illustrate the hardening applied in the media pipeline.

php
            if (is_numeric($key)) { // Special case for inline style attributes, refer to style() method
                $style .= $value;
            } else {
-                $style .= $key . ': ' . $value . ';';
+                // Keyed declarations come from media actions such as resize().
+                // Validate the serialized declaration so a value can't carry
+                // extra CSS past the intended property, the same fail-closed
+                // check style() applies. GHSA-ffmg-hfvg-jhg9.
+                $declaration = $key . ': ' . $value;
+                if (self::isSafeStyleValue($declaration)) {
+                    $style .= $declaration . ';';
+                }
            }
        }
        if ($style) {

Source: GitHub Commit aba291a

php
    public function resize($width = null, $height = null)
    {
-        if ($width) {
+        // Width/height are pixel dimensions reachable from editor Markdown
+        // (`?resize=W,H`). Coerce them to integers before they enter
+        // $styleAttributes so a crafted value such as `100;position:fixed;…`
+        // can't break out of the `width:` declaration and inject extra CSS
+        // into the rendered `<img style="…">`. resize() writes keyed style
+        // values directly and so bypassed the style() sanitizer.
+        $width = is_numeric($width) ? (int) $width : 0;
+        $height = is_numeric($height) ? (int) $height : 0;
+
+        if ($width > 0) {
            $this->styleAttributes['width'] = $width . 'px';
        } else {
            unset($this->styleAttributes['width']);
        }
-        if ($height) {
+        if ($height > 0) {
            $this->styleAttributes['height'] = $height . 'px';
        } else {
            unset($this->styleAttributes['height']);

Source: GitHub Commit aba291a

Detection Methods for CVE-2026-75831

Indicators of Compromise

  • Markdown pages referencing audio or video media where the URL fragment contains HTML metacharacters such as <, >, ", or onerror=.
  • Rendered pages that contain <source> tags with unexpected attributes or inline event handlers.
  • Modifications to content files under the Grav user/pages/ directory by low-privileged accounts prior to upgrading to 2.0.15.

Detection Strategies

  • Scan the Grav content tree for audio and video markdown entries containing script tags, javascript: URIs, or attribute-breaking characters in the URL segment.
  • Diff production content against a known-good baseline to surface unauthorized edits by non-administrative editors.
  • Inspect web server access logs for POST activity to Grav's admin editor endpoints followed by anomalous visitor sessions on the same page URL.

Monitoring Recommendations

  • Enable Content Security Policy (CSP) reporting to capture blocked inline scripts originating from rendered Grav pages.
  • Monitor authenticated editor accounts for out-of-pattern content changes, especially involving media shortcodes.
  • Alert on outbound requests from visitor browsers to unfamiliar domains sourced from Grav-hosted pages, which can indicate exfiltration via stored XSS.

How to Mitigate CVE-2026-75831

Immediate Actions Required

  • Upgrade Grav to version 2.0.15 or later on all environments hosting user-generated or editor-authored content.
  • Audit accounts with content authoring roles and revoke access for any that are no longer required.
  • Review recently modified pages containing audio or video media references and remove any suspicious markup.

Patch Information

The fix is delivered in Grav 2.0.15. The upstream remediation is tracked in GitHub Security Advisory GHSA-6qw9-4vv5-jr97 and the corresponding GitHub Commit aba291a. Additional context is available in the VulnCheck Advisory.

Workarounds

  • Restrict content authoring roles to trusted users until the patch can be applied across all instances.
  • Deploy a strict Content Security Policy that disallows inline scripts and untrusted script sources to blunt payload execution.
  • Temporarily disable audio and video media rendering in templates for sites that cannot upgrade immediately.
bash
# Upgrade Grav via the built-in CLI
bin/gpm selfupgrade
bin/gpm update

# Verify the installed version is 2.0.15 or later
bin/grav --version

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.