GHSA-3446-6mgw-f79p Grav is Vulnerable to XXE via SVG Upload
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 DetailsRoot 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 PatternAttack VectorAttacker authenticates to Grav admin panelUploads malicious SVG file via Pages → Media or File Manager pluginServer parses SVG and processes XXE entitiesArbitrary file contents are exfiltratedImpactAn authenticated attacker can:Read sensitive files:`/etc/passwd` - System user information`user/accounts/*.yaml` - Admin credentials and 2FA secrets`user/config/system.yaml` - System configuration`.env` files - Environment secrets and API keysPerform SSRF - Access internal services via external entity URLsPotential DoS - Billion laughs attack via recursive entity expansionProof of ConceptMalicious SVG PayloadSteps to ReproduceLogin to Grav CMS admin panelNavigate to Pages → select any page → Media tabUpload the malicious SVG fileObserve file contents in response/error or stored outputRecommended FixOption 1: Add XXE Protection FlagsOption 2: Use SVG Sanitizer Library (Recommended)The `enshrined/svg-sanitize` library properly strips XXE payloads and other malicious SVG content.RequestPlease acknowledge receipt of this report within 5 business daysPlease provide an estimated timeline for a security patchI am happy to assist with testing the fixI request a CVE be assigned for this vulnerabilityIf you have a security advisory process, please include me in the creditsTurki Almatrafi.Maintainer note — fix applied (2026-04-24)Fixed across two repos:**Grav core on the `2.0` branch** (commit [`5a12f9be8`](https://github.com/getgrav/grav/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`](https://github.com/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: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`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Page/Medium/VectorImageMedium.php).[`tests/unit/Grav/Common/Security/SvgXxeSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/SvgXxeSecurityTest.php) — XXE neutralization + billion-laughs + plain-SVG regression.dom-sanitizer: [`src/DOMSanitizer.php`](https://github.com/rhukster/dom-sanitizer/blob/main/src/DOMSanitizer.php) + two new XXE tests in its own suite.
CVSS v3.1 score: 6.5