Sep 17, 2026·9 min read·7 visits
Authenticated Remote Code Execution in Grav CMS before 2.0.13 via array-notation dynamic data providers.
CVE-2026-72819 is a high-severity Remote Code Execution (RCE) vulnerability in Grav CMS before version 2.0.13. The vulnerability lies in the validation of dynamic data providers (callbacks) within the Flex Objects plugin settings and blueprints, allowing administrative users to bypass validation checks via array-notation callables. This validation failure enables administrative users to execute arbitrary PHP classes and methods, including the GPM Installer unZip routine, leading to full remote code execution on the server.
Grav CMS is a popular flat-file content management system written in PHP. The platform relies heavily on plugins and dynamic configuration files, known as blueprints, to render administrative forms and manage data objects. One of the core components facilitating structured content editing is the Flex Objects plugin. This plugin exposes an administrative interface that allows users to model and manipulate data fields based on YAML blueprint schemas.\n\nThe attack surface of concern involves the parsing of dynamic properties within these blueprints, specifically fields prefixed with data-*@. These fields allow forms to populate options or settings dynamically by executing specified PHP callables. To prevent malicious administrative users from executing arbitrary code, Grav CMS implements validation logic designed to restrict these dynamic calls to an approved set of safe methods.\n\nCVE-2026-72819 represents a critical bypass of this safety validation mechanism in the Blueprint class. By supplying the dynamic data provider as a PHP array rather than a standard string representation, an authenticated administrator can entirely evade validation checks. This bypass allows the direct execution of arbitrary PHP classes and methods available in the application scope, leading to unauthenticated remote code execution via local file upload mechanisms.\n\nThis vulnerability has been assigned CVSS base scores of 8.8 (CVSS v3.1) and 8.7 (CVSS v4.0), emphasizing its severity in compromised administrative contexts. The primary risk stems from the fact that administrative users, who are normally allowed to edit form designs, can abuse this privilege to gain underlying operating system shell access. Understanding the root cause requires a deep dive into how PHP handles dynamic callables and how the validation function processed them.
The root cause of CVE-2026-72819 lies in the discrepancy between how PHP identifies callable structures and how the validation function in Grav CMS parsed them. Prior to version 2.0.13, the Blueprint::isSafeDynamicCall() method, defined in system/src/Grav/Common/Data/Blueprint.php, was designed to inspect and sanitize any callback provided in blueprint configuration files. The method expected the callback to be represented as a scalar string, typically formatted with a scope resolution operator as Class::method.\n\nIf the input to the validation function matches this string format, the code performs strict validation. It evaluates whether the target class and method reside on a denylist or fall outside an allowed range of execution. However, PHP natively supports multiple representations of the callable type. Beyond the standard Class::method string representation, PHP permits callables to be structured as an array containing two elements: a class name or object at index 0, and a method name at index 1, such as [Class, method].\n\nWhen the validation engine encountered an array-based callback, the expression is_string($function) returned false. Consequently, the conditional block containing the validation, filtering, and denylist check was bypassed entirely. Because the validation routine did not explicitly reject array-based inputs, it defaulted to returning true, validating the call as safe. The unchecked callable array then reached the execution sink, call_user_func_array(), which correctly recognized the array structure as a valid callable and executed the target method on the server.\n\nThis flaw highlights a classic bug pattern where input validation logic is tightly coupled to specific data formats without accounting for alternative formats that the underlying runtime interpreter accepts. Because PHP's dynamic typing and flexible method invocation accept both string and array formats, validators must normalize all inputs to a uniform format before performing checks. Failing to do so allows alternative representations to slip past security filters while remaining fully executable by the engine.
To understand the mechanics of the vulnerability, we must examine the vulnerable code path inside system/src/Grav/Common/Data/Blueprint.php alongside the patched implementation. In the vulnerable version, the signature of isSafeDynamicCall received the $function parameter, which could be either a string or an array depending on how it was declared in the YAML blueprint.\n\nThe following code block shows the vulnerable validation logic. Notice how it expects $function to be a string containing double colons, failing to handle array structures:\n\nphp\n// Vulnerable Implementation (system/src/Grav/Common/Data/Blueprint.php)\npublic static function isSafeDynamicCall($function, array $params): bool\n{\n // The routine checks if the function is a string and contains '::'\n if (is_string($function) && str_contains($function, '::')) {\n // Validation logic for Class::method goes here\n // If dangerous, this block returns false\n }\n\n // Bare functions are checked here\n if (is_string($function) && in_array($function, self::$dangerous_functions)) {\n return false;\n }\n\n // If $function is an array, both checks are skipped and true is returned\n return true;\n}\n\n\nIn the patched version (2.0.13), the developers resolved this issue by normalizing the array input before executing any safety checks. If $function is detected as an array containing exactly two elements, the code translates it into the standard string format Class::method and processes it normally.\n\nphp\n// Patched Implementation (system/src/Grav/Common/Data/Blueprint.php)\npublic static function isSafeDynamicCall($function, array $params): bool\n{\n // Normalise array-based callables to standard string representation\n if (is_array($function)) {\n if (count($function) === 2 && isset($function[0], $function[1])\n && is_string($function[0]) && is_string($function[1])) {\n $function = $function[0] . '::' . $function[1];\n } else {\n // Refuse any non-standard array structure immediately\n return false;\n }\n }\n\n // Now, array callables converted to strings are properly checked\n if (is_string($function) && str_contains($function, '::')) {\n // Regular validation logic handles the converted string\n }\n\n // Bare function validation remains unchanged\n return true;\n}\n\n\nThe code fix completely neutralizes the bypass by standardizing the array input format into a single representation before any verification happens. Any array that does not match the precise structure of an array-callable (exactly two elements, both of which are strings) is immediately rejected. This prevents arbitrary object instances or multi-element arrays from bypassing the validation logic, closing both the primary exploit vector and potential variants.
Exploiting CVE-2026-72819 requires an attacker to have authenticated access to the administrative dashboard with sufficient privileges to modify blueprints or settings configurations. The attacker first prepares a payload by compressing a PHP web shell inside a ZIP archive. This archive is uploaded to the server using any available page-media or asset-upload feature, storing the compressed payload in a known directory on the target filesystem.\n\nNext, the attacker modifies a settings or blueprint file using the array-based callable syntax to call the GPM Installer's extraction routine. The blueprint configuration points the dynamic data provider to Grav\Common\GPM\Installer::unZip. The parameter list supplied within the blueprint dictates the source ZIP file and the destination directory, which is set to the web root.\n\nyaml\n# Example YAML payload targeting a blueprint field\nvulnerable_field:\n type: select\n data-options@: [\n ['Grav\\Common\\GPM\\Installer', 'unZip'],\n ['user/pages/01.home/payload.zip', './']\n ]\n\n\nOnce the modified blueprint is loaded or saved, the administrative backend parses the configuration. The application passes the array structure ['Grav\Common\GPM\Installer', 'unZip'] to the isSafeDynamicCall method. Because the validation is bypassed, the application proceeds to execute call_user_func_array(). This extracts the uploaded ZIP file to the web root, exposing the PHP shell for direct access via the web browser.\n\nThe following execution flow diagram details the sequence from user submission of the malicious blueprint through validation bypass to arbitrary code execution:\n\nmermaid\ngraph LR\n subgraph Client ["Client Side"]\n A["YAML Payload with Array Callable"]\n end\n subgraph Server ["Server Side Processing"]\n B["Blueprint::isSafeDynamicCall()"]\n C{"Is String?"}\n D["Validation Skipped"]\n E["call_user_func_array()"]\n F["GPM\\Installer::unZip()"]\n G["Web Root (shell.php)"]\n end\n A --> B\n B --> C\n C -- No --> D\n D --> E\n E --> F\n F --> G\n
The security impact of CVE-2026-72819 is classified as high, carrying a CVSS base score of 8.8 (NVD CVSS v3.1). The vulnerability allows full remote code execution in the security context of the web server daemon. An attacker who successfully executes arbitrary PHP code can read, write, or delete files across the application directory, access database credentials, and potentially pivot to other internal services.\n\nAlthough the vulnerability requires authentication, administrative access is a common prerequisite in modern web environments. The exploitability remains high because the payload uses built-in administrative features and standard PHP behaviors, requiring no external compiled dependencies. This increases the threat profile of the vulnerability, particularly in multi-author sites or where administrative access can be obtained via session hijacking or cross-site scripting (XSS).\n\nThis flaw illustrates the risk of partial validation in dynamic languages. When input validation is decoupled from the actual execution sink, minor syntactic variations can bypass security layers entirely. Organizations using Grav CMS must recognize that any user with access to form configurations can escalate their privileges to complete system compromise if the system is left unpatched.\n\nIn addition to direct remote code execution, the ability to write files to arbitrary locations on the server has long-term implications. Threat actors could plant persistent web shells, modify core application logic to intercept user credentials, or deploy ransomware across the hosting environment. Because the exploit runs under the web server's service account, any file writable by the server is vulnerable to modification or disclosure.
The definitive solution for CVE-2026-72819 is upgrading Grav CMS to version 2.0.13 or later. This version incorporates the patch in the Blueprint class, ensuring that all array-based callables are properly normalized and vetted against safety denylists. Administrators should coordinate with their system operators to apply this update immediately, verifying that permissions on the update directory are correctly configured.\n\nIf upgrading is not immediately possible, several defensive mitigations can be deployed to reduce the risk of exploitation. Access to the Grav admin panel should be restricted to trusted IP addresses or placed behind a virtual private network (VPN). Additionally, file-system permissions should be hardened to ensure the web server user cannot write directly to the web root or executable directories, blocking the extraction phase of the exploit.\n\nA Web Application Firewall (WAF) can also be configured to inspect incoming administrative requests. Rules should target request bodies containing YAML array structures associated with dynamic attributes like data-*@ combined with sensitive PHP classes such as GPM\Installer or extraction functions. Security teams are advised to scan existing blueprint configuration files for any unauthorized array-formatted callback registrations.\n\nFinally, organizations should implement routine file integrity monitoring (FIM) and log analysis. File integrity checks can identify anomalous file additions, particularly within the root directory and the public assets folder. Reviewing administrative activity logs for unexpected updates to blueprint schemas or configuration files provides early detection of potential exploitation attempts prior to the execution of arbitrary payloads.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Grav CMS getgrav | < 2.0.13 | 2.0.13 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-72819 |
| CWE ID | CWE-94 |
| CVSS v3.1 Score | 8.8 (High) |
| Exploit Status | Proof of Concept (PoC) verified |
| Affected Component | Blueprint Dynamic Callables |
| Remediation | Upgrade to version 2.0.13 |
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes the input before the code segment is executed.
CVE-2026-75837 is a critical privilege escalation vulnerability affecting the Grav Flat-File Content Management System (CMS) in versions prior to 2.0.14. Due to a missing security guard on the access field within the core Flex group blueprint configuration file (system/blueprints/user/group.yaml), a delegated administrative operator can submit a crafted payload to elevate their permissions to super-administrator, which can then be leveraged to achieve remote code execution.
CVE-2026-76461 is a critical, unauthenticated, remotely exploitable SQL Injection (SQLi) vulnerability in the email parsing engine of Cisco AsyncOS Software for Cisco Secure Email Gateway (SEG). An unauthenticated remote attacker can exploit this vulnerability by transmitting a specially crafted email message containing malicious SQL statements directly through an affected gateway.
Steeltoe, a popular framework for building cloud-native .NET applications, contains a critical data-exposure flaw in its HttpExchanges actuator endpoint before version 4.3.0. When explicitly configured to include query strings, the system records and stores sensitive values (such as OAuth tokens and credentials) in memory and application debug logs without sanitization, exposing them to unauthorized network actors.
A logic verification vulnerability in `@libp2p/peer-store` (part of the `js-libp2p` ecosystem) allows unauthenticated remote attackers to bypass identity verification and poison a victim node's peer store database with arbitrary network multiaddresses. This occurs because `consumePeerRecord()` fails to ensure that the signature's identity matches the inner record payload's identity.
Improper neutralization of input during web page generation in Grav CMS allows authenticated users with page modification privileges to execute stored Cross-Site Scripting (XSS) attacks. The flaw exists in AudioMediaTrait and VideoMediaTrait where media source URLs are concatenated directly into HTML templates without proper escaping.
A directory traversal vulnerability exists in the Junrar archive extraction library prior to version 7.6.1. When extracting crafted RAR archives, the library allows unauthorized directory creation outside the designated destination root due to improper path normalization during directory creation.