getgrav/grav: Page editor can still call arbitrary static methods through a Flex form override
What fixes it
- 2.2.13getgrav/grav, composerGitHub
composer require "getgrav/grav:^2.2.13"
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
Summary
The fix for GHSA-9rq7-4cch-f22j in Grav 2.2.2 was incomplete. A page editor can still get a data-*@ directive in a page-authored Flex form past Grav's allowed-callable check, either by renaming the field with unset-name@ instead of replace-name@, or by giving the field an integer key. The moved field is treated as if it came from the trusted blueprint file, so the directive can call any public static method, for example Folder::delete, Folder::move, Folder::copy or Utils::download.
Affected versions
Grav 2.0.15 through 2.2.12. 2.0.15 is when directives from file blueprints started skipping the allowed-callable check; 2.2.2 closed only the replace-name@ spelling. 1.7 is not affected.
Details
Since 2.0.15, directives from file blueprints are trusted and directives from page-authored forms are held to a Class::method allowlist. Blueprint::extend() decides which incoming fields are untrusted by reading the incoming form as written, then merges it into the trusted blueprint. Any directive the merge puts at a different path is never marked untrusted, so Blueprint::init() gives it the base blueprint's trust and skips the allowlist.
2.2.2 removed replace-name@ from untrusted input before the merge. The merge in rockettheme/toolbox (BlueprintForm::deepMerge()) handles unset and replace in the same branch, so unset-name@ (also written @unset-name@ or unset-name@2) moves the field, directive included, to a new top-level key in exactly the same way. A second route needs no merge action at all: the merge adds a key the base does not have with array_merge(), which renumbers integer keys, so an override field keyed 5 lands at 0 and loses its untrusted attribution.
Both Flex form builders (FlexForm, FlexDirectoryForm) extend a file-defined directory blueprint with a form taken from page frontmatter, which is the reachable path. The directive runs when the page's form is built, for example when an administrator opens the page on the site.
Impact
An account with page-edit permission (api.pages.write, or admin.pages in the classic admin) can call server-side methods its role does not grant, giving arbitrary directory deletion, file move or overwrite, and file read outside the page tree.
Patches
Fixed in Grav 2.2.13. unset-name@ is now removed from untrusted input along with replace-name@, and after every merge of untrusted input Grav compares the merged directives with the ones the blueprint had before. Any directive that is new or changed is treated as untrusted, wherever the merge put it.
Credits
Reported by @soquip.
Text from GitHub Advisory Database
Affected versions 1
- getgrav/grav, composer>= 2.0.15, < 2.2.13GitHub