The vulnerability, GHSA-fwwx-3362-3947 (duplicate of GHSA-f8wv-xp27-6gq7), describes an arbitrary file write in Grav before version 2.0.15. The core issue lies within the Blueprint dynamic-data bare-function validation, which used an incomplete denylist instead of a positive allowlist. Specifically, the error_log PHP function was not included in the denylist, allowing authenticated attackers (with page-edit or blueprint-config access) to invoke it via a data directive. This enabled them to append PHP payloads to web-accessible files, leading to remote code execution.
My analysis focused on the patch commit af31066e9b6a5e6b5e675cc3368f4a741fe3ea21, which is associated with the fix for GHSA-f8wv-xp27-6gq7 and released in Grav 2.0.15. The patch modifies system/src/Grav/Common/Data/Blueprint.php within the isSafeDynamicCall method. The vulnerable code previously checked if (is_string($function) && Utils::isDangerousFunction($function)), which relied on the Utils::isDangerousFunction denylist. The fix replaced this with a positive allowlist check, if (!is_string($function) || !isset(self::$allowedDynamicCallables[strtolower(ltrim($function, '\\'))])), ensuring only explicitly allowed functions can be executed.
Therefore, the vulnerable functions are:
Grav\Common\Data\Blueprint::isSafeDynamicCall: This function was the primary security gate that failed. Its reliance on an incomplete denylist allowed the bypass.
Grav\Common\Utils::isDangerousFunction: This function, while not directly modified in the patch, is explicitly identified in the advisory and patch comments as being incomplete, thus failing to identify error_log as dangerous. Its inadequacy is the root cause of the bypass.
call_user_func_array: This PHP built-in function is the direct sink that executes the attacker-controlled function and arguments. A profiler would show this function being called during exploitation.
error_log: This specific PHP built-in function is the target of the exploitation, used to perform the arbitrary file write. A profiler would show this function being called with attacker-controlled parameters.