{"schema":"threatline.record/2","record":{"id":"CVE-2026-56701","aliases":["GHSA-3446-6mgw-f79p"],"summary":"Grav is Vulnerable to XXE via SVG Upload ","details":"Dear Grav Security Team,\n\nA security vulnerability was discovered in Grav CMS that allows authenticated attackers to read arbitrary files from the server through XML External Entity (XXE) injection.\n\n Vulnerability Summary\n\n| Field | Details |\n|-------|---------|\n| Vulnerability Type | XML External Entity (XXE) Injection |\n| Severity | High (CVSS 7.5) |\n| Affected Versions | Grav CMS <= 1.7.x |\n| Affected Component | SVG file upload/processing |\n| CWE | CWE-611: Improper Restriction of XML External Entity Reference |\n| Authentication Required | Yes (Admin panel access) |\n\nTechnical Details\n\n Root Cause\nThe 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.\n\n Vulnerable Code Pattern\n```php\n// Current (Vulnerable):\n$svg = simplexml_load_string($content);\n\n// No LIBXML_NOENT flag or entity loader protection\n```\n\n Attack Vector\n1. Attacker authenticates to Grav admin panel\n2. Uploads malicious SVG file via Pages → Media or File Manager plugin\n3. Server parses SVG and processes XXE entities\n4. Arbitrary file contents are exfiltrated\n\n Impact\n\nAn authenticated attacker can:\n\n1. Read sensitive files:\n   - `/etc/passwd` - System user information\n   - `user/accounts/*.yaml` - Admin credentials and 2FA secrets\n   - `user/config/system.yaml` - System configuration\n   - `.env` files - Environment secrets and API keys\n\n2. Perform SSRF - Access internal services via external entity URLs\n\n3. Potential DoS - Billion laughs attack via recursive entity expansion\n\nProof of Concept\n\n Malicious SVG Payload\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE svg [\n  <!ENTITY xxe SYSTEM \"file:///etc/passwd\">\n]>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\">\n  <text x=\"10\" y=\"50\">&xxe;</text>\n</svg>\n```\n\n Steps to Reproduce\n1. Login to Grav CMS admin panel\n2. Navigate to Pages → select any page → Media tab\n3. Upload the malicious SVG file\n4. Observe file contents in response/error or stored output\n\n Recommended Fix\n\n Option 1: Add XXE Protection Flags\n```php\nlibxml_use_internal_errors(true);\n$svg = simplexml_load_string($content, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_DTDLOAD);\n```\n\n Option 2: Use SVG Sanitizer Library (Recommended)\n```php\nuse enshrined\\svgSanitize\\Sanitizer;\n\n$sanitizer = new Sanitizer();\n$sanitizer->removeRemoteReferences(true);\n$cleanSVG = $sanitizer->sanitize($content);\n```\n\nThe `enshrined/svg-sanitize` library properly strips XXE payloads and other malicious SVG content.\n\n Request\n\n1. Please acknowledge receipt of this report within 5 business days\n2. Please provide an estimated timeline for a security patch\n3. I am happy to assist with testing the fix\n4. I request a CVE be assigned for this vulnerability\n5. If you have a security advisory process, please include me in the credits\n\nTurki Almatrafi.\n\n\n\n---\n\n## Maintainer note — fix applied (2026-04-24)\n\nFixed across two repos:\n\n1. **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.\n\n2. **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`.\n\nWith both layers in place, the PoC:\n\n```xml\n<!DOCTYPE svg [\n  <!ENTITY xxe SYSTEM \"file:///etc/passwd\">\n]>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\">\n  <text x=\"10\" y=\"50\">&xxe;</text>\n</svg>\n```\n\nno 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.\n\n**Files:**\n- [`system/src/Grav/Common/Page/Medium/VectorImageMedium.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Page/Medium/VectorImageMedium.php).\n- [`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.\n- dom-sanitizer: [`src/DOMSanitizer.php`](https://github.com/rhukster/dom-sanitizer/blob/main/src/DOMSanitizer.php) + two new XXE tests in its own suite.","categories":["Libraries & packages"],"product":"getgrav/grav","vendor":"Grav","ecosystem":"composer","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-3446-6mgw-f79p","source_modified_at":"2026-09-23T21:28:08Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"composer","name":"getgrav/grav"},"range":"< 2.0.0-beta.2"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-56701","source_modified_at":"2026-10-08T15:23:26.112Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"Grav","vendor":"Grav","versions":[{"lessThan":"2.0.0-beta.2","status":"affected","version":"0","versionType":"semver"},{"status":"unaffected","version":"2.0.0-beta.2","versionType":"semver"}],"defaultStatus":"unaffected"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-3446-6mgw-f79p","source_modified_at":"2026-09-23T21:45:08.000784960Z","fetched_at":"2026-10-23T18:52:38.922436Z","package":{"name":"getgrav/grav","ecosystem":"Packagist","purl":"pkg:composer/getgrav/grav"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.0.0-beta.2"}]}],"versions":["0.8.0","0.9.0","0.9.1","0.9.10","0.9.11","0.9.12","0.9.13","0.9.14","0.9.15","0.9.16","0.9.17","0.9.18","0.9.19","0.9.2","0.9.20","0.9.21","0.9.22","0.9.23","0.9.24","0.9.25","0.9.26","0.9.27","0.9.28","0.9.29","0.9.3","0.9.30","0.9.31","0.9.32","0.9.33","0.9.34","0.9.35","0.9.36","0.9.37","0.9.38","0.9.39","0.9.4","0.9.40","0.9.41","0.9.42","0.9.43","0.9.44","0.9.45","0.9.5","0.9.6","0.9.7","0.9.8","0.9.9","1.0.0","1.0.0-rc.1","1.0.0-rc.2"]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-3446-6mgw-f79p","source_modified_at":"2026-09-23T21:28:08Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"composer","name":"getgrav/grav"},"version":"2.0.0-beta.2"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-56701","source_modified_at":"2026-10-08T15:23:26.112Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"Grav","ecosystem":null},"version":"2.0.0-beta.2","basis":"first unaffected version stated in the CVE record"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-3446-6mgw-f79p","source_modified_at":"2026-09-23T21:45:08.000784960Z","fetched_at":"2026-10-23T18:52:38.922436Z","package":{"name":"getgrav/grav","ecosystem":"Packagist","purl":"pkg:composer/getgrav/grav"},"version":"2.0.0-beta.2"}],"cvss":6.5,"severity":"Medium","kev":{"listed":false},"epss":{"probability":0.00305,"percentile":0.2212,"score_date":"2026-10-23"},"published_at":"2026-06-23T12:13:06.215Z","modified_at":"2026-10-08T15:23:26.112Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-3446-6mgw-f79p","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-09-23T21:28:08Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-56701","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-10-08T15:23:26.112Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-3446-6mgw-f79p","fetched_at":"2026-10-23T18:52:38.922436Z","source_modified_at":"2026-09-23T21:45:08.000784960Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-56701","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/getgrav/grav/security/advisories/GHSA-3446-6mgw-f79p","tags":["exploit"]},{"url":"https://github.com/getgrav/grav/commit/5a12f9be8314682c8713e569e330f11805d0a663","source":"GitHub Advisory Database"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-56701","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://www.vulncheck.com/advisories/grav-xml-external-entity-injection-via-svg-upload","tags":["third-party-advisory"]},{"url":"https://github.com/advisories/GHSA-3446-6mgw-f79p","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"poc","automatable":"no","technical_impact":"partial","assessed_at":"2026-06-23T14:35:03.732699Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"ecosystem","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-08T15:23:26.112Z","content_hash":"932daf42ab3eb7e37cf2971dc7c13dbe1d89f6ce43d2217afea298936de3aefd"}}
