{"id":"CVE-2026-72832","aliases":["EUVD-2026-58646","GHSA-269c-h76q-8cxw","GHSA-cgvr-f65r-pjv3"],"status":"published","title":"Grav before 2.0.13 Stored XSS via quoted-attribute bypass","description":"Grav versions from 1.5.2 through 2.0.12 contain a stored cross-site scripting vulnerability in the Security::detectXss() function (system/src/Grav/Common/Security.php). The event-handler scan is anchored at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`; when a `\u003e` appears inside a quoted attribute value the browser keeps the tag open and parses a subsequent event handler (e.g. onerror), so the detector and browser disagree. A page editor without admin.super privileges can save page content such as `\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e`, which is accepted, stored, and executed in the site origin when any visitor (including unauthenticated users) views the page. Fixed in 2.0.13.","severity":{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00314,"percentile":0.24692,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-79"],"published":"2026-08-14T11:35:38Z","modified":"2026-10-30T10:47:20Z","fixes":[{"ecosystem":"Packagist","package":"getgrav/grav","fixed_in":["2.0.13"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"getgrav/grav","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.5.2"},{"last_affected":"2.0.12"},{"fixed":"2.0.13"}]}]},{"source":"osv","ecosystem":"Packagist","package":"getgrav/grav","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.5.2"},{"fixed":"2.0.13"}]}],"versions":["1.5.10","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.5.8","1.5.9","1.6.0","1.6.0-beta.1","1.6.0-beta.2","1.6.0-beta.3","1.6.0-beta.4","1.6.0-beta.5","1.6.0-beta.6","1.6.0-beta.7","1.6.0-beta.8","1.6.0-rc.1","1.6.0-rc.2","1.6.0-rc.3","1.6.0-rc.4","1.6.1","1.6.10","1.6.11","1.6.12","1.6.13","1.6.14","1.6.15","1.6.16","1.6.17","1.6.18","1.6.19","1.6.2","1.6.20","1.6.21","1.6.22","1.6.23","1.6.24","1.6.25","1.6.26","1.6.27","1.6.28","1.6.29","1.6.3","1.6.30","1.6.31","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.7.0","1.7.0-beta.1","1.7.0-beta.10","1.7.0-beta.2","1.7.0-beta.3","1.7.0-beta.4","1.7.0-beta.5","1.7.0-beta.6","1.7.0-beta.7","1.7.0-beta.8","1.7.0-beta.9","1.7.0-rc.1","1.7.0-rc.10","1.7.0-rc.11","1.7.0-rc.12","1.7.0-rc.13","1.7.0-rc.14","1.7.0-rc.15","1.7.0-rc.16","1.7.0-rc.17","1.7.0-rc.18","1.7.0-rc.19","1.7.0-rc.2","1.7.0-rc.20","1.7.0-rc.3","1.7.0-rc.4","1.7.0-rc.5","1.7.0-rc.6","1.7.0-rc.7","1.7.0-rc.8","1.7.0-rc.9","1.7.1","1.7.10","1.7.12","1.7.13","1.7.14","1.7.15","1.7.16","1.7.17","1.7.18","1.7.19","1.7.20","1.7.21","1.7.22","1.7.23","1.7.24","1.7.25","1.7.26","1.7.26.1","1.7.27","1.7.27.1","1.7.28","1.7.29","1.7.29.1","1.7.3","1.7.30","1.7.31","1.7.32","1.7.33","1.7.34","1.7.35","1.7.36","1.7.37","1.7.37.1","1.7.38","1.7.39","1.7.39.1","1.7.39.2","1.7.39.3","1.7.39.4","1.7.4","1.7.40","1.7.41","1.7.41.1","1.7.41.2","1.7.42","1.7.42.1","1.7.42.2","1.7.42.3","1.7.43","1.7.44","1.7.45","1.7.46","1.7.47","1.7.48","1.7.49","1.7.49.1","1.7.49.2","1.7.49.3","1.7.49.4","1.7.49.5","1.7.5","1.7.51","1.7.52","1.7.53","1.7.53.1","1.7.53.2","1.7.53.3","1.7.6","1.7.7","1.7.8","1.7.9","1.8.0-beta.1","1.8.0-beta.10","1.8.0-beta.11","1.8.0-beta.12","1.8.0-beta.13","1.8.0-beta.14","1.8.0-beta.15","1.8.0-beta.16","1.8.0-beta.17","1.8.0-beta.18","1.8.0-beta.19","1.8.0-beta.2","1.8.0-beta.20","1.8.0-beta.21","1.8.0-beta.22","1.8.0-beta.23","1.8.0-beta.24","1.8.0-beta.25","1.8.0-beta.26","1.8.0-beta.27","1.8.0-beta.28","1.8.0-beta.29","1.8.0-beta.3","1.8.0-beta.4","1.8.0-beta.5","1.8.0-beta.6","1.8.0-beta.7","1.8.0-beta.8","1.8.0-beta.9","2.0.0","2.0.0-beta.1","2.0.0-beta.2","2.0.0-beta.3","2.0.0-beta.4","2.0.0-rc.1","2.0.0-rc.10","2.0.0-rc.2","2.0.0-rc.3","2.0.0-rc.4","2.0.0-rc.5","2.0.0-rc.6","2.0.0-rc.7","2.0.0-rc.8","2.0.0-rc.9","2.0.1","2.0.10","2.0.11","2.0.12","2.0.2","2.0.3","2.0.4","2.0.5","2.0.6","2.0.7","2.0.8","2.0.9"]}],"products":[{"vendor":"getgrav","product":"grav"}],"references":[{"url":"https://github.com/getgrav/grav/security/advisories/GHSA-269c-h76q-8cxw","tags":["vendor-advisory","exploit","advisory","web"]},{"url":"https://github.com/getgrav/grav/commit/ad9709f865b09b68798fb1ac375b484a8cc1d892","tags":["patch","fix","web"]},{"url":"https://www.vulncheck.com/advisories/grav-before-stored-xss-via-quoted-attribute-bypass","tags":["third-party-advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-72832","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-269c-h76q-8cxw"},{"url":"https://github.com/advisories/GHSA-cgvr-f65r-pjv3"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/72xxx/CVE-2026-72832.json","tags":["advisory"]},{"url":"https://github.com/getgrav/grav","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-72832","title":"Grav before 2.0.13 Stored XSS via quoted-attribute bypass","description":"Grav versions from 1.5.2 through 2.0.12 contain a stored cross-site scripting vulnerability in the Security::detectXss() function (system/src/Grav/Common/Security.php). The event-handler scan is anchored at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`; when a `\u003e` appears inside a quoted attribute value the browser keeps the tag open and parses a subsequent event handler (e.g. onerror), so the detector and browser disagree. A page editor without admin.super privileges can save page content such as `\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e`, which is accepted, stored, and executed in the site origin when any visitor (including unauthenticated users) views the page. Fixed in 2.0.13.","status":"published","published":"2026-08-14T11:35:38.484Z","modified":"2026-10-29T21:16:52.047Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N","level":"medium","assessor":"cna"},{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:19:51.663093Z"}],"euvd":[{"id":"EUVD-2026-58646","description":"Grav versions from 1.5.2 through 2.0.12 contain a stored cross-site scripting vulnerability in the Security::detectXss() function (system/src/Grav/Common/Security.php). The event-handler scan is anchored at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`; when a `\u003e` appears inside a quoted attribute value the browser keeps the tag open and parses a subsequent event handler (e.g. onerror), so the detector and browser disagree. A page editor without admin.super privileges can save page content such as `\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e`, which is accepted, stored, and executed in the site origin when any visitor (including unauthenticated users) views the page. Fixed in 2.0.13.","status":"published","published":"2026-08-14T11:35:38Z","modified":"2026-10-29T21:16:52Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N","level":"medium"}],"updated_at":"2026-11-02T16:59:13.894662Z"}],"ghsa":[{"id":"GHSA-269c-h76q-8cxw","title":"Grav: Stored XSS via quoted-attribute bypass in detectXss","description":"### Summary\n\nA page editor without `admin.super` can place an event handler after a `\u003e` inside a quoted attribute. Grav accepts and stores the page, then executes the handler in the application origin when a visitor opens it.\n\n### Details\n\n`Security::detectXss()` (`system/src/Grav/Common/Security.php:253`) anchors the `on_events` scan at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`. When that character is inside a quoted value, the browser keeps the tag open and parses the later `onerror` attribute, so the detector and browser disagree. `AdminController::savePage()` relies on this detector when saving content from page editors outside the `admin.super` whitelist.\n\n### PoC\n\nI reproduced this with `getgrav/grav` 2.0.11 (`ad9709f865b09b68798fb1ac375b484a8cc1d892`), Admin 1.10.52, and Quark 2 1.1.4.\n\n1. Sign in as a user with `admin.login` and `admin.pages`, but without `admin.super`.\n2. Create or edit `/xsstest` and save this page body:\n\n```html\n\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e\n```\n\n3. Open `/xsstest` in a private browser window.\n\nThe save succeeds and the visitor sees an alert containing the site domain. With the body changed to `\u003cimg src=x onerror=alert(1)\u003e`, the same endpoint rejects it with `XSS issue detected` and does not store it.\n\n### Impact\n\nA page editor can execute JavaScript in the origin of every user who views the stored page, including unauthenticated visitors.\n\n### Anticipated objection and response\n\nAlthough the `detectXss()` docblock describes it as a heuristic that cannot catch every XSS, this check is the storage-time boundary for page editors outside the default `security.xss_whitelist` of `admin.super`. The same endpoint rejects a plain handler but accepts this executable form, allowing a lower-trust editor to cross the boundary the check is intended to enforce.\n\n### Suggested fix\n\nPrefer an HTML tokenizer or sanitizer that rejects event-handler attributes on parsed elements. If the existing tripwire remains, make its tag scan quote-aware instead of treating every `\u003e` as a boundary. Add double-quoted and single-quoted regression cases plus the rejected plain-handler control.","status":"published","published":"2026-09-17T17:28:34Z","modified":"2026-09-17T17:28:35Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:41:18.357514Z"},{"id":"GHSA-cgvr-f65r-pjv3","title":"Duplicate Advisory: Grav: Stored XSS via quoted-attribute bypass in detectXss","description":"### Duplicate Advisory\nThis advisory has been withdrawn because it is a duplicate of GHSA-269c-h76q-8cxw. This link is maintained to preserve external references.\n\n### Original Description\nGrav versions from 1.5.2 through 2.0.12 contain a stored cross-site scripting vulnerability in the Security::detectXss() function (system/src/Grav/Common/Security.php). The event-handler scan is anchored at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`; when a `\u003e` appears inside a quoted attribute value the browser keeps the tag open and parses a subsequent event handler (e.g. onerror), so the detector and browser disagree. A page editor without admin.super privileges can save page content such as `\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e`, which is accepted, stored, and executed in the site origin when any visitor (including unauthenticated users) views the page. Fixed in 2.0.13.","status":"withdrawn","published":"2026-08-14T12:31:26Z","modified":"2026-09-17T17:28:20Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium"},{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:41:18.357514Z"}],"nvd":[{"id":"CVE-2026-72832","description":"Grav versions from 1.5.2 through 2.0.12 contain a stored cross-site scripting vulnerability in the Security::detectXss() function (system/src/Grav/Common/Security.php). The event-handler scan is anchored at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`; when a `\u003e` appears inside a quoted attribute value the browser keeps the tag open and parses a subsequent event handler (e.g. onerror), so the detector and browser disagree. A page editor without admin.super privileges can save page content such as `\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e`, which is accepted, stored, and executed in the site origin when any visitor (including unauthenticated users) views the page. Fixed in 2.0.13.","status":"published","published":"2026-08-14T12:16:46.54Z","modified":"2026-10-08T16:17:36.197Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"},{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:12:37.557758Z"}],"osv":[{"id":"CVE-2026-72832","title":"Grav before 2.0.13 Stored XSS via quoted-attribute bypass","description":"Grav versions from 1.5.2 through 2.0.12 contain a stored cross-site scripting vulnerability in the Security::detectXss() function (system/src/Grav/Common/Security.php). The event-handler scan is anchored at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`; when a `\u003e` appears inside a quoted attribute value the browser keeps the tag open and parses a subsequent event handler (e.g. onerror), so the detector and browser disagree. A page editor without admin.super privileges can save page content such as `\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e`, which is accepted, stored, and executed in the site origin when any visitor (including unauthenticated users) views the page. Fixed in 2.0.13.","status":"published","published":"2026-08-14T11:35:38.484Z","modified":"2026-10-30T10:47:20.031929229Z","cvss":[{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:45:15.88521Z"},{"id":"GHSA-269c-h76q-8cxw","title":"Grav: Stored XSS via quoted-attribute bypass in detectXss","description":"### Summary\n\nA page editor without `admin.super` can place an event handler after a `\u003e` inside a quoted attribute. Grav accepts and stores the page, then executes the handler in the application origin when a visitor opens it.\n\n### Details\n\n`Security::detectXss()` (`system/src/Grav/Common/Security.php:253`) anchors the `on_events` scan at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`. When that character is inside a quoted value, the browser keeps the tag open and parses the later `onerror` attribute, so the detector and browser disagree. `AdminController::savePage()` relies on this detector when saving content from page editors outside the `admin.super` whitelist.\n\n### PoC\n\nI reproduced this with `getgrav/grav` 2.0.11 (`ad9709f865b09b68798fb1ac375b484a8cc1d892`), Admin 1.10.52, and Quark 2 1.1.4.\n\n1. Sign in as a user with `admin.login` and `admin.pages`, but without `admin.super`.\n2. Create or edit `/xsstest` and save this page body:\n\n```html\n\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e\n```\n\n3. Open `/xsstest` in a private browser window.\n\nThe save succeeds and the visitor sees an alert containing the site domain. With the body changed to `\u003cimg src=x onerror=alert(1)\u003e`, the same endpoint rejects it with `XSS issue detected` and does not store it.\n\n### Impact\n\nA page editor can execute JavaScript in the origin of every user who views the stored page, including unauthenticated visitors.\n\n### Anticipated objection and response\n\nAlthough the `detectXss()` docblock describes it as a heuristic that cannot catch every XSS, this check is the storage-time boundary for page editors outside the default `security.xss_whitelist` of `admin.super`. The same endpoint rejects a plain handler but accepts this executable form, allowing a lower-trust editor to cross the boundary the check is intended to enforce.\n\n### Suggested fix\n\nPrefer an HTML tokenizer or sanitizer that rejects event-handler attributes on parsed elements. If the existing tripwire remains, make its tag scan quote-aware instead of treating every `\u003e` as a boundary. Add double-quoted and single-quoted regression cases plus the rejected plain-handler control.","status":"published","published":"2026-09-17T17:28:34Z","modified":"2026-09-17T17:45:04.2170479Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:46:33.956911Z"},{"id":"GHSA-cgvr-f65r-pjv3","title":"Duplicate Advisory: Grav: Stored XSS via quoted-attribute bypass in detectXss","description":"### Duplicate Advisory\nThis advisory has been withdrawn because it is a duplicate of GHSA-269c-h76q-8cxw. This link is maintained to preserve external references.\n\n### Original Description\nGrav versions from 1.5.2 through 2.0.12 contain a stored cross-site scripting vulnerability in the Security::detectXss() function (system/src/Grav/Common/Security.php). The event-handler scan is anchored at `\u003c` and uses `[^\u003e]*?`, which cannot cross the first literal `\u003e`; when a `\u003e` appears inside a quoted attribute value the browser keeps the tag open and parses a subsequent event handler (e.g. onerror), so the detector and browser disagree. A page editor without admin.super privileges can save page content such as `\u003cimg src=x title=\"\u003e\" onerror=alert(document.domain)\u003e`, which is accepted, stored, and executed in the site origin when any visitor (including unauthenticated users) views the page. Fixed in 2.0.13.","status":"withdrawn","published":"2026-08-14T12:31:26Z","modified":"2026-09-17T17:45:04.285054329Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"},{"version":"4.0","score":5.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:46:44.927642Z"}]}}
