Every CVE Search/ Get API key

CVE-2026-72702

Grav CMS before 2.0.16 Origin Validation Bypass via Referer

9.3 CriticalFix available

Grav CMS before 2.0.16 contains an origin validation bypass in the Uri::referrer() and Pages::referrerRoute() methods, which validate the Referer header using an unanchored string prefix match (str_starts_with($referrer, $base)) with no trailing delimiter. An attacker who controls a domain that begins with the victim site's origin (e.g. https://example.com.attacker.tld) can send a request with such a Referer to be treated as same-origin, bypassing the Referer-based origin check.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.15%Unchanged since Aug 25, 2026
90%50%10%1%

What each source reports

CVE List Score used 9.3

Grav CMS before 2.0.16 Origin Validation Bypass via Referer

Grav CMS before 2.0.16 contains an origin validation bypass in the Uri::referrer() and Pages::referrerRoute() methods, which validate the Referer header using an unanchored string prefix match (str_starts_with($referrer, $base)) with no trailing delimiter. An attacker who controls a domain that begins with the victim site's origin (e.g. https://example.com.attacker.tld) can send a request with such a Referer to be treated as same-origin, bypassing the Referer-based origin check.

Record
CVE-2026-72702
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
Weakness
CWE-346
Updated
2026-11-02
EUVDEUVD-2026-65216 9.3

Grav CMS before 2.0.16 contains an origin validation bypass in the Uri::referrer() and Pages::referrerRoute() methods, which validate the Referer header using an unanchored string prefix match (str_starts_with($referrer, $base)) with no trailing delimiter. An attacker who controls a domain that begins with the victim site's origin (e.g. https://example.com.attacker.tld) can send a request with such a Referer to be treated as same-origin, bypassing the Referer-based origin check.

Record
EUVD-2026-65216
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-qh7h-6c7g-x8m6 9.3

Duplicate Advisory: Grav: Origin validation bypass in Uri::referrer() and Pages::referrerRoute() via unanchored prefix match

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-9ccq-2jfg-qw33. This link is maintained to preserve external references.

Original Description

Grav CMS before 2.0.16 contains an origin validation bypass in the Uri::referrer() and Pages::referrerRoute() methods, which validate the Referer header using an unanchored string prefix match (str_starts_with($referrer, $base)) with no trailing delimiter. An attacker who controls a domain that begins with the victim site's origin (e.g. https://example.com.attacker.tld) can send a request with such a Referer to be treated as same-origin, bypassing the Referer-based origin check.

Record
GHSA-qh7h-6c7g-x8m6
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/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
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
Weakness
CWE-346
Updated
2026-11-02
GitHub AdvisoriesGHSA-9ccq-2jfg-qw33 No score

Grav: Origin validation bypass in Uri::referrer() and Pages::referrerRoute() via unanchored prefix match

Summary

Grav\Common\Uri::referrer() and Grav\Common\Page\Pages::referrerRoute() both check whether an incoming request's Referer header "came from our site" using str_starts_with($referrer, $base), where $base is the site's own absolute root URL (for example https://example.com, no trailing slash). Because the comparison has no boundary character after the prefix, any Referer value that merely starts with that string is accepted, including a Referer from a completely different host such as https://example.com.attacker.tld.

This is the same class of bug already fixed once in 2.0.15 for the fast static asset server (GHSA-4v9q-p283-qc2m, "also allowing any neighbouring directory whose name starts with the same letters"). The identical pattern is still present in both places that trust the Referer header, and neither is covered by that fix.

Affected product and version

Product: Grav CMS, getgrav/grav Confirmed present in: 2.0.15, commit c2b46866857a93a0aa7048e7ed707ed3ed45dbc3 The pattern is not touched by any of the 2.0.15 security fixes, so earlier 2.x releases are likely affected too. I have not checked how far back it goes.

Affected code

system/src/Grav/Common/Uri.php, method referrer():

$referrer = $_SERVER['HTTP_REFERER'] ?? null;
...
$base = $this->rootUrl(true);   // e.g. "https://example.com", no trailing slash
// Referrer should always have host set and it should come from the same base address.
if (!is_string($referrer) || !str_starts_with($referrer, $base)) {
    $referrer = $default ?: $this->route(true, true);
}
$referrer = substr($referrer, strlen($base));

system/src/Grav/Common/Page/Pages.php, method referrerRoute():

$referrer = $_SERVER['HTTP_REFERER'] ?? null;
$root = $this->grav['base_url_absolute'];   // e.g. "https://example.com"
if (!is_string($referrer) || !str_starts_with($referrer, (string) $root)) {
    return null;
}

Note that the inner per-language loop later in the same referrerRoute() method does anchor the check correctly (str_starts_with($referrer, "{$base}/")), and system/src/Grav/Common/Themes.php line 300 does the same thing correctly ($current === $base || str_starts_with($current, $base . '/')). So the codebase already has the correct pattern elsewhere. Only the two outer checks quoted above compare against the bare root URL with no trailing delimiter.

Root cause

str_starts_with($referrer, $base) treats $base as a plain string prefix. Since $base has no trailing /, a string is accepted as long as it begins with those exact characters, regardless of what character follows. An attacker fully controls their own domain name, so producing a string that begins with the victim's origin is trivial, for example by registering example.com.attacker.tld or example.com-attacker.tld.

Under the default browser Referrer Policy (strict-origin-when-cross-origin), a cross-origin click or form submission from the attacker's page sends only the origin (scheme://host) as Referer, which is exactly the granularity $base is compared at, so no unusual browser configuration is required.

Proof of concept, verified, real output

This was run directly against the actual, unmodified source file from the repository, not a reimplementation. Steps and exact output below.

Step 1, clone the repo and confirm the commit under test:

$ git clone --depth 1 https://github.com/getgrav/grav.git
$ cd grav && git log -1 --format="%H %ai"
c2b46866857a93a0aa7048e7ed707ed3ed45dbc3 2026-08-03 15:14:50 +0100

Step 2, install PHP to execute the real class:

$ apt-get install -y php-cli
$ php -v
PHP 8.3.6 (cli) (built: Jul 16 2026 18:30:41) (NTS)

Step 3, PoC harness. Full site bootstrap, composer install, database, config, is not required to demonstrate this specific bug, since referrer() only needs the $root property, which init() would normally compute from the site config. The harness sets that one property with PHP…

Record
GHSA-9ccq-2jfg-qw33
Weakness
CWE-346
Updated
2026-11-02
NVD 9.3

Grav CMS before 2.0.16 contains an origin validation bypass in the Uri::referrer() and Pages::referrerRoute() methods, which validate the Referer header using an unanchored string prefix match (str_starts_with($referrer, $base)) with no trailing delimiter. An attacker who controls a domain that begins with the victim site's origin (e.g. https://example.com.attacker.tld) can send a request with such a Referer to be treated as same-origin, bypassing the Referer-based origin check.

Record
CVE-2026-72702
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/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
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
Weakness
CWE-346
Updated
2026-11-02
OSV 9.3

Grav CMS before 2.0.16 Origin Validation Bypass via Referer

Grav CMS before 2.0.16 contains an origin validation bypass in the Uri::referrer() and Pages::referrerRoute() methods, which validate the Referer header using an unanchored string prefix match (str_starts_with($referrer, $base)) with no trailing delimiter. An attacker who controls a domain that begins with the victim site's origin (e.g. https://example.com.attacker.tld) can send a request with such a Referer to be treated as same-origin, bypassing the Referer-based origin check.

Record
CVE-2026-72702
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-346
Updated
2026-11-02
OSVGHSA-qh7h-6c7g-x8m6 9.3

Duplicate Advisory: Grav: Origin validation bypass in Uri::referrer() and Pages::referrerRoute() via unanchored prefix match

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-9ccq-2jfg-qw33. This link is maintained to preserve external references.

Original Description

Grav CMS before 2.0.16 contains an origin validation bypass in the Uri::referrer() and Pages::referrerRoute() methods, which validate the Referer header using an unanchored string prefix match (str_starts_with($referrer, $base)) with no trailing delimiter. An attacker who controls a domain that begins with the victim site's origin (e.g. https://example.com.attacker.tld) can send a request with such a Referer to be treated as same-origin, bypassing the Referer-based origin check.

Record
GHSA-qh7h-6c7g-x8m6
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/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
CVSS 3.1
5.4 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
Weakness
CWE-346
Updated
2026-11-02
OSVGHSA-9ccq-2jfg-qw33 No score

Grav: Origin validation bypass in Uri::referrer() and Pages::referrerRoute() via unanchored prefix match

Summary

Grav\Common\Uri::referrer() and Grav\Common\Page\Pages::referrerRoute() both check whether an incoming request's Referer header "came from our site" using str_starts_with($referrer, $base), where $base is the site's own absolute root URL (for example https://example.com, no trailing slash). Because the comparison has no boundary character after the prefix, any Referer value that merely starts with that string is accepted, including a Referer from a completely different host such as https://example.com.attacker.tld.

This is the same class of bug already fixed once in 2.0.15 for the fast static asset server (GHSA-4v9q-p283-qc2m, "also allowing any neighbouring directory whose name starts with the same letters"). The identical pattern is still present in both places that trust the Referer header, and neither is covered by that fix.

Affected product and version

Product: Grav CMS, getgrav/grav Confirmed present in: 2.0.15, commit c2b46866857a93a0aa7048e7ed707ed3ed45dbc3 The pattern is not touched by any of the 2.0.15 security fixes, so earlier 2.x releases are likely affected too. I have not checked how far back it goes.

Affected code

system/src/Grav/Common/Uri.php, method referrer():

$referrer = $_SERVER['HTTP_REFERER'] ?? null;
...
$base = $this->rootUrl(true);   // e.g. "https://example.com", no trailing slash
// Referrer should always have host set and it should come from the same base address.
if (!is_string($referrer) || !str_starts_with($referrer, $base)) {
    $referrer = $default ?: $this->route(true, true);
}
$referrer = substr($referrer, strlen($base));

system/src/Grav/Common/Page/Pages.php, method referrerRoute():

$referrer = $_SERVER['HTTP_REFERER'] ?? null;
$root = $this->grav['base_url_absolute'];   // e.g. "https://example.com"
if (!is_string($referrer) || !str_starts_with($referrer, (string) $root)) {
    return null;
}

Note that the inner per-language loop later in the same referrerRoute() method does anchor the check correctly (str_starts_with($referrer, "{$base}/")), and system/src/Grav/Common/Themes.php line 300 does the same thing correctly ($current === $base || str_starts_with($current, $base . '/')). So the codebase already has the correct pattern elsewhere. Only the two outer checks quoted above compare against the bare root URL with no trailing delimiter.

Root cause

str_starts_with($referrer, $base) treats $base as a plain string prefix. Since $base has no trailing /, a string is accepted as long as it begins with those exact characters, regardless of what character follows. An attacker fully controls their own domain name, so producing a string that begins with the victim's origin is trivial, for example by registering example.com.attacker.tld or example.com-attacker.tld.

Under the default browser Referrer Policy (strict-origin-when-cross-origin), a cross-origin click or form submission from the attacker's page sends only the origin (scheme://host) as Referer, which is exactly the granularity $base is compared at, so no unusual browser configuration is required.

Proof of concept, verified, real output

This was run directly against the actual, unmodified source file from the repository, not a reimplementation. Steps and exact output below.

Step 1, clone the repo and confirm the commit under test:

$ git clone --depth 1 https://github.com/getgrav/grav.git
$ cd grav && git log -1 --format="%H %ai"
c2b46866857a93a0aa7048e7ed707ed3ed45dbc3 2026-08-03 15:14:50 +0100

Step 2, install PHP to execute the real class:

$ apt-get install -y php-cli
$ php -v
PHP 8.3.6 (cli) (built: Jul 16 2026 18:30:41) (NTS)

Step 3, PoC harness. Full site bootstrap, composer install, database, config, is not required to demonstrate this specific bug, since referrer() only needs the $root property, which init() would normally compute from the site config. The harness sets that one property with PHP…

Record
GHSA-9ccq-2jfg-qw33
Weakness
CWE-346
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
getgrav/grav
Packagist
through 2.0.15
2.0.16
getgrav/grav
Packagist
before 2.0.16, plus listed versions
0.8.00.9.00.9.10.9.100.9.11
Show 45 moreShow less
0.9.120.9.130.9.140.9.150.9.160.9.170.9.180.9.190.9.20.9.200.9.210.9.220.9.230.9.240.9.250.9.260.9.270.9.280.9.290.9.30.9.300.9.310.9.320.9.330.9.340.9.350.9.360.9.370.9.380.9.390.9.40.9.400.9.410.9.420.9.430.9.440.9.450.9.50.9.60.9.70.9.80.9.91.0.01.0.0-rc.11.0.0-rc.2
2.0.16

History

History
RecordedChangedSources
affected
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-72702 is a critical vulnerability (CVSS 9.3) in Getgrav Grav, published August 25, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.15%, the 4th percentile. Fixed in getgrav/grav 2.0.16.

References

This page doesn't list everything

Left out here: 300 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open