home intel cve-2026-42611-grav-svg-xss-rce-chain
CVE Analysis 2026-05-11 · 8 min read

CVE-2026-42611: Grav CMS SVG Injection to RCE via Admin Nonce Chain

A low-privileged Grav CMS user can inject SVG payloads into page content, escalating stored XSS to full RCE by stealing admin nonces and chaining authenticated requests.

#stored-xss#svg-injection#privilege-escalation#information-disclosure#remote-code-execution
Technical mode — for security professionals
▶ Attack flow — CVE-2026-42611 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2026-42611Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2026-42611 is a stored XSS-to-RCE chain in Grav CMS, a file-based PHP web platform. Any authenticated user with page-creation privileges — a role commonly granted to contributors and editors — can embed a raw <svg> element into page content. Grav's Markdown/HTML pipeline fails to sanitize SVG-embedded JavaScript, resulting in script execution in the browser context of any administrator who views the page.

The impact escalates beyond typical stored XSS because Grav's admin panel exposes a /admin/config/info endpoint that returns full system configuration, PHP environment details, and server internals without additional authentication checks beyond the session. The same XSS payload can exfiltrate the admin nonce — a CSRF token bound to the session — enabling the attacker to issue arbitrary authenticated admin API calls, including plugin installation and file writes, achieving Remote Code Execution. CVSS 8.9 (HIGH).

Root cause: Grav's page rendering pipeline passes raw HTML from Markdown content through the Twig templating engine without invoking a dedicated SVG/HTML sanitizer, allowing <svg onload=> and <svg><script> vectors to reach the browser DOM intact.

Affected Component

The vulnerable surface is Grav's page content renderer, specifically the interaction between the Parsedown Markdown parser (which permits inline HTML by default) and the Twig template layer that echoes page.content without escaping. All Grav versions prior to 2.0.0-beta.2 are affected.

Key files and components involved:

  • system/src/Grav/Common/Page/Medium/Medium.php — media embedding, no SVG stripping
  • system/src/Grav/Common/Twig/Extension/GravExtension.php — Twig filters, missing sanitize call on raw output
  • user/themes/*/templates/ — theme templates echo {{ page.content|raw }}
  • system/src/Grav/Common/Security.php — XSS detection logic that was bypassable via SVG namespace

Root Cause Analysis

Grav's Security::detectXss() method in Security.php scans page content for dangerous patterns before saving. The detection relied on a regex denylist that checked for common vectors like <script>, javascript:, and event handler attributes. SVG-specific vectors were not covered.

// Pseudocode reconstruction of Security::detectXss() — pre-patch
// system/src/Grav/Common/Security.php

bool Security::detectXss(string $content) {
    // BUG: denylist patterns do not cover SVG namespace vectors
    static patterns[] = {
        "/]/i",
        "/javascript:/i",
        "/on(load|click|error|mouseover)=/i",   // BUG: only matches attribute= form
        "/