Summary
The fix for SSTI using |map
, |filter
and |reduce
twigs implemented in the commit 71bbed1 introduces bypass of the denylist due to incorrect return value from isDangerousFunction()
, which allows to execute the payload prepending double backslash (\\
)
Details
The isDangerousFunction()
check in version 1.7.42 and onwards retuns false
value instead of true
when the \
symbol is found in the $name
.
...
if (strpos($name, "\\") !== false) {
return false;
}
if (in_array($name, $commandExecutionFunctions)) {
return true;
}
...
Based on the code where the function is used, it is expected that any dangerous condition would return true
/**
* @param Environment $env
* @param array $array
* @param callable|string $arrow
* @return array|CallbackFilterIterator
* @throws RuntimeError
*/
function mapFunc(Environment $env, $array, $arrow)
{
if (!$arrow instanceof \Closure && !is_string($arrow) || Utils::isDangerousFunction($arrow)) {
throw new RuntimeError('Twig |map("' . $arrow . '") is not allowed.');
}
when |map('\system')
is used in the malicious payload, the single backslash is dropped prior to reaching strpos($name, '\\')
check, thus $name
variable already has no backslash, and the command is blacklisted because it reaches the if (in_array($name, $commandExecutionFunctions)) {
validation step.
However if |map('\\system')
is used (i.e. double backslash), then the strpos($name, "\\") !== false
takes effect, and isDangerousFunction()
returns false
, in which case the RuntimeError
is not generated, and blacklist is bypassed leading to code execution.
Exploit Conditions
This vulnerability can be exploited if the attacker has access to:
- an Administrator account, or
- a non-administrator, user account that has Admin panel access and Create/Update page permissions
Steps to reproduce
- Log in to Grav Admin using an administrator account.
- Navigate to
Accounts > Add
, and ensure that the following permissions are assigned when creating a new low-privileged user:- Login to Admin - Allowed
- Page Update - Allowed
- Log out of Grav Admin
- Login using the account created in step 2.
- Choose
Pages -> Home
- Click the
Advanced
tab and select the checkbox besideTwig
to ensure that Twig processing is enabled for the modified webpage. - Under the
Content
tab, insert the following payload within the editor:
{{ ['id'] | map('\\system') | join() }}
- Click the
Preview
button. Observe that the output of the id shell command is returned in the preview.
Mitigation
diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php
index 2f121bbe3..7b267cd0f 100644
--- a/system/src/Grav/Common/Utils.php
+++ b/system/src/Grav/Common/Utils.php
@@ -2069,7 +2069,7 @@ abstract class Utils
}
if (strpos($name, "\\") !== false) {
- return false;
+ return true;
}
if (in_array($name, $commandExecutionFunctions)) {
References:
- https://github.com/getgrav/grav/security/advisories/GHSA-9436-3gmp-4f53
- https://nvd.nist.gov/vuln/detail/CVE-2023-37897
- https://github.com/getgrav/grav/commit/71bbed12f950de8335006d7f91112263d8504f1b
- https://github.com/getgrav/grav/commit/b4c62101a43051fc7f5349c7d0a5b6085375c1d7
- https://github.com/advisories/GHSA-9436-3gmp-4f53