CVE-2026-56701Medium· 6.5▾ SunlitGrav is Vulnerable to XXE via SVG Upload
▾ Sunlit zone — Low / medium · no exploitation signal
impact 35.8 · likelihood 0.1 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via GHSA
Last analysed / modified upstream
0.4%
Dear Grav Security Team,
A security vulnerability was discovered in Grav CMS that allows authenticated attackers to read arbitrary files from the server through XML External Entity (XXE) injection.
Vulnerability Summary
| Field | Details |
|---|---|
| Vulnerability Type | XML External Entity (XXE) Injection |
| Severity | High (CVSS 7.5) |
| Affected Versions | Grav CMS <= 1.7.x |
| Affected Component | SVG file upload/processing |
| CWE | CWE-611: Improper Restriction of XML External Entity Reference |
| Authentication Required | Yes (Admin panel access) |
Technical Details
Root Cause
The application uses simplexml_load_string() to process uploaded SVG files without disabling external entity loading. This allows attackers to inject XXE payloads that are processed by the XML parser.
Vulnerable Code Pattern
// Current (Vulnerable):
$svg = simplexml_load_string($content);
// No LIBXML_NOENT flag or entity loader protection
Attack Vector
Impact
An authenticated attacker can:
Read sensitive files:
/etc/passwd - System user informationuser/accounts/*.yaml - Admin credentials and 2FA secretsuser/config/system.yaml - System configuration.env files - Environment secrets and API keysPerform SSRF - Access internal services via external entity URLs
Potential DoS - Billion laughs attack via recursive entity expansion
Proof of Concept
Malicious SVG Payload
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<text x="10" y="50">&xxe;</text>
</svg>
Steps to Reproduce
Recommended Fix
Option 1: Add XXE Protection Flags
libxml_use_internal_errors(true);
$svg = simplexml_load_string($content, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_DTDLOAD);
Option 2: Use SVG Sanitizer Library (Recommended)
use enshrined\svgSanitize\Sanitizer;
$sanitizer = new Sanitizer();
$sanitizer->removeRemoteReferences(true);
$cleanSVG = $sanitizer->sanitize($content);
The enshrined/svg-sanitize library properly strips XXE payloads and other malicious SVG content.
Request
Turki Almatrafi.
Fixed across two repos:
Grav core on the 2.0 branch (commit 5a12f9be8, ships in 2.0.0-beta.2) — VectorImageMedium::__construct (the code path that reads width/height from an uploaded SVG) now strips <!DOCTYPE> and <!ENTITY> declarations before parsing, and calls simplexml_load_string with LIBXML_NONET | LIBXML_NOERROR | LIBXML_NOWARNING. On PHP < 8 it also calls libxml_disable_entity_loader(true) for the duration of the parse.
rhukster/dom-sanitizer (commit 02d08ec) — the library Grav ships as its SVG sanitizer. loadDocument now applies the same DOCTYPE/ENTITY strip and passes LIBXML_NONET to loadXML/loadHTML.
With both layers in place, the PoC:
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<text x="10" y="50">&xxe;</text>
</svg>
no longer expands &xxe;, and the parser cannot make outbound filesystem or network requests for external entities/DTDs. Billion-laughs-style entity expansion is also neutralized because the declarations are stripped before libxml ever sees them.
Files:
system/src/Grav/Common/Page/Medium/VectorImageMedium.php.tests/unit/Grav/Common/Security/SvgXxeSecurityTest.php — XXE neutralization + billion-laughs + plain-SVG regression.src/DOMSanitizer.php + two new XXE tests in its own suite.getgrav/grav < 2.0.0-beta.2Upgrade to a patched release:
getgrav/grav 2.0.0-beta.2Connected by shared product, vendor, weakness, or advisory.
GHSA-32fw-h446-j4hhHigh· 6.5Duplicate Advisory: Grav is Vulnerable to XXE via SVG Upload
CVE-2026-72697High· 6.5Grav: media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content
GHSA-896w-cw95-xq7wHigh· 8.1Duplicate Advisory: Grav: Path Traversal in MediaUploadTrait::deleteFile() Allows Arbitrary File Deletion
CVE-2026-72695High· 8.1Grav: Path Traversal in MediaUploadTrait::deleteFile() Allows Arbitrary File Deletion
GHSA-993v-76jg-67xrMedium· 5.4Duplicate Advisory: Grav: Single invalid UTF-8 byte disables every rule in Security::detectXss(), bypassing the page-content XSS safety gate
GHSA-9pr6-8r9w-wvmjCritical· 8.7Duplicate Advisory: Grav: detectXss() misses an event-handler attribute after an unpaired quote in an unquoted attribute value, giving stored XSS